@nodefony/mongoose 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 (51) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +97 -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/index.js +90 -0
  6. package/dist/nodefony/config/config.js +57 -0
  7. package/dist/nodefony/config/defineModuleConfig.js +60 -0
  8. package/dist/nodefony/entity/sessionEntity.js +63 -0
  9. package/dist/nodefony/entity/tokenEntity.js +184 -0
  10. package/dist/nodefony/entity/userEntity.js +106 -0
  11. package/dist/nodefony/entity/webAuthnCredentialEntity.js +97 -0
  12. package/dist/nodefony/entity/webhookEndpointEntity.js +109 -0
  13. package/dist/nodefony/interfaces/IMongooseConfig.js +1 -0
  14. package/dist/nodefony/interfaces/index.js +1 -0
  15. package/dist/nodefony/registerStores.js +89 -0
  16. package/dist/nodefony/service/MongooseService.js +95 -0
  17. package/dist/nodefony/src/MongooseTokenStore.js +237 -0
  18. package/dist/nodefony/src/MongooseUserRepository.js +205 -0
  19. package/dist/nodefony/src/MongooseWebAuthnCredentialStore.js +144 -0
  20. package/dist/nodefony/src/MongooseWebhookStore.js +181 -0
  21. package/dist/nodefony/src/SessionStorage.js +241 -0
  22. package/dist/nodefony/src/mongoOrder.js +49 -0
  23. package/dist/nodefony/src/orm-core/MongooseOrm.js +440 -0
  24. package/dist/nodefony/src/orm-core/MongooseRepository.js +300 -0
  25. package/dist/nodefony/src/orm-core/MongooseTransaction.js +53 -0
  26. package/dist/nodefony/src/orm-core/index.js +4 -0
  27. package/dist/types/index.d.ts +74 -0
  28. package/dist/types/nodefony/config/config.d.ts +21 -0
  29. package/dist/types/nodefony/config/defineModuleConfig.d.ts +26 -0
  30. package/dist/types/nodefony/entity/sessionEntity.d.ts +42 -0
  31. package/dist/types/nodefony/entity/tokenEntity.d.ts +59 -0
  32. package/dist/types/nodefony/entity/userEntity.d.ts +54 -0
  33. package/dist/types/nodefony/entity/webAuthnCredentialEntity.d.ts +61 -0
  34. package/dist/types/nodefony/entity/webhookEndpointEntity.d.ts +62 -0
  35. package/dist/types/nodefony/interfaces/IMongooseConfig.d.ts +17 -0
  36. package/dist/types/nodefony/interfaces/index.d.ts +1 -0
  37. package/dist/types/nodefony/registerStores.d.ts +37 -0
  38. package/dist/types/nodefony/service/MongooseService.d.ts +48 -0
  39. package/dist/types/nodefony/src/MongooseTokenStore.d.ts +126 -0
  40. package/dist/types/nodefony/src/MongooseUserRepository.d.ts +82 -0
  41. package/dist/types/nodefony/src/MongooseWebAuthnCredentialStore.d.ts +52 -0
  42. package/dist/types/nodefony/src/MongooseWebhookStore.d.ts +72 -0
  43. package/dist/types/nodefony/src/SessionStorage.d.ts +63 -0
  44. package/dist/types/nodefony/src/mongoOrder.d.ts +40 -0
  45. package/dist/types/nodefony/src/orm-core/MongooseOrm.d.ts +132 -0
  46. package/dist/types/nodefony/src/orm-core/MongooseRepository.d.ts +51 -0
  47. package/dist/types/nodefony/src/orm-core/MongooseTransaction.d.ts +37 -0
  48. package/dist/types/nodefony/src/orm-core/index.d.ts +9 -0
  49. package/docs/configuration.md +776 -0
  50. package/docs/index.md +881 -0
  51. package/package.json +97 -0
@@ -0,0 +1,776 @@
1
+ ---
2
+ title: "Configuration — brancher MongoDB"
3
+ lang: fr
4
+ module: "@nodefony/mongoose"
5
+ topic: mongoose
6
+ section: "Données"
7
+ audience: [developer]
8
+ tags:
9
+ [
10
+ configuration,
11
+ zod,
12
+ connecteurs,
13
+ uri,
14
+ environnement,
15
+ infra,
16
+ atlas,
17
+ replica-set,
18
+ secrets,
19
+ ]
20
+ version: "doc"
21
+ status: stable
22
+ updated: 2026-07-19
23
+ source: "src/packages/@nodefony/mongoose/docs/configuration.md"
24
+ coverageModule: mongoose
25
+ coverageFiles: config.ts,defineModuleConfig.ts,MongooseService.ts
26
+ ---
27
+
28
+ # Configuration — brancher MongoDB
29
+
30
+ > Trois clés, pas une de plus : **quelles bases** tu ouvres (`connectors`), **si tu traces** les
31
+ > requêtes (`debug`), et **si le module pose le schéma du framework** (`frameworkEntities`). Tout le
32
+ > reste — l'adresse réelle, le mot de passe, le pool — arrive par l'**environnement**, jamais par le
33
+ > dépôt. Cette page dit d'où vient chaque valeur, dans quel ordre, et ce que fait le serveur quand la
34
+ > base ne répond pas au démarrage.
35
+
36
+ 📍 [Documentation](../../../../../docs/index.md) › [MongoDB (Mongoose)](./index.md) › **Configuration**
37
+
38
+ ## 🧠 Le schéma général — d'où vient chaque valeur
39
+
40
+ Une valeur de configuration traverse **six étapes** avant d'ouvrir une connexion. Chaque étape peut
41
+ écraser la précédente ; la dernière gèle le résultat.
42
+
43
+ ```mermaid
44
+ flowchart TD
45
+ D["1 · Défauts d'usine<br/>schéma Zod du module"] --> U["2 · Ta config d'app<br/>use('@nodefony/mongoose', …)"]
46
+ U --> O["3 · Override d'un autre module<br/>clé module-mongoose"]
47
+ O --> E["4 · Env générique<br/>NF__MONGOOSE__…"]
48
+ E --> Z["5 · Validation Zod<br/>types, bornes, défauts manquants"]
49
+ Z --> V["6 · Env du driver<br/>MONGODB_URI · NF_DATABASE_URL · NF_MONGODB_DEBUG"]
50
+ V --> F["Object.freeze<br/>config immuable"]
51
+ F --> S["MongooseService<br/>1 connexion par connecteur"]
52
+ S --> DB[("MongoDB")]
53
+
54
+ Z -. config invalide .-> KO["Boot interrompu<br/>message de champ précis"]
55
+ ```
56
+
57
+ Deux choses se lisent sur ce schéma, et elles expliquent presque tous les comportements de la page :
58
+
59
+ - **La validation est au milieu, pas à la fin.** Ce que tu écris dans `use()` et ce que tu poses par
60
+ `NF__MONGOOSE__…` passent devant le contrôleur ; l'URI du driver, elle, est appliquée **après** —
61
+ elle n'est donc pas re-validée, mais elle gagne toujours.
62
+ - **La configuration est gelée** avant d'atteindre le service. Personne ne la modifie à chaud : ni un
63
+ module, ni un controller, ni Studio.
64
+
65
+ ## 📖 Lexique
66
+
67
+ | Terme | Sens |
68
+ | --------------------- | -------------------------------------------------------------------------------------------------------------------------- |
69
+ | Connecteur | Une connexion **nommée** déclarée en config. Sa clé devient le nom de l'ORM (`ormRegistry.get("nodefony")`). |
70
+ | URI de connexion | L'adresse complète d'une base Mongo : `mongodb://hôte:port/base` ou `mongodb+srv://…` (forme DNS des services managés). |
71
+ | `mongodb+srv` | Variante d'URI qui résout la liste des serveurs par un enregistrement DNS — la forme employée par MongoDB Atlas. |
72
+ | Replica set | Un groupe de serveurs Mongo répliqués. **Obligatoire** pour les transactions. |
73
+ | Schéma Zod | La description exécutable de la config : types, bornes, valeurs par défaut, texte d'aide. C'est **la** source de vérité. |
74
+ | JSON Schema | Le format standard dans lequel le schéma Zod est publié, pour qu'un outil (Studio) sache dessiner un formulaire d'édition. |
75
+ | `ConnectOptions` | Les options de connexion propres à Mongoose (pool, délais, TLS, identifiants) — validées par Mongoose, pas par Nodefony. |
76
+ | Infra déclarée | Le modèle « une URL par service » (`NF_DATABASE_URL`, `NF_REDIS_URL`) : tu décris ton infrastructure, pas chaque brique. |
77
+ | Store (brique) | L'implémentation d'une brique du framework — session, jetons, passkeys, webhooks — sur un backend donné. |
78
+ | Sentinelle `auto` | La valeur par défaut d'un champ `store` : « choisis pour moi, selon l'infra que j'ai déclarée ». |
79
+ | Fail-soft / fail-loud | Continuer en signalant (fail-soft) ou s'arrêter franchement (fail-loud). Nodefony choisit selon la nature de l'erreur. |
80
+
81
+ ## ❓ Qu'est-ce que c'est ?
82
+
83
+ Configurer un module, dans Nodefony, ce n'est pas remplir un fichier de réglages : c'est **écrire tes
84
+ écarts** par rapport à des défauts qui marchent déjà. Un module démarre sans que tu écrives quoi que
85
+ ce soit — `@nodefony/mongoose` ouvre alors `localhost:27017/nodefony`. Tu ne touches à la config que
86
+ là où ta réalité diffère.
87
+
88
+ Ces défauts ne sont pas cachés dans du code : ils vivent dans un **schéma** unique, où chaque champ
89
+ porte son type, ses bornes, sa valeur d'usine et sa raison d'être. Le schéma sert trois publics d'un
90
+ seul geste : il **valide** ta config au démarrage, il **documente** chaque clé, et il se publie en
91
+ JSON Schema pour qu'un formulaire puisse être dessiné sans qu'on redécrive quoi que ce soit.
92
+
93
+ Le mot d'ordre : **rien de secret dans le dépôt**. Ton `nodefony.config.ts` décrit la forme —
94
+ « il y a une base, elle s'appelle comme ça » — et l'environnement fournit l'adresse et les
95
+ identifiants réels. C'est ce qui permet au même code de tourner sur ta machine et dans un conteneur
96
+ sans qu'une seule ligne change.
97
+
98
+ ## 🧭 La vision Nodefony — deux fichiers, deux rôles
99
+
100
+ La convention du framework fige **exactement deux fichiers** de configuration par module, portant les
101
+ mêmes noms partout — pour qu'on ne se pose jamais la question de savoir où regarder.
102
+
103
+ | Fichier | Son rôle | Ce qu'on y trouve |
104
+ | --------------------------------------- | -------------- | ------------------------------------------------------------------------------ |
105
+ | `nodefony/config/config.ts` | **Le QUOI** | Le schéma Zod commenté, source unique des défauts, et le type TS qui en dérive |
106
+ | `nodefony/config/defineModuleConfig.ts` | **Le COMMENT** | Le builder : valide → applique l'environnement → gèle. Plus le JSON Schema. |
107
+
108
+ Le schéma (`mongooseConfigSchema` (`config.ts:83`)) porte les valeurs d'usine sous forme de
109
+ `.default(…)`, et chaque champ son `.describe(…)`. Les défauts effectifs du module ne sont pas
110
+ retapés à la main : ils sont **matérialisés depuis le schéma lui-même**
111
+ (`mongooseConfigSchema.parse({})` (`config.ts:122`)). Changer un défaut = changer le `.default()`,
112
+ et nulle part ailleurs. Le type TypeScript suit le même chemin : `MongooseConfig` (`config.ts:116`)
113
+ est **inféré** du schéma, jamais redéclaré.
114
+
115
+ Le builder (`defineMongooseConfig()` (`defineModuleConfig.ts:61`)) tient en trois gestes — valider,
116
+ surcharger par l'environnement, geler — et **ne réécrit jamais un défaut**. Il ne connaît pas les
117
+ valeurs : il ne connaît que le schéma.
118
+
119
+ > [!IMPORTANT]
120
+ > **Le schéma reste pur : aucune lecture de `process.env` dedans.** C'est ce qui le rend
121
+ > déterministe, testable sans serveur, et publiable en JSON Schema. Toute la lecture d'environnement
122
+ > est concentrée dans une seule fonction, en aval (`applyEnvOverrides()` (`defineModuleConfig.ts:22`)).
123
+ > Un schéma qui lirait l'environnement produirait une documentation différente selon la machine — et
124
+ > un formulaire Studio qui mentirait.
125
+
126
+ ### Où mongoose s'écarte de la référence
127
+
128
+ `@nodefony/drizzle` est l'adapter de référence pour cette convention. Sur la **structure**, mongoose
129
+ s'y conforme entièrement : deux fichiers aux noms canoniques, schéma pur, builder qui ne retape aucun
130
+ défaut, fonction préfixée par le module (`defineMongooseConfig`, jamais un `defineConfig` générique
131
+ qui collisionnerait à l'import). Trois écarts réels, tous **assumés et vérifiables** :
132
+
133
+ 1. **Le connecteur par défaut s'appelle `nodefony`, pas `default`.** Drizzle nomme le sien `default`.
134
+ Ce n'est pas de la fantaisie : les entités sont indexées par `(connecteur, nom)` dans un registre
135
+ **global au processus**, donc deux drivers avec le même nom de connecteur feraient collision sur
136
+ leurs entités `session` homonymes. Un nom distinct règle le problème par construction
137
+ (`FRAMEWORK_CONNECTOR` (`registerStores.ts:49`)).
138
+ 2. **Mongoose expose deux variables dédiées** (`MONGODB_URI`, `NF_MONGODB_DEBUG`) là où Drizzle ne lit
139
+ que l'infra déclarée. C'est un héritage de convention du driver Mongo, conservé parce qu'il est
140
+ universellement connu — mais l'infra déclarée reste le chemin recommandé.
141
+ 3. **`options` n'est pas re-modélisé** (`options` (`config.ts:71`)) : c'est un dictionnaire libre
142
+ passé tel quel à Mongoose. Re-décrire les dizaines de `ConnectOptions` en Zod produirait une
143
+ deuxième vérité, condamnée à diverger de la bibliothèque à chaque version. Le prix à payer est
144
+ assumé : une faute de frappe dans `options` n'est pas attrapée par Zod, elle l'est par Mongoose
145
+ au moment de la connexion.
146
+
147
+ ## 🚀 Démarrage rapide
148
+
149
+ Vu d'une application créée par `nodefony create app`. Trois configurations, de la plus simple à la
150
+ plus complète — chacune est un fichier entier, copiable tel quel.
151
+
152
+ ### 1. Ma base tourne sur ma machine
153
+
154
+ Le cas du développement : un `mongod` local, une base à moi.
155
+
156
+ ```typescript
157
+ // nodefony.config.ts — le manifeste des modules de l'application
158
+ export default defineConfig(() => ({
159
+ modules: [
160
+ // Le driver AVANT les modules qui consomment ses stores : les fabriques de
161
+ // store exigent un ORM DÉJÀ connecté au moment où elles se montent.
162
+ use("@nodefony/mongoose", {
163
+ connectors: {
164
+ // `nodefony` = le connecteur par défaut du module (≠ `default` de Drizzle).
165
+ nodefony: { host: "127.0.0.1", port: 27017, dbname: "blog" },
166
+ },
167
+ }),
168
+ "@nodefony/http",
169
+ "@nodefony/framework",
170
+ ],
171
+ }));
172
+ ```
173
+
174
+ Au démarrage, le journal confirme la cible — **sans jamais les identifiants** :
175
+
176
+ ```
177
+ INFO mongoose Mongoose ORM "nodefony" connected (127.0.0.1:27017/blog)
178
+ ```
179
+
180
+ ### 2. La même application, en production
181
+
182
+ Rien ne change dans le fichier : c'est l'environnement qui parle. Tu peux même **ne rien déclarer du
183
+ tout** — les défauts suffisent à décrire la forme, l'URI viendra du dehors.
184
+
185
+ ```typescript
186
+ // nodefony.config.ts — aucune adresse en dur, aucun secret dans le dépôt
187
+ export default defineConfig((ctx) => ({
188
+ modules: [
189
+ use("@nodefony/mongoose", {
190
+ // En développement seulement : trace chaque opération Mongoose.
191
+ // (`NF_MONGODB_DEBUG=1` fait la même chose sans toucher au fichier.)
192
+ debug: ctx.isDev,
193
+ }),
194
+ "@nodefony/http",
195
+ "@nodefony/framework",
196
+ ],
197
+ }));
198
+ ```
199
+
200
+ ```bash
201
+ # L'adresse ET le secret arrivent par l'environnement, jamais par le dépôt.
202
+ export NF_DATABASE_URL='mongodb+srv://app:********@cluster0.exemple.mongodb.net/prod'
203
+ node dist/index.js
204
+ ```
205
+
206
+ > [!TIP]
207
+ > Préfère `NF_DATABASE_URL` à `MONGODB_URI` : c'est **la** variable d'infrastructure du framework.
208
+ > Elle sert du même coup à résoudre les briques laissées en `auto` — sessions, jetons, passkeys se
209
+ > posent alors d'elles-mêmes sur Mongo. Une variable, deux effets cohérents.
210
+
211
+ ### 3. Brancher les briques du framework
212
+
213
+ Une fois le driver chargé, les stores Mongo sont **sélectionnables par leur nom**, sans aucun câblage :
214
+ le module les enregistre lui-même à son démarrage.
215
+
216
+ ```typescript
217
+ // nodefony.config.ts — sessions, jetons, passkeys et webhooks dans Mongo
218
+ export default defineConfig(() => ({
219
+ modules: [
220
+ use("@nodefony/mongoose", {
221
+ connectors: { nodefony: { uri: "mongodb://127.0.0.1:27017/app" } },
222
+ }),
223
+ use("@nodefony/http", { session: { store: "mongoose" } }),
224
+ use("@nodefony/security", {
225
+ tokenStore: { store: "mongoose" },
226
+ passkeys: { store: "mongoose" },
227
+ webhooks: { store: "mongoose" },
228
+ }),
229
+ "@nodefony/framework",
230
+ ],
231
+ }));
232
+ ```
233
+
234
+ Si `NF_DATABASE_URL` pointe déjà sur du `mongodb://`, tu peux laisser **tous** ces champs sur leur
235
+ défaut `auto` : la résolution suit l'infra déclarée (`resolveAutoStore()` (`infra.ts:241`)) et
236
+ journalise sa raison. Détail dans le hub, section
237
+ [Ce qui se passe quand tu ne choisis rien](./index.md#ce-qui-se-passe-quand-tu-ne-choisis-rien).
238
+
239
+ ## ⚙️ Toutes les clés
240
+
241
+ Trois clés à la racine. Les défauts ci-dessous sont ceux du schéma, relus au code — pas une copie de
242
+ mémoire.
243
+
244
+ | Clé | Type | Défaut | Effet |
245
+ | ------------------- | ----------------------------- | --------------------------------------- | ------------------------------------------------------------------------- |
246
+ | `connectors` | dictionnaire nom → connecteur | `nodefony` → `localhost:27017/nodefony` | Une connexion ouverte au boot par entrée ; la clé devient le nom de l'ORM |
247
+ | `debug` | `boolean` | `false` | Trace **toutes** les opérations Mongoose du processus |
248
+ | `frameworkEntities` | `boolean` | `true` | Déclare le schéma du framework et rend ses stores sélectionnables |
249
+
250
+ Et six champs par connecteur :
251
+
252
+ | Champ | Type | Défaut | Effet |
253
+ | ----------- | --------------------- | ------------- | ------------------------------------------------------------------------ |
254
+ | `uri` | `string` non vide | _aucun_ | Adresse complète. **Prime** sur `host`/`port`/`dbname`, qui sont ignorés |
255
+ | `host` | `string` non vide | `"localhost"` | Hôte du serveur. Ignoré si `uri` est fourni |
256
+ | `port` | entier, **1 à 65535** | `27017` | Port TCP. Ignoré si `uri` est fourni |
257
+ | `dbname` | `string` non vide | `"nodefony"` | Nom de la base. Ignoré si `uri` est fourni |
258
+ | `autoIndex` | `boolean` | _aucun_ | Construire les index au démarrage. Voir « Les index » ci-dessous |
259
+ | `options` | dictionnaire libre | _aucun_ | `ConnectOptions` Mongoose : pool, délais, TLS, identifiants |
260
+
261
+ ### `connectors` — une ou plusieurs bases
262
+
263
+ Chaque entrée de `connectors` (`config.ts:93`) devient une **connexion isolée** ouverte au démarrage,
264
+ puis un ORM inscrit sous ce nom. Deux entrées = deux connexions, deux ORM, deux jeux d'entités : c'est
265
+ ainsi qu'une application lit une base métier et écrit dans une base d'archives sans les mélanger.
266
+
267
+ L'adresse se donne de deux façons, jamais les deux à la fois utilement :
268
+
269
+ - **En pièces détachées** — `host`, `port`, `dbname` : lisible, adapté au développement. Le service
270
+ les assemble en `mongodb://hôte:port/base` (`MongooseService.buildUri()` (`MongooseService.ts:76`)).
271
+ - **En une URI** — `uri` (`config.ts:41`) : la seule forme capable d'exprimer un replica set, un
272
+ `mongodb+srv`, des options de requête. **Dès qu'`uri` est présent, les trois autres champs ne sont
273
+ plus lus du tout.**
274
+
275
+ ```typescript
276
+ use("@nodefony/mongoose", {
277
+ connectors: {
278
+ nodefony: { uri: "mongodb://127.0.0.1:27017/app" }, // base principale
279
+ archives: { host: "10.0.0.7", dbname: "archives" }, // seconde connexion
280
+ },
281
+ });
282
+ ```
283
+
284
+ Les entités choisissent leur connecteur par son nom (`@entities([…], { connector: "archives" })`), et
285
+ le repository se demande au registre sous ce même nom. Le nom de connecteur est donc une **clé
286
+ publique** de ton application : le changer déplace des entités.
287
+
288
+ > [!WARNING]
289
+ > Le connecteur nommé `nodefony` a un statut particulier : c'est **lui** qui porte le schéma du
290
+ > framework (`FRAMEWORK_CONNECTOR` (`registerStores.ts:49`)) et le stockage de session
291
+ > (`SESSION_CONNECTOR` (`sessionEntity.ts:5`)). Le renommer ou le supprimer sans le remplacer casse
292
+ > les stores framework — ils cherchent un ORM `nodefony` connecté et échouent franchement s'il manque
293
+ > (`resolveConnectedOrm()` (`registerStores.ts:64`)).
294
+
295
+ ### `options` — ce que Mongoose sait faire et Nodefony ne re-décrit pas
296
+
297
+ `options` (`config.ts:71`) est transmis tel quel au driver
298
+ (`MongooseService.#connectOne()` (`MongooseService.ts:87`)). On y met tout ce qui touche au
299
+ **transport** plutôt qu'à l'adresse : taille du pool, délais, TLS, identifiants séparés.
300
+
301
+ ```typescript
302
+ use("@nodefony/mongoose", {
303
+ connectors: {
304
+ nodefony: {
305
+ uri: process.env.NF_DATABASE_URL,
306
+ options: {
307
+ maxPoolSize: 50, // connexions simultanées ouvertes vers le serveur
308
+ serverSelectionTimeoutMS: 5_000, // délai avant de déclarer la base injoignable
309
+ socketTimeoutMS: 45_000, // délai d'une opération individuelle
310
+ },
311
+ },
312
+ },
313
+ });
314
+ ```
315
+
316
+ Le contenu d'`options` n'est **pas** validé par Zod : c'est Mongoose qui l'accepte ou le rejette, à la
317
+ connexion. Une clé mal orthographiée est donc silencieuse jusqu'au boot, où elle se manifeste soit par
318
+ une erreur du driver, soit par une option sans effet. Contrepartie de ne pas entretenir une deuxième
319
+ description des options du driver.
320
+
321
+ ### `debug` — voir passer les requêtes
322
+
323
+ `debug` (`config.ts:85`) active la trace intégrée de Mongoose (`connectAll()` (`MongooseService.ts:63`)).
324
+ Chaque opération part sur la sortie standard, avec sa collection, son filtre et ses champs.
325
+
326
+ C'est un **réglage de processus, pas de connecteur** : il vaut pour toutes les connexions à la fois.
327
+ En production, laisse-le sur `false` — la trace est verbeuse, et un filtre peut contenir des données
328
+ personnelles. Pour observer une application en production, l'outil approprié est la sonde de flux ORM,
329
+ détaillée dans la section [Observabilité](#-observabilité--studio).
330
+
331
+ ### `frameworkEntities` — le module pose-t-il le schéma du framework ?
332
+
333
+ C'est le champ le plus discret et le plus structurant (`frameworkEntities` (`config.ts:102`)). Sur son
334
+ défaut `true`, le module fait deux choses de plus qu'ouvrir des connexions, dès son enregistrement et
335
+ **avant** que la connexion ne s'ouvre (`Mongoose.onKernelRegister()` (`mongoose/index.ts:65`)) :
336
+
337
+ 1. il **déclare les entités du framework** sur le connecteur `nodefony` — jetons, passkeys, webhooks —
338
+ pour que leurs modèles soient compilés au moment de la connexion ;
339
+ 2. il **enregistre les fabriques** correspondantes dans les registres de `@nodefony/security`, ce qui
340
+ rend le nom `"mongoose"` sélectionnable dans `tokenStore`, `passkeys`, `webhooks`
341
+ (`registerMongooseFrameworkStores()` (`registerStores.ts:94`)).
342
+
343
+ Le passer à `false` transforme le module en **pur driver de données** : tes entités à toi, rien
344
+ d'autre. Les stores framework Mongo deviennent alors introuvables — les sélectionner échoue
345
+ franchement plutôt que de retomber en mémoire sans le dire.
346
+
347
+ | Ta situation | Ce que tu mets |
348
+ | -------------------------------------------------------------------------- | ---------------------- |
349
+ | Application classique — sessions, comptes, jetons dans Mongo | rien (défaut `true`) |
350
+ | Mongo ne sert qu'à tes données ; sécurité et sessions vivent ailleurs | `false` |
351
+ | Tu déclares toi-même une entité framework, à ta façon (nom, index, champs) | rien — voir ci-dessous |
352
+
353
+ Le troisième cas n'a pas besoin de `false` : l'auto-enregistrement **respecte l'application**. Si une
354
+ entité du même nom est déjà déclarée quand le module s'enregistre, il ne l'écrase pas — il la
355
+ signale dans son bilan de démarrage, en journal `DEBUG`. Tu peux donc redéfinir une seule entité sans
356
+ renoncer aux autres.
357
+
358
+ > [!CAUTION]
359
+ > **`frameworkEntities: false` ne désactive pas le stockage de session.** L'entité `session` et son
360
+ > store ne passent pas par ce commutateur : ils s'enregistrent à l'**import** du module, par
361
+ > décorateur (`sessionEntity.ts:41`) et par appel direct au registre de `@nodefony/http`
362
+ > (`SessionsService.registerStorage("mongoose", …)` (`mongoose/nodefony/src/SessionStorage.ts:353`)). Charger le module
363
+ > rend donc toujours `session: { store: "mongoose" }` disponible, quelle que soit la valeur du champ.
364
+ > C'est cohérent avec le texte du schéma, qui n'énumère que jetons, WebAuthn et webhooks — mais
365
+ > contre-intuitif si l'on lit « entités du framework » au sens large.
366
+
367
+ ## ⚙️ Les variables d'environnement
368
+
369
+ Deux familles de variables agissent sur ce module, et elles n'ont **pas la même portée**.
370
+
371
+ ### Celles que le driver lit lui-même
372
+
373
+ Appliquées après la validation, dans une seule fonction
374
+ (`applyEnvOverrides()` (`defineModuleConfig.ts:22`)). Elles gagnent donc sur tout le reste.
375
+
376
+ | Variable | Effet |
377
+ | ------------------ | ------------------------------------------------------------------------------------------------------- |
378
+ | `MONGODB_URI` | Remplace l'`uri` du connecteur primaire. **La place du secret de connexion.** |
379
+ | `NF_DATABASE_URL` | L'infra déclarée du framework. Même effet, **si et seulement si** le schéma est `mongodb`/`mongodb+srv` |
380
+ | `DATABASE_URL` | Alias de la précédente, pour les plateformes qui l'imposent (`resolveInfra()` (`infra.ts:134`)) |
381
+ | `NF_MONGODB_DEBUG` | `1` ou `true` → `debug = true`. Toute autre valeur est sans effet |
382
+
383
+ Trois précisions qui évitent les mauvaises surprises :
384
+
385
+ - **`MONGODB_URI` passe devant l'infra.** Le driver lit d'abord sa variable dédiée et ne consulte
386
+ l'infra qu'à défaut — pratique pour épingler une base Mongo particulière dans un environnement qui
387
+ déclare déjà une autre base.
388
+ - **Une URL SQL est ignorée, pas refusée.** Si `NF_DATABASE_URL` vaut `postgres://…`, ce module la
389
+ laisse passer sans rien faire : elle appartient à `@nodefony/drizzle`. En revanche un schéma
390
+ **inconnu** (ni SQL ni Mongo) fait échouer le démarrage franchement, pour qu'aucune base ne soit
391
+ choisie par hasard (`parseDatabaseUrl()` (`infra.ts:96`)).
392
+ - **Le connecteur visé est le primaire** : `nodefony` s'il existe, sinon la première entrée déclarée.
393
+ Une variable d'environnement ne peut donc pas viser un connecteur secondaire.
394
+
395
+ ### L'override générique du framework
396
+
397
+ Toute clé de config d'un module se surcharge par `NF__<MODULE>__<CHEMIN>`, le double tiret bas
398
+ séparant les niveaux (`parseNfEnvOverrides()` (`envOverride.ts:80`)). Le segment de module est le nom
399
+ court : `MONGOOSE`.
400
+
401
+ ```bash
402
+ NF__MONGOOSE__DEBUG=true # racine
403
+ NF__MONGOOSE__FRAMEWORKENTITIES=false # casse indifférente
404
+ NF__MONGOOSE__CONNECTORS__NODEFONY__DBNAME=recette # champ imbriqué
405
+ NF__MONGOOSE__CONNECTORS__NODEFONY__PORT=27018 # coercé en nombre
406
+ ```
407
+
408
+ Ces overrides sont posés **avant** la validation Zod (`Kernel.applyEnvConfigOverrides()` (`Kernel.ts:1600`)) :
409
+ une valeur aberrante est donc rejetée comme si tu l'avais écrite dans ton fichier. C'est voulu — un
410
+ réglage d'environnement invalide doit casser aussi fort qu'un réglage de code.
411
+
412
+ > [!WARNING]
413
+ > **Un override ne peut viser qu'un champ qui existe déjà.** Le mécanisme refuse de créer une clé
414
+ > absente, pour ne pas fabriquer une clé fantôme à la mauvaise casse que le schéma ignorerait ensuite
415
+ > en silence (`applyResolvedPath()` (`envOverride.ts:300`)). Conséquence concrète :
416
+ > `NF__MONGOOSE__CONNECTORS__NODEFONY__URI` **ne fait rien** si ton `use()` ne déclare pas déjà un
417
+ > `uri` — `uri` n'a pas de valeur par défaut, donc le chemin n'existe pas. Le cas n'est pas silencieux
418
+ > pour autant : le démarrage émet un `WARNING` nommant le segment fautif et listant les clés
419
+ > disponibles. **Pour poser une URI par l'environnement, utilise `MONGODB_URI` ou `NF_DATABASE_URL`.**
420
+
421
+ ### Ce qui n'est pas une variable de ce module
422
+
423
+ | Variable | Qui la lit | Effet |
424
+ | ------------------- | --------------------------------- | --------------------------------------------------------------------------- |
425
+ | `NF_STORE` | Le cœur, pour toute brique `auto` | Force un backend partout — sert surtout à mesurer sans le goulot d'une base |
426
+ | `NF_ORM_FLOW` | La sonde de flux ORM | `1`/`true` l'allume, `0`/`false` l'éteint ; sinon : allumée hors production |
427
+ | `NF_MONGO_TEST_URI` | La suite de tests du module | Pointe un serveur Mongo existant au lieu d'en démarrer un jetable |
428
+
429
+ Le dépôt d'utilisateurs, lui, ne se choisit pas par une clé de ce module : il se pose dans le
430
+ `provisionUsers` de ton application. Voir
431
+ [Brancher l'annuaire utilisateurs](./index.md#brancher-lannuaire-utilisateurs).
432
+
433
+ ### L'ordre complet, une bonne fois
434
+
435
+ De la plus faible à la plus forte priorité :
436
+
437
+ 1. **Les défauts du schéma** — `localhost:27017/nodefony`, `debug: false`, `frameworkEntities: true`.
438
+ 2. **Ta config d'app** — `use("@nodefony/mongoose", { … })`, fusionnée en profondeur sous les défauts
439
+ (`Kernel.loadModulesFromManifest()` (`Kernel.ts:1150`)).
440
+ 3. **Un override venu d'un autre module** — la clé `module-mongoose` dans la config d'un module tiers.
441
+ 4. **`NF__MONGOOSE__…`** — l'override générique d'environnement.
442
+ 5. **La validation Zod** — types, bornes, défauts des champs restés absents.
443
+ 6. **`MONGODB_URI` / infra / `NF_MONGODB_DEBUG`** — la couche du driver, appliquée après validation.
444
+ 7. **Le gel** — la configuration devient immuable pour la durée de vie du processus.
445
+
446
+ > [!NOTE]
447
+ > Les étapes 4 et 6 sont toutes deux « l'environnement », et pourtant **6 gagne sur 4**. Ce n'est pas
448
+ > une incohérence : 4 sert à ajuster n'importe quel champ de n'importe quel module, 6 sert à porter
449
+ > l'**adresse de la base**, qui est la donnée la plus dépendante du déploiement. En cas de doute,
450
+ > `MONGODB_URI` est toujours le dernier mot.
451
+
452
+ ## Mises en situation
453
+
454
+ Quatre déploiements, quatre configurations. Le fichier d'application change à peine ; c'est
455
+ l'environnement qui porte la différence.
456
+
457
+ ### Sur ma machine — je veux juste que ça tourne
458
+
459
+ Un `mongod` local, aucune authentification, une base par projet.
460
+
461
+ ```typescript
462
+ use("@nodefony/mongoose", {
463
+ connectors: { nodefony: { dbname: "blog" } }, // host et port restent aux défauts
464
+ });
465
+ ```
466
+
467
+ Ce qu'on observe : `Mongoose ORM "nodefony" connected (localhost:27017/blog)`. Ce qui **ne marchera
468
+ pas** : les transactions. Un `mongod` isolé ne les supporte pas — il faut un replica set, même à un
469
+ seul nœud.
470
+
471
+ ### En développement, mais avec les transactions
472
+
473
+ Un replica set à un nœud donne les transactions sans monter une grappe. L'URI doit nommer le jeu de
474
+ réplication, ce qui impose la forme `uri`.
475
+
476
+ ```typescript
477
+ use("@nodefony/mongoose", {
478
+ connectors: {
479
+ nodefony: {
480
+ uri: "mongodb://127.0.0.1:27017/blog?replicaSet=rs0&directConnection=true",
481
+ },
482
+ },
483
+ });
484
+ ```
485
+
486
+ Le paramètre `directConnection=true` évite la découverte de topologie quand le jeu n'a qu'un membre —
487
+ sans lui, le driver peut attendre longuement un serveur primaire qu'il ne trouvera pas.
488
+
489
+ ### Une base managée — Atlas ou équivalent
490
+
491
+ L'adresse est un `mongodb+srv`, le secret vit dans l'environnement, le fichier d'application ne
492
+ contient **rien** de spécifique.
493
+
494
+ ```bash
495
+ export NF_DATABASE_URL='mongodb+srv://app:********@cluster0.exemple.mongodb.net/prod?retryWrites=true&w=majority'
496
+ ```
497
+
498
+ ```typescript
499
+ // Aucun connecteur déclaré : le défaut suffit, l'environnement fournit l'adresse.
500
+ use("@nodefony/mongoose", {});
501
+ ```
502
+
503
+ Trois points d'attention avec un service managé :
504
+
505
+ - **Le TLS est implicite** en `mongodb+srv` — inutile de l'activer dans `options`.
506
+ - **Le replica set vient d'office**, donc les transactions fonctionnent sans rien faire.
507
+ - **Dimensionne `maxPoolSize` par processus**, pas pour l'application entière : dix pods à 50
508
+ connexions font 500 connexions vers ton cluster.
509
+
510
+ ### En conteneur / dans un orchestrateur
511
+
512
+ L'image ne contient aucune adresse : elle prend ce que l'orchestrateur lui donne.
513
+
514
+ ```yaml
515
+ # Extrait d'un déploiement — l'URI vient d'un secret, jamais de l'image
516
+ env:
517
+ - name: NF_DATABASE_URL
518
+ valueFrom:
519
+ secretKeyRef: { name: mongo-credentials, key: uri }
520
+ - name: NF__MONGOOSE__CONNECTORS__NODEFONY__OPTIONS__MAXPOOLSIZE
521
+ value: "20"
522
+ ```
523
+
524
+ Le second override suppose que `options` est **déjà déclaré** dans ton `use()` (règle du chemin
525
+ existant, plus haut). Si tu veux régler le pool par l'environnement, déclare-le explicitement avec une
526
+ valeur de départ :
527
+
528
+ ```typescript
529
+ use("@nodefony/mongoose", {
530
+ connectors: { nodefony: { options: { maxPoolSize: 10 } } },
531
+ });
532
+ ```
533
+
534
+ ### Deux bases dans la même application
535
+
536
+ Une base métier et une base de lecture séparée. Chaque entité déclare son connecteur ; les stores du
537
+ framework restent sur `nodefony`.
538
+
539
+ ```typescript
540
+ use("@nodefony/mongoose", {
541
+ connectors: {
542
+ nodefony: { uri: "mongodb://primaire:27017/app" }, // métier + framework
543
+ reporting: {
544
+ uri: "mongodb://replica:27017/app",
545
+ options: { maxPoolSize: 5 },
546
+ },
547
+ },
548
+ });
549
+ ```
550
+
551
+ Les deux connexions s'ouvrent en série au démarrage, dans l'ordre de déclaration
552
+ (`connectAll()` (`MongooseService.ts:63`)), et se ferment toutes à l'arrêt
553
+ (`disconnectAll()` (`MongooseService.ts:134`)). Un service peut demander l'une ou l'autre par son nom
554
+ (`getOrm()` (`MongooseService.ts:142`)), mais l'usage courant reste le registre d'ORM.
555
+
556
+ ## 🔐 Le secret de connexion
557
+
558
+ Un identifiant de base de données est le secret le plus rentable à voler : il ouvre **toutes** les
559
+ données d'un coup. La règle est donc sans nuance.
560
+
561
+ - **Jamais dans le dépôt.** Ni dans `nodefony.config.ts`, ni dans un fichier d'exemple, ni « juste
562
+ pour le développement ». Le secret arrive par `MONGODB_URI` ou `NF_DATABASE_URL`.
563
+ - **Ni dans les journaux, ni dans Studio.** L'URI est systématiquement nettoyée de tout
564
+ `utilisateur:motdepasse@` avant d'être affichée (`MongooseOrm.safeTarget()` (`MongooseOrm.ts:595`)),
565
+ y compris pour les URI multi-hôtes que l'analyseur d'URL standard ne sait pas découper. C'est cette
566
+ cible nettoyée que voit le plan d'administration
567
+ (`MongooseOrm.describeConnection()` (`MongooseOrm.ts:583`)) et le message de connexion au démarrage.
568
+ - **Les identifiants passés par `options`** (`user`, `pass`) suivent la même règle : ils viennent de
569
+ l'environnement, pas du fichier. Le framework rédige d'ailleurs la valeur de tout override
570
+ d'environnement dont le chemin ressemble à un secret, avant de le journaliser
571
+ (`pathLooksSecret()` (`envOverride.ts:375`)).
572
+
573
+ > [!TIP]
574
+ > Vérifie ta redaction en une commande : démarre l'application et lis la ligne de connexion. Elle doit
575
+ > montrer `hôte/base` et **rien d'autre**. Si un `user:pass@` y apparaît, c'est un incident — le
576
+ > secret est probablement écrit ailleurs qu'à l'endroit prévu.
577
+
578
+ ## 🔎 Les index — ce qui est construit, ce qui est seulement CONSTATÉ
579
+
580
+ MongoDB n'a pas de schéma à déclarer, mais il a des **index**, et certains portent des contraintes
581
+ d'unicité dont l'application dépend : l'identifiant d'un utilisateur, le condensat d'un jeton,
582
+ l'identifiant d'une session. Un index unique absent n'est pas une lenteur — c'est une contrainte qui
583
+ n'existe pas.
584
+
585
+ Mongoose les construit au démarrage, en tâche de fond. Cette construction peut **échouer** : une
586
+ collection qui contient déjà des doublons au moment d'une montée de version, un index existant de
587
+ même nom mais de définition différente. Nodefony **constate** donc l'écart après chaque connexion et
588
+ journalise tout index déclaré mais absent en **`CRITIC`**, en nommant la collection et l'index :
589
+
590
+ ```
591
+ index DÉCLARÉS mais ABSENTS de la collection "users" (entité "User") : identifier_1
592
+ — toute contrainte d'unicité qu'ils portent n'est PAS appliquée
593
+ ```
594
+
595
+ Ce constat ne fait **jamais** de réparation. L'outil de mongoose qui répare, `syncIndexes()`,
596
+ **supprime** au passage tout index non déclaré au schéma : sur une base qu'un exploitant a indexée à
597
+ la main, la réparation automatique serait pire que le mal. Réparer reste un geste explicite.
598
+
599
+ ### `autoIndex` — construire, ou seulement constater
600
+
601
+ Par défaut Mongoose construit. Sur une grosse collection, la construction bloque les opérations : la
602
+ documentation de Mongoose recommande de la couper en production, une fois les index posés une bonne
603
+ fois par un déploiement maîtrisé.
604
+
605
+ ```ts
606
+ use("@nodefony/mongoose", {
607
+ connectors: {
608
+ nodefony: { autoIndex: false }, // ne construit plus ; constate et alerte toujours
609
+ },
610
+ });
611
+ ```
612
+
613
+ À `false`, un index manquant **n'est pas créé** — il est seulement constaté, et le `CRITIC` reste
614
+ émis. C'est précisément l'intérêt : le pod ne bloque pas, et l'exploitant sait ce qu'il lui reste à
615
+ poser.
616
+
617
+ > `autoIndex` peut aussi être écrit dans le fourre-tout `options`. Quand les deux sont donnés, le
618
+ > **champ déclaré gagne** (`MongooseService.buildConnectOptions()`).
619
+
620
+ ### Quand un index manque en production
621
+
622
+ 1. **Lire le `CRITIC`** : il nomme la collection et l'index (`identifier_1` = champ `identifier`,
623
+ ordre croissant), donc la contrainte qui n'est pas tenue.
624
+ 2. **Chercher la cause dans la donnée** avant l'index. Un index unique refusé signifie presque
625
+ toujours des **doublons déjà présents** :
626
+ ```js
627
+ db.users.aggregate([
628
+ { $group: { _id: "$identifier", n: { $sum: 1 } } },
629
+ { $match: { n: { $gt: 1 } } },
630
+ ]);
631
+ ```
632
+ 3. **Résoudre les doublons** — c'est une décision métier (fusionner, renommer, supprimer), jamais
633
+ un geste automatique.
634
+ 4. **Poser l'index**, en arrière-plan pour ne pas bloquer la collection :
635
+ ```js
636
+ db.users.createIndex(
637
+ { identifier: 1 },
638
+ { unique: true, name: "identifier_1" },
639
+ );
640
+ ```
641
+ 5. **Redémarrer un pod** et vérifier que le `CRITIC` a disparu.
642
+
643
+ Tant que l'index manque, considérer la contrainte comme absente : le code qui compte sur elle
644
+ (inscription, rotation de jeton) peut créer des doublons sans erreur.
645
+
646
+ ## Quand la configuration ne passe pas
647
+
648
+ Deux échecs très différents, deux comportements assumés.
649
+
650
+ ### La config est invalide
651
+
652
+ Un port hors bornes, un type erroné, un champ vide : le démarrage s'arrête avec un message qui **nomme
653
+ le champ**, pas une pile d'appels.
654
+
655
+ ```
656
+ [@nodefony/mongoose] Invalid config: connectors.x.port: Too small: expected number to be >=1
657
+ ```
658
+
659
+ Le message est assemblé à partir des remontées de validation, chemin de champ compris
660
+ (`Mongoose.onKernelRegister()` (`mongoose/index.ts:65`)). C'est un arrêt **volontairement franc** :
661
+ une configuration fausse ne se répare pas en continuant, et un serveur qui démarre à moitié est plus
662
+ coûteux à diagnostiquer qu'un serveur qui refuse de démarrer.
663
+
664
+ ### La base ne répond pas
665
+
666
+ Le cas courant : la config est parfaite, mais Mongo n'est pas joignable — conteneur pas encore prêt,
667
+ réseau coupé, identifiants périmés. Le comportement **dépend de l'environnement**, arbitré par la
668
+ politique de boot du cœur (`Kernel.isBootErrorFatal()` (`Kernel.ts:2665`)) :
669
+
670
+ | Environnement | Ce qui se passe |
671
+ | ------------------- | -------------------------------------------------------------------------------------- |
672
+ | Développement, test | `WARNING`, l'échec est agrégé au bilan de démarrage, **le serveur démarre quand même** |
673
+ | Production | L'échec **interrompt le démarrage** : le processus sort en erreur |
674
+
675
+ Ce n'est pas une inconséquence : en développement, tu veux ton serveur debout pour travailler sur le
676
+ reste ; en production, un pod qui répond sans sa base est un piège — l'orchestrateur doit le voir
677
+ tomber pour le relancer, et c'est le modèle cloud-native que le framework applique partout.
678
+
679
+ > [!IMPORTANT]
680
+ > **Le module est déclaré non critique** (`Mongoose.critical` (`mongoose/index.ts:48`)), et cette
681
+ > déclaration protège bien ses **hooks de module** — mais l'ouverture de la connexion, elle, est faite
682
+ > par un écouteur `onBoot` posé par le service, qui ne porte pas cette étiquette
683
+ > (`MongooseService.ts:41`). En production, une base injoignable interrompt donc
684
+ > le démarrage. Si tu attends l'inverse — un serveur qui démarre sans sa base et se rattrape plus
685
+ > tard — ne compte pas dessus : prévois une sonde de disponibilité côté orchestrateur.
686
+
687
+ ### Pendant l'arrêt du serveur
688
+
689
+ À l'arrêt, les connexions se ferment alors que des requêtes peuvent encore être en vol. Le stockage de
690
+ session **dégrade gracieusement** plutôt que de lever une exception
691
+ (`SessionStorage.#repo()` (`SessionStorage.ts:45`)) : une session non persistée le temps de l'arrêt
692
+ vaut mieux qu'une erreur 500 et un rejet non capturé. À l'inverse, une entité absente sur un ORM
693
+ **connecté** est une vraie erreur de configuration : celle-là est levée sans ménagement.
694
+
695
+ ## 📡 Observabilité — Studio
696
+
697
+ La configuration ne se contente pas d'être validée : elle se **montre**.
698
+
699
+ - Le module publie son schéma en JSON Schema (`Mongoose.configSchema()` (`mongoose/index.ts:55`) →
700
+ `mongooseConfigJsonSchema()` (`defineModuleConfig.ts:72`)). C'est ce qui permet à Studio d'afficher
701
+ chaque clé avec son type, son défaut et son texte d'aide — sans qu'une seule ligne de description
702
+ soit recopiée quelque part.
703
+ - L'écran `/nodefony/config` montre la configuration **effective** après toutes les couches, et la
704
+ **provenance** de chaque champ : valeur d'usine, écrite par l'application, ou venue de
705
+ l'environnement. C'est l'outil qui répond à « pourquoi cette valeur ? » sans relire six fichiers.
706
+ - L'écran `/nodefony/databases` montre les connexions et leur santé ; `/nodefony/stores` montre quelle
707
+ brique s'est posée sur quel backend **et pourquoi**.
708
+
709
+ La cible affichée est toujours la version nettoyée de l'URI — aucun identifiant ne franchit la
710
+ frontière du plan d'administration.
711
+
712
+ ## ⚠️ Pièges (symptôme → cause → correction)
713
+
714
+ | Symptôme | Cause | Correction |
715
+ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
716
+ | `host`/`port`/`dbname` semblent ignorés | Un `uri` est présent (dans le fichier ou via l'environnement) et **prime** toujours | Retirer `uri`, ou tout exprimer dedans |
717
+ | `NF__MONGOOSE__CONNECTORS__NODEFONY__URI` sans effet, `WARNING` au boot | `uri` n'a pas de défaut → le chemin n'existe pas ; l'override ne crée jamais une clé | Utiliser `MONGODB_URI` (ou `NF_DATABASE_URL`) |
718
+ | `NF_DATABASE_URL` est bien posée, Mongo n'est pas utilisé | L'URL est de famille SQL : elle appartient à `@nodefony/drizzle` | Une URL `mongodb://`/`mongodb+srv://`, ou passer par `MONGODB_URI` |
719
+ | Le démarrage échoue sur le schéma de l'URL | Schéma inconnu (`mongo://`, faute de frappe) — refus délibéré, jamais de repli | Corriger le schéma : `mongodb://` ou `mongodb+srv://` |
720
+ | `Transaction numbers are only allowed on a replica set…` | Serveur isolé : pas de transactions | Un replica set, même à un nœud (`?replicaSet=rs0&directConnection=true`) |
721
+ | `ORM "nodefony" introuvable` au montage d'un store | `@nodefony/security` chargé **avant** `@nodefony/mongoose` | Placer le driver **avant** dans `modules` (`resolveConnectedOrm()` (`registerStores.ts:64`)) |
722
+ | Le store `mongoose` est introuvable pour les jetons | `frameworkEntities: false` — le module est en mode données seules | Repasser à `true`, ou choisir un backend qui porte la brique |
723
+ | `session: { store: "mongoose" }` marche malgré `frameworkEntities: false` | Attendu : la session s'enregistre à l'import, pas via ce champ | Rien à corriger — voir l'avertissement de la section `frameworkEntities` |
724
+ | Une option de `options` n'a aucun effet | Elle n'est pas validée par Zod ; Mongoose l'a ignorée ou refusée | Vérifier son nom exact dans les `ConnectOptions` de la version de Mongoose installée |
725
+ | `CRITIC` : « index DÉCLARÉS mais ABSENTS » | Construction refusée (doublons présents) ou `autoIndex: false` | Voir « Quand un index manque en production » — jamais de réparation automatique |
726
+ | Le serveur démarre sans base en développement, tombe en production | Politique de boot : fail-soft en développement, arrêt franc en production | Attendu — s'assurer que la base est joignable avant de déployer |
727
+ | Deux ORM entrent en collision sur l'entité `session` | Un autre driver déclare le même nom de connecteur | Garder `nodefony` pour Mongoose (c'est précisément à quoi sert ce nom distinct) |
728
+
729
+ ## 🧪 Tests et couverture
730
+
731
+ Deux familles couvrent la configuration ; les compteurs exacts sont recomptés à chaque génération,
732
+ jamais figés ici.
733
+
734
+ - **Unitaires, sans aucune base** (`tests/unit/config.test.ts`) : les défauts du connecteur `nodefony`,
735
+ la fusion d'un connecteur personnalisé avec les défauts manquants, le rejet d'un port hors bornes,
736
+ les deux variables du driver (`MONGODB_URI`, `NF_MONGODB_DEBUG`), le gel de la configuration retournée,
737
+ et la production du JSON Schema. C'est **la seule famille qui tourne sans serveur Mongo**.
738
+ - **Intégration, sur un vrai `mongod`** (`tests/integration/MongooseService.test.ts` et les dix autres
739
+ bancs) : l'assemblage d'URI, l'ouverture et la fermeture des connexions, puis tout le reste du
740
+ module — contrat ORM, session, jetons, passkeys, webhooks, utilisateurs.
741
+
742
+ Ce qui **n'est pas** couvert, dit franchement : la surcharge par l'infra déclarée
743
+ (`NF_DATABASE_URL`/`DATABASE_URL`) n'a pas de test unitaire propre à ce module — seule la variable
744
+ dédiée `MONGODB_URI` en a un. Il n'y a pas non plus de test de charge ni de mesure mémoire dédiés au
745
+ driver.
746
+
747
+ > [!WARNING]
748
+ > **Un « tout vert » ne prouve pas ce qu'on croit ici.** L'immense majorité des cas exige un serveur
749
+ > MongoDB. Sans lui, l'infrastructure de test fournit `mongoUri` à `null`
750
+ > (`globalSetup.ts:48`), chaque banc se met alors en `describe.skipIf` (`mongoTestUri()` (`mongoTestUri.ts:12`))…
751
+ > **et un test sauté compte comme vert.** La suite passe alors en n'ayant réellement exercé que la
752
+ > configuration — c'est-à-dire une petite minorité des cas. Avant de conclure « ça marche », vérifie
753
+ > que la base était là : soit `NF_MONGO_TEST_URI` pointe un conteneur
754
+ > (`docker run -p 27017:27017 mongo:7`), soit le serveur en mémoire a démarré.
755
+ >
756
+ > Le catalogue des variables d'infrastructure du dépôt est `vitest.gates.ts`, à la racine. Ce module
757
+ > **n'y déclare pas de porte** : ses sauts sont donc silencieux, là où les suites SQL et Redis
758
+ > annoncent en fin de course ce qu'elles n'ont pas joué.
759
+
760
+ Couverture : `npm run coverage` dans `@nodefony/mongoose`.
761
+
762
+ ## 🔗 Pour aller plus loin
763
+
764
+ - ⬆️ **Retour au hub** : [MongoDB (Mongoose) — vue d'ensemble](./index.md) ·
765
+ [Toute la documentation](../../../../../docs/index.md)
766
+ - 🧭 **Le socle** : [`@nodefony/orm-core`](../../orm-core/docs/index.md) — les contrats portables ·
767
+ [tutoriel : créer une entité](../../orm-core/docs/tutorial-entity.md)
768
+ - 🔄 **L'autre driver** : [`@nodefony/drizzle`](../../drizzle/docs/index.md) — la référence de cette
769
+ convention de configuration · [`@nodefony/redis`](../../redis/docs/index.md)
770
+ - 🔌 **Les modules servis** : [sessions HTTP](../../http/docs/session.md) ·
771
+ [jetons](../../security/docs/tokens.md) · [passkeys](../../security/docs/webauthn.md) ·
772
+ [webhooks](../../security/docs/webhooks.md) · [utilisateurs](../../user/docs/index.md)
773
+ - 🏛️ **Transverse** : [guide de configuration](../../../../../docs/guides/configuration.md) —
774
+ `defineConfig`, `use()`, l'infra déclarée · [guide de la persistance](../../../../../docs/guides/persistence.md) ·
775
+ [stockage de session](../../../../../docs/guides/session-storage.md)
776
+ - 📖 [Lexique général](../../../../../docs/lexique.md) · [Par où démarrer](../../../../../docs/demarrer.md)