@nodefony/documentation 10.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +168 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js +8 -0
  6. package/dist/index.js +75 -0
  7. package/dist/nodefony/config/config.js +71 -0
  8. package/dist/nodefony/config/defineModuleConfig.js +49 -0
  9. package/dist/nodefony/controller/DocumentationController.js +102 -0
  10. package/dist/nodefony/interfaces/IDocumentation.js +1 -0
  11. package/dist/nodefony/interfaces/index.js +1 -0
  12. package/dist/nodefony/service/DocumentationService.js +421 -0
  13. package/dist/nodefony/src/docScanner.js +58 -0
  14. package/dist/nodefony/src/errors/DocumentationError.js +45 -0
  15. package/dist/nodefony/src/frontmatter.js +63 -0
  16. package/dist/nodefony/src/linkResolver.js +68 -0
  17. package/dist/nodefony/src/search.js +120 -0
  18. package/dist/nodefony/src/slug.js +62 -0
  19. package/dist/types/index.d.ts +58 -0
  20. package/dist/types/nodefony/config/config.d.ts +37 -0
  21. package/dist/types/nodefony/config/defineModuleConfig.d.ts +17 -0
  22. package/dist/types/nodefony/controller/DocumentationController.d.ts +33 -0
  23. package/dist/types/nodefony/interfaces/IDocumentation.d.ts +144 -0
  24. package/dist/types/nodefony/interfaces/index.d.ts +5 -0
  25. package/dist/types/nodefony/service/DocumentationService.d.ts +78 -0
  26. package/dist/types/nodefony/src/docScanner.d.ts +47 -0
  27. package/dist/types/nodefony/src/errors/DocumentationError.d.ts +32 -0
  28. package/dist/types/nodefony/src/frontmatter.d.ts +37 -0
  29. package/dist/types/nodefony/src/linkResolver.d.ts +55 -0
  30. package/dist/types/nodefony/src/search.d.ts +64 -0
  31. package/dist/types/nodefony/src/slug.d.ts +48 -0
  32. package/docs/architecture.md +647 -0
  33. package/docs/index.md +369 -0
  34. package/package.json +81 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Parseur de frontmatter YAML minimal — **sans dépendance**.
3
+ *
4
+ * Un fichier de doc Nodefony commence (optionnellement) par un bloc encadré de
5
+ * `---` qui porte des métadonnées (`title`, `audience`, `section`, `version`,
6
+ * `status`, `updated`, `source`, `publish`). On n'embarque PAS `gray-matter` (~plusieurs
7
+ * deps transitives) pour ça : la doc n'utilise qu'un sous-ensemble plat de YAML
8
+ * (clé: valeur scalaire ou liste). Ce parseur couvre ce sous-ensemble et reste
9
+ * volontairement strict/petit.
10
+ *
11
+ * Supporté :
12
+ * - `key: value` → string (quotes simples/doubles retirées)
13
+ * - `key: [a, b, c]` → string[] (liste inline)
14
+ * - `key:` puis lignes ` - x` → string[] (liste en bloc)
15
+ * - lignes vides / commentaires `#` ignorées
16
+ *
17
+ * NON supporté (volontaire) : objets imbriqués, multi-lignes `|`/`>`, ancres.
18
+ * La doc n'en a pas besoin → on ne paie pas la complexité.
19
+ */
20
+ /** Métadonnées extraites du frontmatter — valeurs scalaires ou listes. */
21
+ export type Frontmatter = Record<string, string | string[]>;
22
+ /** Résultat du parse : `meta` (frontmatter) + `body` (markdown sans le bloc). */
23
+ export interface ParsedDoc {
24
+ meta: Frontmatter;
25
+ body: string;
26
+ }
27
+ /**
28
+ * Sépare le bloc frontmatter du corps markdown et parse les métadonnées.
29
+ *
30
+ * @param raw - contenu brut du fichier `.md`.
31
+ * @returns `{ meta, body }` — `meta` vide et `body = raw` s'il n'y a pas de bloc.
32
+ */
33
+ export declare function parseFrontmatter(raw: string): ParsedDoc;
34
+ /** Lit une clé de frontmatter en string (1er élément si liste), ou `undefined`. */
35
+ export declare function metaString(meta: Frontmatter, key: string): string | undefined;
36
+ /** Lit une clé de frontmatter en string[] (wrappe un scalaire), ou `[]`. */
37
+ export declare function metaList(meta: Frontmatter, key: string): string[];
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Réécriture des liens internes d'une page de doc en **slugs**.
3
+ *
4
+ * Une page vit sur le disque et lie ses voisines par chemin relatif — c'est ce
5
+ * qui la rend lisible sur GitHub et dans un éditeur : `[CORS](cors.md)`,
6
+ * `[Documentation](../../../../../docs/index.md)`. Mais le portail ne navigue
7
+ * pas par chemin : il navigue par **slug** (`mod~security~cors`), parce qu'un
8
+ * slug est une clé d'allowlist et jamais un chemin FS (anti-traversée, cf
9
+ * {@link ../src/slug}).
10
+ *
11
+ * Sans traduction, seuls les liens PLATS fonctionnaient dans Studio : toute
12
+ * remontée (`../index.md`) tombait en ancre HTML morte. La résolution appartient
13
+ * au serveur, seul à connaître la table chemin → slug ; le client n'a aucun
14
+ * moyen de deviner à quel fichier `../../..` correspond.
15
+ *
16
+ * Le lien conserve l'extension `.md` après réécriture (`mod~security~cors.md`) :
17
+ * le rendu markdown reconnaît un lien interne à cette extension, et un slug
18
+ * seul serait indistinguable d'une URL relative quelconque.
19
+ */
20
+ /** Options de {@link rewriteInternalLinks}. */
21
+ export interface RewriteLinksOptions {
22
+ /**
23
+ * Dossier de la page courante, relatif à la racine du projet (POSIX, sans
24
+ * fichier). Ex. `src/packages/@nodefony/security/docs`.
25
+ */
26
+ fromDir: string;
27
+ /**
28
+ * Traduit un chemin de fichier relatif à la racine du projet en slug.
29
+ * Retourne `undefined` si le fichier n'est pas dans l'index (lien laissé
30
+ * intact — on ne fabrique jamais un slug qui n'existe pas).
31
+ */
32
+ toSlug: (repoRelPath: string) => string | undefined;
33
+ /**
34
+ * Suffixe ajouté derrière la cible traduite. Vaut `".md"` par défaut : le
35
+ * portail reconnaît un lien interne à cette extension, et un slug nu serait
36
+ * indistinguable d'une URL relative quelconque.
37
+ *
38
+ * Un générateur de site statique publie en revanche des URL réelles
39
+ * (`/modules/security/cors/`) : il passe `""` et `toSlug` rend l'URL. Sans ce
40
+ * point d'extension, il faudrait recopier la résolution relative ailleurs —
41
+ * et deux implémentations d'une même règle divergent toujours.
42
+ */
43
+ suffix?: string;
44
+ }
45
+ /**
46
+ * Réécrit les liens markdown internes d'une page en slugs navigables.
47
+ *
48
+ * Un lien dont la cible n'est pas indexée est **laissé tel quel** : mieux vaut
49
+ * un lien inerte qu'un slug inventé qui produirait un 404 côté portail.
50
+ *
51
+ * @param markdown - corps de la page (frontmatter déjà retiré).
52
+ * @param options - dossier d'origine + résolution chemin → slug.
53
+ * @returns le markdown avec ses liens internes traduits.
54
+ */
55
+ export declare function rewriteInternalLinks(markdown: string, options: RewriteLinksOptions): string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Recherche plein texte du corpus de documentation — brique PURE.
3
+ *
4
+ * Elle ne lit ni disque ni réseau : on lui passe des pages déjà chargées, elle
5
+ * rend un classement. C'est ce qui lui permet d'avoir DEUX consommateurs très
6
+ * différents sans être écrite deux fois :
7
+ *
8
+ * - le service {@link DocumentationService.search}, côté serveur, qui lit les
9
+ * `.md` du disque à chaque requête ;
10
+ * - le générateur du site public, qui n'a pas de serveur du tout : il embarque
11
+ * un index dans la page et fait tourner CETTE fonction dans le navigateur du
12
+ * lecteur (`searchDocs.toString()` — voir `build-docs-site.mjs`).
13
+ *
14
+ * D'où la contrainte qui gouverne ce fichier : **`searchDocs` et ses aides sont
15
+ * auto-suffisantes**. Aucun import, aucune variable de portée supérieure, aucune
16
+ * syntaxe qui suppose un bundler. Le jour où l'une d'elles capture quelque chose
17
+ * de son module, le site continue de se construire et la recherche casse chez le
18
+ * lecteur — sans un mot. Le test de parité (`search-parity.test.ts`) est là pour
19
+ * ça : il exécute la fonction SÉRIALISÉE, pas la fonction importée.
20
+ */
21
+ import type { IDocSearchResult } from "../interfaces/IDocumentation.js";
22
+ /** Une page telle que la recherche a besoin de la voir. */
23
+ export interface SearchableDoc {
24
+ slug: string;
25
+ title: string;
26
+ navTitle: string;
27
+ /** Libellé de la section d'arbre qui porte la page (« Guides », « Cœur »…). */
28
+ sectionLabel: string;
29
+ /** Corps de la page, frontmatter retiré. */
30
+ body: string;
31
+ }
32
+ /**
33
+ * Réduit un texte à sa forme comparable : minuscules, accents retirés.
34
+ *
35
+ * « Sécurité » et « securite » doivent trouver la même chose — personne ne tape
36
+ * les accents dans un champ de recherche.
37
+ */
38
+ export declare function foldText(text: string): string;
39
+ /** Termes effectivement cherchés : pliés, dédoublonnés, un caractère écarté. */
40
+ export declare function splitSearchTerms(query: string): string[];
41
+ /**
42
+ * Prose INDEXABLE d'une page de documentation.
43
+ *
44
+ * Ce qui est retiré ne l'est pas par souci de taille mais de PERTINENCE : un
45
+ * bloc de code fait remonter une page sur `const`, un tableau de compatibilité
46
+ * sur n'importe quel nom de navigateur, et le fil d'Ariane sur le titre de
47
+ * toutes ses voisines. Une recherche qui rend tout ne rend rien.
48
+ *
49
+ * @param markdown - le corps de la page, frontmatter déjà retiré.
50
+ * @returns le texte à indexer, lignes conservées (les extraits s'y resituent).
51
+ */
52
+ export declare function extractSearchText(markdown: string): string;
53
+ /**
54
+ * Classe les pages qui portent TOUS les termes, la plus pertinente d'abord.
55
+ *
56
+ * ⚠️ Cette fonction est SÉRIALISÉE et exécutée dans un navigateur (voir l'en-tête
57
+ * du fichier) : elle ne doit rien référencer hors de son propre corps.
58
+ *
59
+ * @param docs - le corpus à balayer.
60
+ * @param query - ce que le lecteur a tapé.
61
+ * @param limit - nombre de résultats rendus (le total retenu est dit à part).
62
+ * @returns le classement, plus ce que la recherche a réellement balayé.
63
+ */
64
+ export declare function searchDocs(docs: SearchableDoc[], query: string, limit?: number): IDocSearchResult;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Sécurité et encodage des slugs de documentation.
3
+ *
4
+ * Le data plane LIT des fichiers `.md` sur disque → surface de **traversée de
5
+ * répertoire** (path traversal). Règle de sécurité (cf
6
+ * `feedback_security_rfc_rigor`) :
7
+ *
8
+ * 1. Le slug n'est JAMAIS concaténé brut dans un chemin FS. Le service garde,
9
+ * pour chaque fichier scanné, son chemin absolu RÉEL ; servir une page =
10
+ * retrouver l'entrée par **égalité de slug** dans la liste scannée, puis
11
+ * lire le chemin connu. Le slug est une CLÉ d'allowlist, pas un chemin.
12
+ * 2. {@link isSafeSlug} est une garde **défense-en-profondeur** : on rejette
13
+ * tout slug suspect (segment `..`, séparateur de chemin, octet nul,
14
+ * caractère de contrôle) AVANT même de chercher dans l'allowlist.
15
+ *
16
+ * Schéma de slug (URL-safe, un seul segment de route) :
17
+ * - doc racine `docs/guides/session-storage.md` → `root~guides~session-storage`
18
+ * - doc module `<module>/docs/index.md` → `mod~<module>~index`
19
+ *
20
+ * Le `/` du chemin devient `~` (le slug doit tenir dans UN segment de route
21
+ * `/api/page/{slug}`). On NE reconstruit jamais le chemin depuis le slug.
22
+ */
23
+ /**
24
+ * Valide qu'un slug est sûr à manipuler (avant toute recherche/lecture).
25
+ *
26
+ * Rejette : chaîne vide, trop longue, segment `..`, présence de `/` `\` `\0`,
27
+ * tout caractère hors charset autorisé.
28
+ *
29
+ * @param slug - slug brut reçu du client (déjà URL-décodé par le framework).
30
+ * @returns `true` si le slug est sûr, `false` sinon.
31
+ */
32
+ export declare function isSafeSlug(slug: string): boolean;
33
+ /** Source d'un fichier de doc : racine du projet ou un module. */
34
+ export type DocSource = {
35
+ kind: "root";
36
+ } | {
37
+ kind: "module";
38
+ module: string;
39
+ };
40
+ /**
41
+ * Construit le slug d'un fichier à partir de sa source et de son chemin
42
+ * relatif (POSIX, sans `.md`). Inverse jamais utilisé (slug = clé, pas chemin).
43
+ *
44
+ * @param source - racine ou module propriétaire.
45
+ * @param relPath - chemin relatif POSIX du `.md` (séparateur `/`).
46
+ * @returns slug URL-safe.
47
+ */
48
+ export declare function pathToSlug(source: DocSource, relPath: string): string;