@nodefony/mongoose 10.0.0-beta.1 → 10.0.0-beta.2
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.
- package/README.md +13 -12
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/index.js +2 -2
- package/dist/nodefony/config/config.js +1 -1
- package/dist/nodefony/entity/sessionEntity.js +1 -1
- package/dist/nodefony/src/MongooseTokenStore.js +1 -1
- package/dist/nodefony/src/MongooseUserRepository.js +1 -1
- package/dist/nodefony/src/MongooseWebhookStore.js +1 -1
- package/dist/nodefony/src/orm-core/MongooseRepository.js +15 -19
- package/docs/configuration.md +19 -21
- package/docs/index.md +12 -13
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -55,18 +55,19 @@ durables sont portées, aucune ne retombe sur un autre backend. C'est la propri
|
|
|
55
55
|
de choisir sa base — un backend durable est un chemin complet, ou il oblige à en rapatrier un
|
|
56
56
|
second pour une seule brique.
|
|
57
57
|
|
|
58
|
-
| Brique | Ce qu'elle range | Comment l'activer
|
|
59
|
-
| ------------ | ----------------------------------------- |
|
|
60
|
-
| Session | sessions HTTP | `use("@nodefony/http", { session: { store: "mongoose" } })`
|
|
61
|
-
| Utilisateurs | l'annuaire | `
|
|
62
|
-
| Jetons | rafraîchissement, clés d'API, révocations | `tokenStore: { store: "mongoose" }`
|
|
63
|
-
| Passkeys | credentials WebAuthn | `passkeys: { store: "mongoose" }`
|
|
64
|
-
| TOTP | secrets du second facteur | `totp: { store: "mongoose" }`
|
|
65
|
-
| Audit | journal de sécurité append-only | `audit: { store: "mongoose" }`
|
|
66
|
-
| Webhooks | endpoints notifiés | `webhooks: { store: "mongoose" }`
|
|
67
|
-
| Idempotence | dédoublonnage des mutations rejouées | `
|
|
68
|
-
|
|
69
|
-
Les
|
|
58
|
+
| Brique | Ce qu'elle range | Comment l'activer |
|
|
59
|
+
| ------------ | ----------------------------------------- | -------------------------------------------------------------------- |
|
|
60
|
+
| Session | sessions HTTP | `use("@nodefony/http", { session: { store: "mongoose" } })` |
|
|
61
|
+
| Utilisateurs | l'annuaire | `provisionUsers` → `MongooseUserRepository.from(orm)` |
|
|
62
|
+
| Jetons | rafraîchissement, clés d'API, révocations | `tokenStore: { store: "mongoose" }` |
|
|
63
|
+
| Passkeys | credentials WebAuthn | `passkeys: { store: "mongoose" }` |
|
|
64
|
+
| TOTP | secrets du second facteur | `totp: { store: "mongoose" }` |
|
|
65
|
+
| Audit | journal de sécurité append-only | `audit: { store: "mongoose" }` |
|
|
66
|
+
| Webhooks | endpoints notifiés | `webhooks: { store: "mongoose" }` |
|
|
67
|
+
| Idempotence | dédoublonnage des mutations rejouées | `use("@nodefony/framework", { idempotency: { store: "mongoose" } })` |
|
|
68
|
+
|
|
69
|
+
Les configurations des jetons, passkeys, TOTP, audit et webhooks vivent sous
|
|
70
|
+
`use("@nodefony/security", …)`.
|
|
70
71
|
**Rien d'autre à écrire** : charger le module suffit à rendre ces backends sélectionnables, et
|
|
71
72
|
`store: "auto"` (le défaut) les choisit tout seul dès qu'une infra Mongo est déclarée
|
|
72
73
|
(`NF_DATABASE_URL=mongodb://…`).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorate.js
|
|
2
2
|
function __decorate(decorators, target, key, desc) {
|
|
3
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorateMetadata.js
|
|
2
2
|
function __decorateMetadata(k, v) {
|
|
3
3
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4
4
|
}
|
package/dist/index.js
CHANGED
|
@@ -19,8 +19,8 @@ import { MongooseAuditStore } from "./nodefony/src/MongooseAuditStore.js";
|
|
|
19
19
|
import { MongooseTotpSecretStore } from "./nodefony/src/MongooseTotpSecretStore.js";
|
|
20
20
|
import { MongooseIdempotencyStore } from "./nodefony/src/MongooseIdempotencyStore.js";
|
|
21
21
|
import { FRAMEWORK_CONNECTOR, registerMongooseFrameworkStores } from "./nodefony/registerStores.js";
|
|
22
|
-
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.
|
|
23
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.
|
|
22
|
+
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
|
|
23
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
|
|
24
24
|
import sessionEntity_default, { SESSION_CONNECTOR, sessionSchema as schema } from "./nodefony/entity/sessionEntity.js";
|
|
25
25
|
import SessionStorage from "./nodefony/src/SessionStorage.js";
|
|
26
26
|
import { MongooseUserRepository } from "./nodefony/src/MongooseUserRepository.js";
|
|
@@ -46,7 +46,7 @@ const connectorSchema = z.strictObject({
|
|
|
46
46
|
const mongooseConfigSchema = z.strictObject({
|
|
47
47
|
debug: z.boolean().default(false).describe("Active la trace Mongoose des requêtes (`mongoose.set('debug')`). Défaut false. Mettre true en dev pour voir chaque opération. Surchargé par l'env `NF_MONGODB_DEBUG` (1/true). Reco prod : false."),
|
|
48
48
|
connectors: z.record(z.string(), connectorSchema).default(() => ({ nodefony: connectorSchema.parse({}) })).describe("Connexions indexées par nom (= clé dans le `ormRegistry`). Défaut : un connecteur `nodefony` sur `localhost:27017/nodefony`. Le nom `nodefony` (≠ `default` de Drizzle) évite toute collision d'entité dans le `entityRegistry` (process-wide) si les deux ORM cohabitent."),
|
|
49
|
-
frameworkEntities: z.boolean().default(true).describe("Déclare le schéma framework sur le connecteur `nodefony` (tokens, webauthn, webhooks — modèles compilés au connect) et rend les stores correspondants sélectionnables par nom (`mongoose`).
|
|
49
|
+
frameworkEntities: z.boolean().default(true).describe("Déclare le schéma framework sur le connecteur `nodefony` (tokens, webauthn, totp, audit, webhooks, idempotence — modèles compilés au connect) et rend les stores correspondants sélectionnables par nom (`mongoose`). `false` = module data-only.")
|
|
50
50
|
}).describe("Configuration de @nodefony/mongoose.");
|
|
51
51
|
/**
|
|
52
52
|
* Défauts du module, matérialisés depuis le schéma (source unique). Toujours
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
|
|
2
2
|
import { entity } from "@nodefony/orm-core";
|
|
3
3
|
//#region nodefony/entity/sessionEntity.ts
|
|
4
4
|
/** ORM cible du stockage de session (connecteur par défaut du module). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mongoOrder } from "./mongoOrder.js";
|
|
2
1
|
import { TOKEN_ENTITY_NAMES } from "../entity/tokenEntity.js";
|
|
2
|
+
import { mongoOrder } from "./mongoOrder.js";
|
|
3
3
|
import { assertPageQuery } from "nodefony";
|
|
4
4
|
import { paginate } from "@nodefony/orm-core";
|
|
5
5
|
import { TOKEN_DEFAULT_ORDER, TOKEN_SORTABLE_FIELDS, tokenStatusCriteria } from "@nodefony/security";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mongoOrder, toMongoSort } from "./mongoOrder.js";
|
|
2
1
|
import { DOCUMENT_USER_COLUMNS } from "../entity/userEntity.js";
|
|
2
|
+
import { mongoOrder, toMongoSort } from "./mongoOrder.js";
|
|
3
3
|
import { assertPageQuery, escapeRegExp } from "nodefony";
|
|
4
4
|
import { BaseUser, USER_DEFAULT_ORDER, USER_SORTABLE_FIELDS, assertUserContract, attachExtraColumns } from "@nodefony/user";
|
|
5
5
|
//#region nodefony/src/MongooseUserRepository.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { mongoOrder, toMongoSort } from "./mongoOrder.js";
|
|
2
1
|
import { WEBHOOK_ENDPOINT_ENTITY } from "../entity/webhookEndpointEntity.js";
|
|
2
|
+
import { mongoOrder, toMongoSort } from "./mongoOrder.js";
|
|
3
3
|
import { assertPageQuery, escapeRegExp } from "nodefony";
|
|
4
4
|
import { WEBHOOK_DEFAULT_ORDER, WEBHOOK_SORTABLE_FIELDS } from "@nodefony/security";
|
|
5
5
|
//#region nodefony/src/MongooseWebhookStore.ts
|
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
import { MONGO_ORDER_ALIASES } from "../mongoOrder.js";
|
|
2
1
|
import { writeCount } from "../writeCount.js";
|
|
3
2
|
import { RequestContext, redactSecrets } from "nodefony";
|
|
4
3
|
import { ReferencedEntityError, UnknownCriteriaField, assertOrderOption, isFieldOperators, isUpdateOperators, likePatternToRegExp, queryFlowMonitor } from "@nodefony/orm-core";
|
|
5
4
|
//#region nodefony/src/orm-core/MongooseRepository.ts
|
|
6
5
|
/**
|
|
7
|
-
* Clé Mongo d'un champ de TRI — `id` public → `_id` au repos.
|
|
8
|
-
*
|
|
9
|
-
* Mongo ne se plaint pas d'un tri sur un champ absent : il rend les documents
|
|
10
|
-
* dans un ordre arbitraire. `id` n'est qu'un virtuel de lecture ; sans cette
|
|
11
|
-
* traduction, `order: [["id", "DESC"]]` — le départage du tri par défaut de
|
|
12
|
-
* tout CRUD généré — serait silencieusement inerte ici et correct en SQL. Même
|
|
13
|
-
* table que les stores Mongo ({@link MONGO_ORDER_ALIASES}).
|
|
14
|
-
*/
|
|
15
|
-
function sortKey(field) {
|
|
16
|
-
return MONGO_ORDER_ALIASES[field] ?? field;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
6
|
* Repository portable (contrat {@link IRepository}) au-dessus d'un modèle Mongoose.
|
|
20
7
|
*
|
|
21
8
|
* Démontre la portabilité du contrat sur un **store documentaire hétérogène** :
|
|
@@ -145,14 +132,18 @@ var MongooseRepository = class MongooseRepository {
|
|
|
145
132
|
* donnait 0 résultat silencieux — et divergeait de Drizzle qui, lui, renvoyait
|
|
146
133
|
* tout). Échoue tôt et pareil sur les deux drivers.
|
|
147
134
|
*
|
|
148
|
-
*
|
|
135
|
+
* Sert AUSSI au tri : Mongo ne se plaint pas d'un `sort` sur un champ absent
|
|
136
|
+
* — il rend un ordre arbitraire, là où Drizzle refuse. `id` (virtuel de
|
|
137
|
+
* lecture) y devient `_id`, le départage du tri par défaut d'un CRUD généré.
|
|
138
|
+
*
|
|
139
|
+
* @param field - clé brute du critère ou du tri.
|
|
149
140
|
* @returns la clé Mongo résolue.
|
|
150
141
|
* @throws UnknownCriteriaField si le champ n'existe pas sur le schéma.
|
|
151
142
|
*/
|
|
152
143
|
#resolveField(field) {
|
|
153
144
|
const key = field === "id" ? "_id" : field;
|
|
154
145
|
const paths = this.#model.schema.paths;
|
|
155
|
-
if (key === "_id" || paths
|
|
146
|
+
if (key === "_id" || Object.hasOwn(paths, key) || Object.hasOwn(paths, key.split(".")[0] ?? key)) return key;
|
|
156
147
|
throw new UnknownCriteriaField(field, this.#model.modelName, Object.keys(paths));
|
|
157
148
|
}
|
|
158
149
|
/**
|
|
@@ -165,8 +156,13 @@ var MongooseRepository = class MongooseRepository {
|
|
|
165
156
|
for (const [field, value] of Object.entries(criteria)) {
|
|
166
157
|
if (field === "$or") {
|
|
167
158
|
if (!Array.isArray(value)) throw new TypeError(`MongooseRepository(${this.#model.modelName}): $or attend un tableau de critères.`);
|
|
168
|
-
|
|
169
|
-
|
|
159
|
+
if (value.length === 0) {
|
|
160
|
+
out.$and = [{ _id: { $in: [] } }];
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const branches = value.map((branch) => this.#filter(branch));
|
|
164
|
+
if (branches.some((f) => Object.keys(f).length === 0)) continue;
|
|
165
|
+
out.$or = branches;
|
|
170
166
|
continue;
|
|
171
167
|
}
|
|
172
168
|
const key = this.#resolveField(field);
|
|
@@ -187,7 +183,7 @@ var MongooseRepository = class MongooseRepository {
|
|
|
187
183
|
if (options?.relations?.length) query = query.populate(options.relations);
|
|
188
184
|
if (options?.offset !== void 0) query = query.skip(options.offset);
|
|
189
185
|
if (options?.limit !== void 0) query = query.limit(options.limit);
|
|
190
|
-
if (options?.order?.length) query = query.sort(Object.fromEntries(options.order.map(([field, dir]) => [
|
|
186
|
+
if (options?.order?.length) query = query.sort(Object.fromEntries(options.order.map(([field, dir]) => [this.#resolveField(field), dir === "DESC" ? -1 : 1])));
|
|
191
187
|
return (await query.exec()).map((doc) => this.#plain(doc));
|
|
192
188
|
}, (docs) => docs.length);
|
|
193
189
|
}
|
|
@@ -198,7 +194,7 @@ var MongooseRepository = class MongooseRepository {
|
|
|
198
194
|
let query = this.#model.findOne(filter);
|
|
199
195
|
if (this.#session) query = query.session(this.#session);
|
|
200
196
|
if (options?.relations?.length) query = query.populate(options.relations);
|
|
201
|
-
if (options?.order?.length) query = query.sort(Object.fromEntries(options.order.map(([field, dir]) => [
|
|
197
|
+
if (options?.order?.length) query = query.sort(Object.fromEntries(options.order.map(([field, dir]) => [this.#resolveField(field), dir === "DESC" ? -1 : 1])));
|
|
202
198
|
const doc = await query.exec();
|
|
203
199
|
return doc ? this.#plain(doc) : null;
|
|
204
200
|
}, (doc) => doc ? 1 : 0);
|
package/docs/configuration.md
CHANGED
|
@@ -334,10 +334,12 @@ C'est le champ le plus discret et le plus structurant (`frameworkEntities` (`con
|
|
|
334
334
|
défaut `true`, le module fait deux choses de plus qu'ouvrir des connexions, dès son enregistrement et
|
|
335
335
|
**avant** que la connexion ne s'ouvre (`Mongoose.onKernelRegister()` (`mongoose/index.ts:65`)) :
|
|
336
336
|
|
|
337
|
-
1. il **déclare les entités du framework** sur le connecteur `nodefony` — jetons, passkeys,
|
|
337
|
+
1. il **déclare les entités du framework** sur le connecteur `nodefony` — jetons, passkeys, TOTP,
|
|
338
|
+
audit, webhooks, idempotence —
|
|
338
339
|
pour que leurs modèles soient compilés au moment de la connexion ;
|
|
339
340
|
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
|
+
rend le nom `"mongoose"` sélectionnable dans `tokenStore`, `passkeys`, `totp`, `audit`, `webhooks`
|
|
342
|
+
et `idempotency`
|
|
341
343
|
(`registerMongooseFrameworkStores()` (`registerStores.ts:126`)).
|
|
342
344
|
|
|
343
345
|
Le passer à `false` transforme le module en **pur driver de données** : tes entités à toi, rien
|
|
@@ -667,22 +669,19 @@ Le cas courant : la config est parfaite, mais Mongo n'est pas joignable — cont
|
|
|
667
669
|
réseau coupé, identifiants périmés. Le comportement **dépend de l'environnement**, arbitré par la
|
|
668
670
|
politique de boot du cœur (`Kernel.isBootErrorFatal()` (`Kernel.ts:3221`)) :
|
|
669
671
|
|
|
670
|
-
| Environnement
|
|
671
|
-
|
|
|
672
|
-
| Développement, test | `WARNING`, l'échec est agrégé au bilan de démarrage, **le serveur démarre quand même** |
|
|
673
|
-
|
|
|
672
|
+
| Environnement | Ce qui se passe |
|
|
673
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
674
|
+
| Développement, test **et production** | `WARNING`, l'échec est agrégé au bilan de démarrage, **le serveur démarre quand même** (module `critical = false`) |
|
|
675
|
+
| Toute configuration invalide | Fatale partout : une `BootConfigurationError` ne se répare pas en attendant |
|
|
674
676
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
tomber pour le relancer, et c'est le modèle cloud-native que le framework applique partout.
|
|
677
|
+
Un pod qui démarre sans sa base n'est pas pour autant mis en service à l'aveugle : c'est la sonde de
|
|
678
|
+
disponibilité (`/readyz`) qui dit à l'orchestrateur de ne pas lui envoyer de trafic.
|
|
678
679
|
|
|
679
680
|
> [!IMPORTANT]
|
|
680
|
-
> **Le module est déclaré non critique** (`Mongoose.critical` (`mongoose/index.ts:48`)), et
|
|
681
|
-
>
|
|
682
|
-
> par
|
|
683
|
-
>
|
|
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.
|
|
681
|
+
> **Le module est déclaré non critique** (`Mongoose.critical` (`mongoose/index.ts:48`)), et l'ouverture
|
|
682
|
+
> de la connexion est posée par `module.hookKernel("onBoot", …)` (`MongooseService.ts:59`), donc
|
|
683
|
+
> couverte par cette étiquette : même en production, une base injoignable n'interrompt pas le
|
|
684
|
+
> démarrage. Prévois une sonde de disponibilité côté orchestrateur.
|
|
686
685
|
|
|
687
686
|
### Pendant l'arrêt du serveur
|
|
688
687
|
|
|
@@ -739,9 +738,8 @@ jamais figés ici.
|
|
|
739
738
|
bancs) : l'assemblage d'URI, l'ouverture et la fermeture des connexions, puis tout le reste du
|
|
740
739
|
module — contrat ORM, session, jetons, passkeys, webhooks, utilisateurs.
|
|
741
740
|
|
|
742
|
-
|
|
743
|
-
(`
|
|
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
|
|
741
|
+
La branche infra (`NF_DATABASE_URL` de famille mongo) et `MONGODB_URI` ont chacune un test unitaire
|
|
742
|
+
(`tests/unit/config.test.ts`). Ce qui **n'est pas** couvert, dit franchement : il n'y a pas de test de charge ni de mesure mémoire dédiés au
|
|
745
743
|
driver.
|
|
746
744
|
|
|
747
745
|
> [!WARNING]
|
|
@@ -753,9 +751,9 @@ driver.
|
|
|
753
751
|
> que la base était là : soit `NF_MONGO_TEST_URI` pointe un conteneur
|
|
754
752
|
> (`docker run -p 27017:27017 mongo:7`), soit le serveur en mémoire a démarré.
|
|
755
753
|
>
|
|
756
|
-
> Le catalogue des variables d'infrastructure du dépôt est `vitest.gates.ts`, à la racine. Ce module
|
|
757
|
-
>
|
|
758
|
-
>
|
|
754
|
+
> Le catalogue des variables d'infrastructure du dépôt est `vitest.gates.ts`, à la racine. Ce module y
|
|
755
|
+
> déclare sa porte `MONGO_GATE` : le rapporteur de fin de course nomme la cible Mongo non exercée, avec
|
|
756
|
+
> la commande exacte pour la monter.
|
|
759
757
|
|
|
760
758
|
Couverture : `npm run coverage` dans `@nodefony/mongoose`.
|
|
761
759
|
|
package/docs/index.md
CHANGED
|
@@ -30,8 +30,8 @@ coverageFiles: MongooseOrm.ts,MongooseRepository.ts,SessionStorage.ts,MongooseTo
|
|
|
30
30
|
|
|
31
31
|
> Le module qui fait parler ton application à **MongoDB**. Il ouvre les connexions au démarrage,
|
|
32
32
|
> compile tes schémas en modèles, et te rend des **repositories portables** : le même code métier
|
|
33
|
-
> tourne sur Mongo ou sur SQL. Il fournit aussi
|
|
34
|
-
> Mongo — sessions, utilisateurs, jetons, passkeys, webhooks. Ce qu'il ne porte pas, il le dit :
|
|
33
|
+
> tourne sur Mongo ou sur SQL. Il fournit aussi huit **briques du framework** prêtes à l'emploi sur
|
|
34
|
+
> Mongo — sessions, utilisateurs, jetons, passkeys, TOTP, audit, webhooks, idempotence. Ce qu'il ne porte pas, il le dit :
|
|
35
35
|
> la couverture est **adaptée à la vocation de MongoDB**, jamais une course à la parité avec SQL.
|
|
36
36
|
|
|
37
37
|
📍 [Documentation](../../../../../docs/index.md) › **MongoDB (Mongoose)**
|
|
@@ -139,7 +139,7 @@ flowchart TD
|
|
|
139
139
|
ENT["Tes entités<br/>defineEntity + @entities"] --> MODEL
|
|
140
140
|
|
|
141
141
|
SESS["session"] -.->|store: mongoose| MR
|
|
142
|
-
TOK["tokens · passkeys · webhooks"] -.->|store: mongoose| MR
|
|
142
|
+
TOK["tokens · passkeys · totp · audit · webhooks · idempotence"] -.->|store: mongoose| MR
|
|
143
143
|
USR["users"] -.->|provisionUsers| MR
|
|
144
144
|
```
|
|
145
145
|
|
|
@@ -231,12 +231,12 @@ réellement chargés :
|
|
|
231
231
|
| Ta situation | Ce que `auto` choisit |
|
|
232
232
|
| ------------------------------------------------------- | -------------------------------------------------------- |
|
|
233
233
|
| `NF_DATABASE_URL=mongodb://…` + module mongoose chargé | `mongoose` — « infra database (mongodb) » |
|
|
234
|
-
|
|
|
234
|
+
| Une brique que le backend d'infra ne porte pas | repli annoncé (`memory`), avec la **raison** dans le log |
|
|
235
235
|
| Aucune infra déclarée, mongoose chargé (et pas drizzle) | `mongoose` — backend local persistant |
|
|
236
236
|
| `NF_REDIS_URL` déclaré, brique non durable (session…) | `redis` d'abord (le cache passe avant la base) |
|
|
237
237
|
|
|
238
238
|
Rien n'est jamais dégradé en silence : la raison du choix est journalisée. Et si tu nommes
|
|
239
|
-
**explicitement** un store qui n'existe pas (`audit: { store: "
|
|
239
|
+
**explicitement** un store qui n'existe pas (`audit: { store: "mongo" }`), l'échec est franc —
|
|
240
240
|
boot avorté en production, brique désactivée avec un log `CRITIC` en développement. Un store durable
|
|
241
241
|
ne retombe **jamais** en mémoire sans le dire.
|
|
242
242
|
|
|
@@ -469,7 +469,7 @@ sequenceDiagram
|
|
|
469
469
|
|
|
470
470
|
K->>M: onKernelRegister
|
|
471
471
|
M->>M: valider la config (Zod) + geler
|
|
472
|
-
M->>M: déclarer les entités framework<br/>(tokens · passkeys · webhooks)
|
|
472
|
+
M->>M: déclarer les entités framework<br/>(tokens · passkeys · totp · audit · webhooks · idempotence)
|
|
473
473
|
M->>M: déclarer "mongoose" comme backend utilisateur
|
|
474
474
|
K->>M: onKernelBoot
|
|
475
475
|
M->>M: monter le data plane ORM + l'adapter d'erreurs
|
|
@@ -570,8 +570,8 @@ await articles.find({ publishedAt: { $null: true } }); // jamais publié
|
|
|
570
570
|
```
|
|
571
571
|
|
|
572
572
|
> [!WARNING]
|
|
573
|
-
> Un critère ne combine **qu'une condition par champ** (c'est un ET de champs
|
|
574
|
-
> agrégation, une recherche plein texte ou un index composé : passe par
|
|
573
|
+
> Un critère ne combine **qu'une condition par champ** (c'est un ET de champs ; la disjonction se
|
|
574
|
+
> dit `$or: [...]`). Pour une agrégation, une recherche plein texte ou un index composé : passe par
|
|
575
575
|
> [la trappe native](#la-trappe-native--quand-le-contrat-ne-suffit-plus). C'est prévu, pas subi.
|
|
576
576
|
|
|
577
577
|
### Relations — sans clé étrangère
|
|
@@ -874,10 +874,10 @@ Le module suit la règle de fond du framework : **ce qui n'est pas observé ne c
|
|
|
874
874
|
| `ORM "nodefony" introuvable` au montage d'un store | `@nodefony/security` chargé **avant** `@nodefony/mongoose` | Mettre le driver **avant** dans `modules` (`registerStores.ts:64`) |
|
|
875
875
|
| `no entity model registered under "X"` | Entité déclarée après la connexion (les modèles sont compilés au `connect`) | Déclarer via `@entities` (phase `onRegister`), jamais à `onBoot` |
|
|
876
876
|
| `UnknownCriteriaField` sur un champ pourtant présent en base | Le champ n'est pas dans le **schéma** déclaré | L'ajouter au schéma, ou passer par la connexion native |
|
|
877
|
-
| Un critère renvoie tout au lieu de filtrer | Deux conditions posées sur le **même champ** (le critère est un ET de champs) |
|
|
877
|
+
| Un critère renvoie tout au lieu de filtrer | Deux conditions posées sur le **même champ** (le critère est un ET de champs) | `$or: [...]`, ou une requête native (agrégation) |
|
|
878
878
|
| `many-to-many non portable` | Refus explicite : pas de traduction unique en Mongo | Déclarer la relation via la connexion native |
|
|
879
879
|
| Les sessions disparaissent au redémarrage | `session.store` resté sur `memory` | `session: { store: "mongoose" }`, ou déclarer `NF_DATABASE_URL` et laisser `auto` |
|
|
880
|
-
| `audit store "
|
|
880
|
+
| `audit store "xxx" inconnu` — boot avorté en production | Nom de store inexistant, ou `frameworkEntities: false` | Corriger le nom, ou laisser `auto` |
|
|
881
881
|
| Les comptes ne survivent pas au redémarrage | `provisionUsers` toujours branché sur l'annuaire mémoire | Câbler `MongooseUserRepository.from(orm)` (`MongooseUserRepository.ts:89`) |
|
|
882
882
|
| Un champ écrit se relit `undefined`, sans aucune erreur | Il n'est pas dans le **schéma** : Mongoose est strict et l'écarte en silence | L'ajouter au schéma de l'entité — le type TypeScript seul ne suffit pas |
|
|
883
883
|
| Le premier `npm test` du module met une éternité | Le serveur Mongo de test télécharge son binaire (une seule fois) | Définir `NF_MONGO_TEST_URI` sur un conteneur Mongo |
|
|
@@ -908,9 +908,8 @@ couverts en amont, dans les modules qui possèdent les contrats.
|
|
|
908
908
|
> bien là : soit `NF_MONGO_TEST_URI` pointe sur un conteneur (`docker run -p 27017:27017 mongo:7`), soit
|
|
909
909
|
> le serveur en mémoire a démarré. Les bancs de transaction exigent en plus un **replica set**.
|
|
910
910
|
>
|
|
911
|
-
> Le catalogue des variables d'infrastructure du dépôt est `vitest.gates.ts`, à la racine. Ce module
|
|
912
|
-
>
|
|
913
|
-
> Redis affichent en fin de course ce qu'elles n'ont pas joué.
|
|
911
|
+
> Le catalogue des variables d'infrastructure du dépôt est `vitest.gates.ts`, à la racine. Ce module y
|
|
912
|
+
> déclare sa porte `MONGO_GATE` : le rapporteur de fin de course nomme la cible Mongo non exercée.
|
|
914
913
|
|
|
915
914
|
Couverture : `npm run coverage` dans `@nodefony/mongoose` (rapport lisible aussi dans Studio).
|
|
916
915
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodefony/mongoose",
|
|
3
|
-
"version": "10.0.0-beta.
|
|
3
|
+
"version": "10.0.0-beta.2",
|
|
4
4
|
"description": "Moteur MongoDB de Nodefony sur Mongoose : dépôts typés conformes au contrat orm-core",
|
|
5
5
|
"nodefony": {
|
|
6
6
|
"storeKind": "durable",
|
|
@@ -49,29 +49,29 @@
|
|
|
49
49
|
"esm"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"mongodb": "7.
|
|
53
|
-
"mongoose": "9.10.
|
|
52
|
+
"mongodb": "7.7.0",
|
|
53
|
+
"mongoose": "9.10.3",
|
|
54
54
|
"tslib": "2.8.1",
|
|
55
55
|
"zod": "^4.6.5"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@nodefony/framework": "^10.0.0-beta.
|
|
59
|
-
"@nodefony/http": "^10.0.0-beta.
|
|
60
|
-
"@nodefony/orm-core": "^10.0.0-beta.
|
|
61
|
-
"@nodefony/security": "^10.0.0-beta.
|
|
62
|
-
"@nodefony/user": "^10.0.0-beta.
|
|
58
|
+
"@nodefony/framework": "^10.0.0-beta.2",
|
|
59
|
+
"@nodefony/http": "^10.0.0-beta.2",
|
|
60
|
+
"@nodefony/orm-core": "^10.0.0-beta.2",
|
|
61
|
+
"@nodefony/security": "^10.0.0-beta.2",
|
|
62
|
+
"@nodefony/user": "^10.0.0-beta.2",
|
|
63
63
|
"@types/node": "26.6.3",
|
|
64
64
|
"mongodb-memory-server": "11.3.0",
|
|
65
|
-
"nodefony": "^10.0.0-beta.
|
|
66
|
-
"vitest": "5.0.
|
|
65
|
+
"nodefony": "^10.0.0-beta.2",
|
|
66
|
+
"vitest": "5.0.3"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@nodefony/framework": "^10.0.0-beta.
|
|
70
|
-
"@nodefony/http": "^10.0.0-beta.
|
|
71
|
-
"@nodefony/orm-core": "^10.0.0-beta.
|
|
72
|
-
"@nodefony/security": "^10.0.0-beta.
|
|
73
|
-
"@nodefony/user": "^10.0.0-beta.
|
|
74
|
-
"nodefony": "^10.0.0-beta.
|
|
69
|
+
"@nodefony/framework": "^10.0.0-beta.2",
|
|
70
|
+
"@nodefony/http": "^10.0.0-beta.2",
|
|
71
|
+
"@nodefony/orm-core": "^10.0.0-beta.2",
|
|
72
|
+
"@nodefony/security": "^10.0.0-beta.2",
|
|
73
|
+
"@nodefony/user": "^10.0.0-beta.2",
|
|
74
|
+
"nodefony": "^10.0.0-beta.2"
|
|
75
75
|
},
|
|
76
76
|
"repository": {
|
|
77
77
|
"type": "git",
|