@nodefony/mongoose 10.0.0-alpha.8 → 10.0.0-beta.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.
- package/README.md +32 -0
- package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/index.js +13 -7
- package/dist/nodefony/config/defineModuleConfig.js +4 -3
- package/dist/nodefony/entity/auditEventEntity.js +124 -0
- package/dist/nodefony/entity/idempotencyEntity.js +96 -0
- package/dist/nodefony/entity/sessionEntity.js +1 -1
- package/dist/nodefony/entity/totpSecretEntity.js +103 -0
- package/dist/nodefony/registerStores.js +42 -5
- package/dist/nodefony/service/MongooseService.js +15 -5
- package/dist/nodefony/src/MongooseAuditStore.js +215 -0
- package/dist/nodefony/src/MongooseIdempotencyStore.js +264 -0
- package/dist/nodefony/src/MongooseTokenStore.js +60 -17
- package/dist/nodefony/src/MongooseTotpSecretStore.js +151 -0
- package/dist/nodefony/src/MongooseUserRepository.js +1 -1
- package/dist/nodefony/src/MongooseWebAuthnCredentialStore.js +13 -1
- package/dist/nodefony/src/MongooseWebhookStore.js +14 -2
- package/dist/nodefony/src/SessionStorage.js +8 -0
- package/dist/nodefony/src/orm-core/MongooseOrm.js +68 -22
- package/dist/nodefony/src/orm-core/MongooseRepository.js +147 -25
- package/dist/nodefony/src/orm-core/MongooseTransaction.js +19 -6
- package/dist/nodefony/src/writeCount.js +17 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/nodefony/entity/auditEventEntity.d.ts +76 -0
- package/dist/types/nodefony/entity/idempotencyEntity.d.ts +75 -0
- package/dist/types/nodefony/entity/totpSecretEntity.d.ts +75 -0
- package/dist/types/nodefony/registerStores.d.ts +6 -3
- package/dist/types/nodefony/service/MongooseService.d.ts +2 -2
- package/dist/types/nodefony/src/MongooseAuditStore.d.ts +70 -0
- package/dist/types/nodefony/src/MongooseIdempotencyStore.d.ts +121 -0
- package/dist/types/nodefony/src/MongooseTokenStore.d.ts +8 -1
- package/dist/types/nodefony/src/MongooseTotpSecretStore.d.ts +63 -0
- package/dist/types/nodefony/src/MongooseUserRepository.d.ts +1 -1
- package/dist/types/nodefony/src/MongooseWebAuthnCredentialStore.d.ts +6 -0
- package/dist/types/nodefony/src/MongooseWebhookStore.d.ts +6 -0
- package/dist/types/nodefony/src/SessionStorage.d.ts +6 -0
- package/dist/types/nodefony/src/orm-core/MongooseOrm.d.ts +10 -3
- package/dist/types/nodefony/src/orm-core/MongooseRepository.d.ts +22 -1
- package/dist/types/nodefony/src/orm-core/MongooseTransaction.d.ts +12 -4
- package/dist/types/nodefony/src/writeCount.d.ts +12 -0
- package/docs/configuration.md +27 -27
- package/docs/index.md +133 -82
- package/package.json +23 -19
package/docs/index.md
CHANGED
|
@@ -192,11 +192,11 @@ des connexions, la compilation des schémas, et présente le tout derrière le c
|
|
|
192
192
|
|
|
193
193
|
Nodefony ne demande pas à chaque backend de tout savoir faire. **Chaque adapter déclare ce qu'il
|
|
194
194
|
implémente**, dans son `package.json` (clé `nodefony.stores`), et le framework lit cette déclaration
|
|
195
|
-
à chaud (`readAdapterManifest()` (`KernelAdminApi.ts:
|
|
195
|
+
à chaud (`readAdapterManifest()` (`KernelAdminApi.ts:93`)). Rien n'est curaté dans le cœur : la
|
|
196
196
|
source de vérité, c'est l'adapter lui-même — ce qui vaut aussi pour un adapter tiers.
|
|
197
197
|
|
|
198
|
-
Ce que `@nodefony/mongoose` déclare : `session`, `user`, `tokens`, `passkeys`,
|
|
199
|
-
nature `durable`. Comparé aux deux autres adapters officiels :
|
|
198
|
+
Ce que `@nodefony/mongoose` déclare : les huit briques — `session`, `user`, `tokens`, `passkeys`,
|
|
199
|
+
`totp`, `audit`, `webhooks`, `idempotency` — avec la nature `durable`. Comparé aux deux autres adapters officiels :
|
|
200
200
|
|
|
201
201
|
<!-- prettier-ignore -->
|
|
202
202
|
| Brique | `@nodefony/drizzle` (SQL) | `@nodefony/mongoose` (Mongo) | `@nodefony/redis` (cache) |
|
|
@@ -206,24 +206,16 @@ nature `durable`. Comparé aux deux autres adapters officiels :
|
|
|
206
206
|
| `tokens` | ✅ | ✅ | ✅ |
|
|
207
207
|
| `passkeys` | ✅ | ✅ | ✅ |
|
|
208
208
|
| `webhooks` | ✅ | ✅ | — |
|
|
209
|
-
| `totp` | ✅ |
|
|
210
|
-
| `audit` | ✅ |
|
|
211
|
-
| `idempotency` | ✅ |
|
|
209
|
+
| `totp` | ✅ | ✅ | — |
|
|
210
|
+
| `audit` | ✅ | ✅ | — |
|
|
211
|
+
| `idempotency` | ✅ | ✅ | ✅ |
|
|
212
212
|
| Nature | durable | durable | cache |
|
|
213
213
|
|
|
214
214
|
> [!IMPORTANT]
|
|
215
|
-
> **
|
|
216
|
-
>
|
|
217
|
-
>
|
|
218
|
-
>
|
|
219
|
-
> ne choisis pas de perdre le 2FA, la traçabilité ou la déduplication.**
|
|
220
|
-
>
|
|
221
|
-
> En attendant, ces trois briques se résolvent ailleurs **et te le disent** (repli annoncé au boot,
|
|
222
|
-
> avertissement en production) — mais mesure ce que le repli coûte : secrets TOTP perdus au
|
|
223
|
-
> redémarrage (utilisateurs verrouillés hors de leur second facteur), journal d'audit volatil,
|
|
224
|
-
> idempotence sans effet entre pods. La parade immédiate tient en une ligne : charger
|
|
225
|
-
> `@nodefony/drizzle` à côté de Mongo, **même en SQLite local** — les deux modules cohabitent, chaque
|
|
226
|
-
> brique choisit son store.
|
|
215
|
+
> **Une application tourne entièrement sur MongoDB, sans charger `@nodefony/drizzle`.** Les huit
|
|
216
|
+
> briques y ont leur store, et ce n'est pas qu'une déclaration : `npm run test:all -- --mongo`
|
|
217
|
+
> démarre l'application sur MongoDB et rejoue toute la passe d'intégration, et chaque store joue le
|
|
218
|
+
> même banc de contrat que son jumeau SQL — cf [Parité des adaptateurs](../../../../../docs/guides/parite-des-adaptateurs.md).
|
|
227
219
|
>
|
|
228
220
|
> La colonne `redis` obéit à une autre logique : elle gagnera `totp` (au régime opt-in de ses jetons
|
|
229
221
|
> et passkeys, jamais choisi par `auto`), mais pas `user`, `audit` ni `webhooks` — non parce que
|
|
@@ -233,7 +225,7 @@ nature `durable`. Comparé aux deux autres adapters officiels :
|
|
|
233
225
|
### Ce qui se passe quand tu ne choisis rien
|
|
234
226
|
|
|
235
227
|
Chaque brique a une clé `store` dont le défaut est `"auto"`. La résolution
|
|
236
|
-
(`resolveAutoStore()` (`infra.ts:
|
|
228
|
+
(`resolveAutoStore()` (`infra.ts:297`)) suit l'infrastructure **déclarée**, bornée aux backends
|
|
237
229
|
réellement chargés :
|
|
238
230
|
|
|
239
231
|
| Ta situation | Ce que `auto` choisit |
|
|
@@ -323,6 +315,9 @@ const ArticleEntity = defineEntity({
|
|
|
323
315
|
module: "blog",
|
|
324
316
|
schema: articleSchema,
|
|
325
317
|
timestamps: true, // Mongoose gère createdAt / updatedAt
|
|
318
|
+
// Index COMPOSITE : un schéma plat n'en exprime que par champ. Sans lui, un
|
|
319
|
+
// tri sur deux champs parcourt la collection puis trie en mémoire.
|
|
320
|
+
indexes: [{ fields: { views: -1, createdAt: -1 } }], // « les plus lus, récents d'abord »
|
|
326
321
|
});
|
|
327
322
|
|
|
328
323
|
// ── 2. Le controller : le repository se demande au registre ─────────────────
|
|
@@ -362,6 +357,23 @@ class Blog extends Module {
|
|
|
362
357
|
export default Blog;
|
|
363
358
|
```
|
|
364
359
|
|
|
360
|
+
### Le raccourci — `nodefony create entity`
|
|
361
|
+
|
|
362
|
+
Dans une application MongoDB (`create app --database mongodb`), tout ce qui précède se génère,
|
|
363
|
+
avec la même grammaire de champs qu'en SQL :
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
npx nodefony create entity Post title:string(120) body:text? views:int=0 author:ref:User
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
La commande écrit le schéma Mongoose (`nodefony/entity/Post.ts`), les schémas de validation, un
|
|
370
|
+
service CRUD, un controller REST + WebSocket et les tests, puis câble `@entities([...])` et
|
|
371
|
+
`@controllers([...])`. L'entité se pose sur le connecteur `nodefony` ; une référence est un
|
|
372
|
+
`ObjectId` indexé, que le schéma d'entrée exige bien formé (24 caractères hexadécimaux, sinon 422).
|
|
373
|
+
Les options propres au SQL (`--table`, `--column-case`, `--id-name`, `--dialect`, `--id`,
|
|
374
|
+
`--index`, `--unique`) sont refusées en le disant ; l'entité `User` ne se régénère pas, elle
|
|
375
|
+
s'étend dans `nodefony/entity/User.ts`.
|
|
376
|
+
|
|
365
377
|
### 3. Ce qu'on observe
|
|
366
378
|
|
|
367
379
|
```bash
|
|
@@ -387,7 +399,7 @@ INFO mongoose Mongoose ORM "nodefony" connected (127.0.0.1:27017/blog)
|
|
|
387
399
|
|
|
388
400
|
Une fois le driver chargé, les stores Mongo deviennent **sélectionnables par leur nom**, sans aucun
|
|
389
401
|
câblage : le module les enregistre lui-même à son démarrage
|
|
390
|
-
(`registerMongooseFrameworkStores()` (`registerStores.ts:
|
|
402
|
+
(`registerMongooseFrameworkStores()` (`registerStores.ts:126`)).
|
|
391
403
|
|
|
392
404
|
```typescript
|
|
393
405
|
// nodefony.config.ts — sessions, jetons, passkeys et webhooks dans Mongo
|
|
@@ -472,7 +484,7 @@ sequenceDiagram
|
|
|
472
484
|
|
|
473
485
|
L'ordre n'est pas cosmétique. Les entités sont déclarées à `onKernelRegister`, **strictement avant**
|
|
474
486
|
le `connect` de `onBoot` : les modèles sont compilés à la connexion
|
|
475
|
-
(`MongooseOrm.onConnect()` (`MongooseOrm.ts:
|
|
487
|
+
(`MongooseOrm.onConnect()` (`MongooseOrm.ts:137`)), donc une entité déclarée trop tard n'existe tout
|
|
476
488
|
simplement pas. C'est aussi pour ça que `@entities` s'exécute à la phase `onRegister` et non `onBoot`.
|
|
477
489
|
|
|
478
490
|
Chaque connecteur ouvre une **connexion isolée** (`mongoose.createConnection`), pas le singleton
|
|
@@ -480,8 +492,8 @@ global de Mongoose : c'est ce qui permet à plusieurs bases — voire plusieurs
|
|
|
480
492
|
le même processus.
|
|
481
493
|
|
|
482
494
|
Le service orchestre ce cycle de bout en bout : il ouvre une connexion par connecteur déclaré au
|
|
483
|
-
démarrage (`MongooseService.connectAll()` (`MongooseService.ts:
|
|
484
|
-
(`MongooseService.disconnectAll()` (`MongooseService.ts:
|
|
495
|
+
démarrage (`MongooseService.connectAll()` (`MongooseService.ts:104`)) et referme tout à l'arrêt
|
|
496
|
+
(`MongooseService.disconnectAll()` (`MongooseService.ts:210`)). Le module se déclare **non critique**
|
|
485
497
|
(`Mongoose.critical` (`mongoose/index.ts:48`)) : une base injoignable ne tue pas le processus —
|
|
486
498
|
l'application monte quand même, l'échec est journalisé, et c'est l'orchestrateur qui relèvera Mongo.
|
|
487
499
|
|
|
@@ -490,15 +502,15 @@ l'application monte quand même, l'échec est journalisé, et c'est l'orchestrat
|
|
|
490
502
|
> indexées par `(connecteur, nom)` dans un registre **global au processus**. Si Drizzle (dont le
|
|
491
503
|
> connecteur par défaut est `default`) et Mongoose tournaient ensemble avec le même nom, leurs deux
|
|
492
504
|
> entités `session` entreraient en collision. Un nom distinct par driver règle le problème par
|
|
493
|
-
> construction (`FRAMEWORK_CONNECTOR` (`registerStores.ts:
|
|
505
|
+
> construction (`FRAMEWORK_CONNECTOR` (`registerStores.ts:77`)).
|
|
494
506
|
|
|
495
507
|
### Le trajet d'une requête
|
|
496
508
|
|
|
497
509
|
`repo.find({ views: { $gte: 10 } }, { limit: 20 })` traverse quatre étapes :
|
|
498
510
|
|
|
499
|
-
1. **Traduction du critère** (`MongooseRepository.#filter()` (`MongooseRepository.ts:
|
|
511
|
+
1. **Traduction du critère** (`MongooseRepository.#filter()` (`MongooseRepository.ts:256`)) : chaque
|
|
500
512
|
champ est résolu (`id` devient `_id`), chaque opérateur portable est converti.
|
|
501
|
-
2. **Validation du champ** (`MongooseRepository.#resolveField()` (`MongooseRepository.ts:
|
|
513
|
+
2. **Validation du champ** (`MongooseRepository.#resolveField()` (`MongooseRepository.ts:222`)) : un
|
|
502
514
|
champ absent du schéma lève `UnknownCriteriaField` — plutôt que de renvoyer zéro résultat sans
|
|
503
515
|
rien dire, ce qui est la pire façon d'échouer.
|
|
504
516
|
3. **Exécution** : la requête Mongoose est construite (session transactionnelle, `populate`, `skip`,
|
|
@@ -506,7 +518,7 @@ l'application monte quand même, l'échec est journalisé, et c'est l'orchestrat
|
|
|
506
518
|
4. **Sérialisation** : chaque document sort en objet plat, **virtuels compris** — c'est là que `id`
|
|
507
519
|
apparaît.
|
|
508
520
|
|
|
509
|
-
Une sonde facultative encadre l'opération (`MongooseRepository.#prof()` (`MongooseRepository.ts:
|
|
521
|
+
Une sonde facultative encadre l'opération (`MongooseRepository.#prof()` (`MongooseRepository.ts:118`)) :
|
|
510
522
|
voir [Performance et mémoire](#-performance-et-mémoire).
|
|
511
523
|
|
|
512
524
|
## 🧰 Le repository portable — l'API que tu utilises vraiment
|
|
@@ -527,26 +539,28 @@ drivers. Les signatures exactes vivent dans le graphe généré
|
|
|
527
539
|
| `delete` / `deleteOne` | supprimer | `deleteMany` / `deleteOne` |
|
|
528
540
|
| `findOneAndDelete` | supprimer **et** récupérer le document | `findOneAndDelete` |
|
|
529
541
|
| `count` / `exists` | compter / tester l'existence | `countDocuments` / `exists` |
|
|
542
|
+
| `countDistinct` | compter les valeurs distinctes et non nulles d'un champ | agrégation `$match` → `$group` → `$count` |
|
|
530
543
|
| `withTransaction` | rejouer les mêmes opérations dans une transaction | ajoute la `session` à chaque opération |
|
|
531
544
|
|
|
532
545
|
Les écritures qui « lisent puis écrivent » sont **atomiques par construction**
|
|
533
|
-
(`MongooseRepository.upsert()` (`MongooseRepository.ts:
|
|
534
|
-
`MongooseRepository.increment()` (`MongooseRepository.ts:
|
|
546
|
+
(`MongooseRepository.upsert()` (`MongooseRepository.ts:422`),
|
|
547
|
+
`MongooseRepository.increment()` (`MongooseRepository.ts:507`)) : un seul aller-retour, la
|
|
535
548
|
comparaison est faite par le serveur. Ce n'est pas une optimisation cosmétique — c'est ce qui évite
|
|
536
549
|
que deux requêtes simultanées lisent le même état et s'écrasent mutuellement.
|
|
537
550
|
|
|
538
551
|
### Les critères et leurs opérateurs
|
|
539
552
|
|
|
540
553
|
Les opérateurs portables sont ceux d'[`orm-core`](../../orm-core/docs/index.md), et la plupart sont
|
|
541
|
-
natifs en Mongo. Deux méritent une explication (`MongooseRepository.#mongoOps()` (`MongooseRepository.ts:
|
|
554
|
+
natifs en Mongo. Deux méritent une explication (`MongooseRepository.#mongoOps()` (`MongooseRepository.ts:173`)) :
|
|
542
555
|
|
|
543
|
-
| Opérateur portable
|
|
544
|
-
|
|
|
545
|
-
| `$eq $
|
|
546
|
-
| `$
|
|
547
|
-
| `$
|
|
548
|
-
| `$
|
|
549
|
-
| `$
|
|
556
|
+
| Opérateur portable | Côté Mongo | Remarque |
|
|
557
|
+
| -------------------------- | ------------------------- | ------------------------------------------------------------------- |
|
|
558
|
+
| `$eq $gt $gte $lt $lte` | identiques | natifs |
|
|
559
|
+
| `$ne` / `$nin` | `$nin` + `null` | comme en SQL, jamais un champ absent (un `$nin` vide reste neutre) |
|
|
560
|
+
| `$in` | identique | natif |
|
|
561
|
+
| `$like: "ab%"` | `$regex` **ancrée** | motif SQL traduit (`sqlLikeToRegex()` (`MongooseRepository.ts:25`)) |
|
|
562
|
+
| `$null: true` / `false` | `$eq: null` / `$ne: null` | en Mongo, `null` couvre aussi le champ **absent** |
|
|
563
|
+
| `$max` / `$min` (écriture) | `$max` / `$min` natifs | l'équivalent du `GREATEST(col, ?)` SQL |
|
|
550
564
|
|
|
551
565
|
```typescript
|
|
552
566
|
await articles.find({ tags: "nodefony" }); // tableau : appartenance native
|
|
@@ -575,6 +589,36 @@ Le chargement se demande à la lecture : `find(criteria, { relations: ["comments
|
|
|
575
589
|
`populate`. Le refus du `many-to-many` est volontaire : il n'a pas de traduction unique en Mongo
|
|
576
590
|
(tableau de références ? collection de liaison ?), et un choix imposé serait un mauvais choix.
|
|
577
591
|
|
|
592
|
+
**L'adapter tient la politique d'effacement du SQL généré.** Au `connect()`, il relève tout
|
|
593
|
+
champ `ObjectId` qui porte un `ref` vers une entité de la même connexion, et chaque suppression
|
|
594
|
+
d'un parent (`delete`, `deleteOne`, `findOneAndDelete`) la consulte :
|
|
595
|
+
|
|
596
|
+
| La référence est… | Supprimer le parent… |
|
|
597
|
+
| ------------------------------ | -------------------------------------------------------------------------- |
|
|
598
|
+
| obligatoire (`required: true`) | est **refusé** : `ReferencedEntityError`, que le rendu HTTP traduit en 409 |
|
|
599
|
+
| facultative | passe, et la référence de l'enfant est **remise à `null`** |
|
|
600
|
+
|
|
601
|
+
Une entité que personne ne référence ne paie rien. Une **auto-référence** (`Category.parent`) ne
|
|
602
|
+
retient pas un parent dont l'enfant part dans le même appel : l'arbre entier se supprime d'un coup.
|
|
603
|
+
Une référence **sans index** est signalée au `connect()` (`WARNING`, champ nommé) : sans lui,
|
|
604
|
+
chaque suppression du parent parcourt toute la collection de l'enfant. Quatre limites, à connaître :
|
|
605
|
+
|
|
606
|
+
- **Hors transaction, contrôle et suppression sont deux opérations** : un enfant créé entre les
|
|
607
|
+
deux n'est pas vu. Le SQL, lui, est atomique ; dans `withTransaction`, les deux partagent la
|
|
608
|
+
session.
|
|
609
|
+
- **Un tableau de références** (`[{ type: ObjectId, ref }]`) n'est pas gardé : retirer l'élément
|
|
610
|
+
n'a pas d'équivalent dans `restrict` / `set null`.
|
|
611
|
+
- **La suppression douce** (`deletedAt`) est une mise à jour : elle ne déclenche rien — en SQL
|
|
612
|
+
non plus.
|
|
613
|
+
- **La connexion native** (`getNativeConnection()`) contourne la garde, comme toute requête brute.
|
|
614
|
+
Et une suppression en masse charge tous les `_id` visés : c'est ce qui permet à un seul parent
|
|
615
|
+
retenu de bloquer le lot entier.
|
|
616
|
+
|
|
617
|
+
> [!WARNING]
|
|
618
|
+
> L'**insertion** n'est pas gardée : un enfant qui désigne un parent inexistant est accepté, et
|
|
619
|
+
> `populate` rend alors `null` à sa place. Le contrat d'entrée vérifie la forme de l'identifiant,
|
|
620
|
+
> pas l'existence du parent.
|
|
621
|
+
|
|
578
622
|
### Transactions
|
|
579
623
|
|
|
580
624
|
```typescript
|
|
@@ -586,22 +630,23 @@ await orm.transaction(async (tx) => {
|
|
|
586
630
|
}); // commit si la fonction résout, annulation si elle échoue
|
|
587
631
|
```
|
|
588
632
|
|
|
589
|
-
`MongooseOrm.transaction()` (`MongooseOrm.ts:
|
|
633
|
+
`MongooseOrm.transaction()` (`MongooseOrm.ts:571`) s'appuie sur les sessions Mongo « managées »
|
|
590
634
|
(commit, annulation et **reprises** gérées par le driver).
|
|
591
635
|
|
|
592
636
|
> [!IMPORTANT]
|
|
593
637
|
> **Les transactions exigent un replica set.** Un serveur MongoDB isolé (`mongod` seul, l'installation
|
|
594
638
|
> par défaut) ne les supporte pas. En développement, démarre un replica set à un nœud ; en production,
|
|
595
639
|
> Atlas et la plupart des services managés en fournissent un d'office. Les points de sauvegarde
|
|
596
|
-
> intermédiaires
|
|
640
|
+
> intermédiaires n'existent pas en Mongo : `savepoint()` et `rollbackTo()` lèvent
|
|
641
|
+
> `SavepointNotSupportedError` — pour annuler, c'est la transaction entière qu'on abandonne.
|
|
597
642
|
|
|
598
643
|
### La trappe native — quand le contrat ne suffit plus
|
|
599
644
|
|
|
600
|
-
`MongooseOrm.getNativeConnection()` (`MongooseOrm.ts:
|
|
645
|
+
`MongooseOrm.getNativeConnection()` (`MongooseOrm.ts:589`) rend la connexion Mongoose telle quelle :
|
|
601
646
|
agrégations, `$or`, index, `$text`, `bulkWrite`, changements de flux. Le module lui-même s'en sert
|
|
602
647
|
là où le contrat portable ne suffit pas — par exemple pour la recherche texte du listing des
|
|
603
648
|
webhooks, qui a besoin d'un `$or` sur deux champs
|
|
604
|
-
(`MongooseWebhookStore.#listFilter()` (`MongooseWebhookStore.ts:
|
|
649
|
+
(`MongooseWebhookStore.#listFilter()` (`MongooseWebhookStore.ts:195`)).
|
|
605
650
|
|
|
606
651
|
C'est un **anti-blocage assumé** : le contrat portable couvre le quotidien, la trappe couvre le reste.
|
|
607
652
|
Le code qui l'emprunte cesse d'être portable, et ça se voit — ce qui est exactement le but.
|
|
@@ -613,13 +658,16 @@ contrat est importé en `import type` (effacé à la compilation), et c'est le d
|
|
|
613
658
|
auprès du module consommateur. Le module met en place tout ce câblage à son enregistrement, avant que
|
|
614
659
|
la connexion ne s'ouvre.
|
|
615
660
|
|
|
616
|
-
| Store | Contrat
|
|
617
|
-
| ------------ |
|
|
618
|
-
| Sessions | `ISessionStorage` (http)
|
|
619
|
-
| Utilisateurs | `IUserRepository` (user)
|
|
620
|
-
| Jetons | `ITokenStore` (security)
|
|
621
|
-
| Passkeys | `IWebAuthnCredentialStore`
|
|
622
|
-
| Webhooks | `IWebhookStore`
|
|
661
|
+
| Store | Contrat | Comment le choisir | Collections |
|
|
662
|
+
| ------------ | ----------------------------- | ------------------------------------------------ | -------------------------------------------------- |
|
|
663
|
+
| Sessions | `ISessionStorage` (http) | `session: { store: "mongoose" }` | `session` |
|
|
664
|
+
| Utilisateurs | `IUserRepository` (user) | via `provisionUsers` de ton app | `User` |
|
|
665
|
+
| Jetons | `ITokenStore` (security) | `tokenStore: { store: "mongoose" }` | `access_token`, `denied_jti`, `subject_revocation` |
|
|
666
|
+
| Passkeys | `IWebAuthnCredentialStore` | `passkeys: { store: "mongoose" }` | `webauthn_credential` |
|
|
667
|
+
| Webhooks | `IWebhookStore` | `webhooks: { store: "mongoose" }` | `webhook_endpoint` |
|
|
668
|
+
| 2FA (TOTP) | `ITotpSecretStore` (security) | `totp: { store: "mongoose" }` | `totp_secret` |
|
|
669
|
+
| Audit | `IAuditStore` (security) | `audit: { store: "mongoose" }` | `audit_event` |
|
|
670
|
+
| Idempotence | `IIdempotencyStore` (cœur) | `idempotency: { store: "mongoose" }` (framework) | `idempotency_key` |
|
|
623
671
|
|
|
624
672
|
L'auto-enregistrement peut être coupé (`frameworkEntities: false` (`config.ts:102`)) : le module
|
|
625
673
|
devient alors un pur driver de données, sans schéma framework.
|
|
@@ -632,18 +680,18 @@ devient alors un pur driver de données, sans schéma framework.
|
|
|
632
680
|
|
|
633
681
|
Trois comportements valent d'être connus :
|
|
634
682
|
|
|
635
|
-
- **Purge à deux bornes** — `idleTimeoutS` et `absoluteTimeoutS` (`SessionStorage.gc()` (`SessionStorage.ts:
|
|
683
|
+
- **Purge à deux bornes** — `idleTimeoutS` et `absoluteTimeoutS` (`SessionStorage.gc()` (`SessionStorage.ts:163`)) :
|
|
636
684
|
l'inactivité (depuis la dernière activité) et l'âge absolu (depuis la création, **jamais prolongé** —
|
|
637
685
|
la ré-authentification finit par être imposée, conformément aux recommandations NIST/OWASP).
|
|
638
|
-
- **Prolongation sans réécriture** (`SessionStorage.touch()` (`SessionStorage.ts:
|
|
686
|
+
- **Prolongation sans réécriture** (`SessionStorage.touch()` (`SessionStorage.ts:195`)) : rafraîchir
|
|
639
687
|
l'activité ne réécrit pas le contenu de la session, juste son horodatage.
|
|
640
|
-
- **Écran d'administration redacté par construction** (`SessionStorage.listPage()` (`SessionStorage.ts:
|
|
688
|
+
- **Écran d'administration redacté par construction** (`SessionStorage.listPage()` (`SessionStorage.ts:286`)) :
|
|
641
689
|
le contenu applicatif et les messages flash **ne sortent pas de la base**. Studio affiche qui est
|
|
642
690
|
connecté, jamais ce qu'il y a dans sa session.
|
|
643
691
|
|
|
644
692
|
Quand l'ORM n'est plus connecté — typiquement pendant l'arrêt du serveur, alors que des requêtes sont
|
|
645
693
|
encore en vol — le store dégrade **gracieusement** au lieu de lever une exception
|
|
646
|
-
(`SessionStorage.#repo()` (`SessionStorage.ts:
|
|
694
|
+
(`SessionStorage.#repo()` (`SessionStorage.ts:66`)). Une session non persistée le temps de l'arrêt
|
|
647
695
|
vaut mieux qu'une erreur 500 et un rejet non capturé.
|
|
648
696
|
|
|
649
697
|
### Utilisateurs
|
|
@@ -651,15 +699,15 @@ vaut mieux qu'une erreur 500 et un rejet non capturé.
|
|
|
651
699
|
`MongooseUserRepository` rend des objets `BaseUser` (avec leur comportement : rôles, actif, verrouillé),
|
|
652
700
|
pas des documents nus. Deux recherches lui sont propres :
|
|
653
701
|
|
|
654
|
-
- **par compte social lié** (`MongooseUserRepository.findBySocialProvider()` (`MongooseUserRepository.ts:
|
|
702
|
+
- **par compte social lié** (`MongooseUserRepository.findBySocialProvider()` (`MongooseUserRepository.ts:238`)) :
|
|
655
703
|
un `$elemMatch` sur un tableau libre de fournisseurs — le pendant Mongo du parcours JSON en SQL.
|
|
656
704
|
C'est ce qui porte le motif « Shadow User » d'OAuth (un compte créé à la volée au premier login social),
|
|
657
705
|
**sans colonne par fournisseur** : ajouter GitHub demain n'est pas une migration.
|
|
658
|
-
- **listing paginé** (`MongooseUserRepository.listPage()` (`MongooseUserRepository.ts:
|
|
706
|
+
- **listing paginé** (`MongooseUserRepository.listPage()` (`MongooseUserRepository.ts:281`)) : requête
|
|
659
707
|
native bornée (`skip`/`limit + 1`), tri sur liste blanche, `_id` en départage. Une page est une page,
|
|
660
708
|
jamais la collection entière rapatriée en mémoire.
|
|
661
709
|
|
|
662
|
-
Le comptage des administrateurs actifs (`MongooseUserRepository.countActiveAdmins()` (`MongooseUserRepository.ts:
|
|
710
|
+
Le comptage des administrateurs actifs (`MongooseUserRepository.countActiveAdmins()` (`MongooseUserRepository.ts:328`))
|
|
663
711
|
compte côté serveur — c'est le garde-fou qui empêche de supprimer le dernier administrateur.
|
|
664
712
|
|
|
665
713
|
### Jetons
|
|
@@ -667,25 +715,25 @@ compte côté serveur — c'est le garde-fou qui empêche de supprimer le dernie
|
|
|
667
715
|
Trois collections : les jetons eux-mêmes, la denylist de `jti`, les seuils de révocation par porteur.
|
|
668
716
|
Deux invariants de sécurité sont tenus **par la requête**, pas par du code JavaScript entre deux appels :
|
|
669
717
|
|
|
670
|
-
- **Révocation idempotente** (`MongooseTokenStore.revoke()` (`MongooseTokenStore.ts:
|
|
718
|
+
- **Révocation idempotente** (`MongooseTokenStore.revoke()` (`MongooseTokenStore.ts:275`)) : la
|
|
671
719
|
condition « pas encore révoqué » est dans le filtre. Deux révocations simultanées ne se recouvrent
|
|
672
720
|
pas ; la première date et la première raison sont conservées.
|
|
673
|
-
- **Seuil monotone** (`MongooseTokenStore.revokeAllForSubject()` (`MongooseTokenStore.ts:
|
|
721
|
+
- **Seuil monotone** (`MongooseTokenStore.revokeAllForSubject()` (`MongooseTokenStore.ts:334`)) : le
|
|
674
722
|
« déconnecte-moi de partout » utilise `$max`. Avec une lecture suivie d'une écriture, deux
|
|
675
723
|
déconnexions simultanées pourraient reposer un seuil **plus ancien** — et des jetons révoqués
|
|
676
724
|
redeviendraient valides. Ici, c'est structurellement impossible.
|
|
677
725
|
|
|
678
|
-
La purge, bornée par `retentionRevokedMs` (`MongooseTokenStore.gc()` (`MongooseTokenStore.ts:
|
|
726
|
+
La purge, bornée par `retentionRevokedMs` (`MongooseTokenStore.gc()` (`MongooseTokenStore.ts:356`)), s'appuie sur une particularité utile
|
|
679
727
|
de Mongo : une comparaison numérique **ignore** les documents dont le champ est `null`. Les jetons sans
|
|
680
728
|
expiration ne sont donc jamais balayés par erreur ; ils partent par une règle de rétention distincte.
|
|
681
729
|
|
|
682
730
|
### Passkeys
|
|
683
731
|
|
|
684
732
|
Une collection, l'identifiant du credential en clé primaire. L'enregistrement passe par un `upsert`
|
|
685
|
-
atomique (`MongooseWebAuthnCredentialStore.save()` (`MongooseWebAuthnCredentialStore.ts:
|
|
733
|
+
atomique (`MongooseWebAuthnCredentialStore.save()` (`MongooseWebAuthnCredentialStore.ts:109`)) : deux
|
|
686
734
|
enregistrements concurrents de la même passkey ne peuvent pas produire de collision de clé.
|
|
687
735
|
|
|
688
|
-
Le listing d'administration (`MongooseWebAuthnCredentialStore.listPage()` (`MongooseWebAuthnCredentialStore.ts:
|
|
736
|
+
Le listing d'administration (`MongooseWebAuthnCredentialStore.listPage()` (`MongooseWebAuthnCredentialStore.ts:173`))
|
|
689
737
|
projette **sans la clé publique** — elle ne franchit jamais la frontière du store. La recherche libre
|
|
690
738
|
est un **préfixe ancré**, pas une expression régulière fournie par l'appelant : une recherche
|
|
691
739
|
utilisateur n'est jamais interprétée comme du code.
|
|
@@ -693,18 +741,18 @@ utilisateur n'est jamais interprétée comme du code.
|
|
|
693
741
|
### Webhooks
|
|
694
742
|
|
|
695
743
|
Registre **durable** des destinations à notifier, par opposition au store mémoire qui disparaît au
|
|
696
|
-
redémarrage. Le listing paginé (`MongooseWebhookStore.listPage()` (`MongooseWebhookStore.ts:
|
|
744
|
+
redémarrage. Le listing paginé (`MongooseWebhookStore.listPage()` (`MongooseWebhookStore.ts:227`))
|
|
697
745
|
lit `limit + 1` documents pour savoir s'il existe une page suivante — sans second comptage — et
|
|
698
746
|
échappe les métacaractères de la recherche texte.
|
|
699
747
|
|
|
700
748
|
Détail révélateur de la doctrine du framework : si le store est construit sans modèle natif, le
|
|
701
|
-
listing paginé **refuse** de répondre (`MongooseWebhookStore.#nativeModel()` (`MongooseWebhookStore.ts:
|
|
749
|
+
listing paginé **refuse** de répondre (`MongooseWebhookStore.#nativeModel()` (`MongooseWebhookStore.ts:179`))
|
|
702
750
|
au lieu de retomber sur un chargement complet de la collection. Une garantie silencieusement trahie
|
|
703
751
|
serait pire qu'une erreur.
|
|
704
752
|
|
|
705
753
|
## 🗃 Ce qui est stocké
|
|
706
754
|
|
|
707
|
-
Les
|
|
755
|
+
Les schémas portés par le module — dix collections. Les collections sont créées à la volée par MongoDB — il n'y a
|
|
708
756
|
ni migration ni DDL à jouer, ce qui est l'un des vrais conforts du modèle documentaire.
|
|
709
757
|
|
|
710
758
|
> ⚠️ **Le revers de ce confort : ce qui n'est pas déclaré est JETÉ, sans un mot.** Mongoose valide en
|
|
@@ -719,15 +767,18 @@ ni migration ni DDL à jouer, ce qui est l'un des vrais conforts du modèle docu
|
|
|
719
767
|
> partagé (`USER_COLUMNS`), et le module en dérive un schéma Mongoose. Un champ métier que tu ajoutes
|
|
720
768
|
> à ton utilisateur suit le même chemin — déclaré, donc écrit ; oublié, donc perdu en silence.
|
|
721
769
|
|
|
722
|
-
| Collection | Clé primaire (`_id`)
|
|
723
|
-
| --------------------- |
|
|
724
|
-
| `session` | `ObjectId` (auto)
|
|
725
|
-
| `User` | `ObjectId` (auto)
|
|
726
|
-
| `access_token` | le `jti` (texte)
|
|
727
|
-
| `denied_jti` | le `jti` (texte)
|
|
728
|
-
| `subject_revocation` | le porteur (texte)
|
|
729
|
-
| `webauthn_credential` | l'identifiant du credential
|
|
730
|
-
| `webhook_endpoint` | l'identifiant `wh_…` (texte)
|
|
770
|
+
| Collection | Clé primaire (`_id`) | Contenu | Horodatages |
|
|
771
|
+
| --------------------- | -------------------------------------- | ------------------------------------------------------------------------------------ | ------------------ |
|
|
772
|
+
| `session` | `ObjectId` (auto) | identifiant de session, contenu, messages flash, méta, utilisateur | nombres (ms) |
|
|
773
|
+
| `User` | `ObjectId` (auto) | identifiant, mot de passe haché, rôles, comptes sociaux, méta | gérés par Mongoose |
|
|
774
|
+
| `access_token` | le `jti` (texte) | type, porteur, périmètres, empreinte du secret, révocation | nombres (ms) |
|
|
775
|
+
| `denied_jti` | le `jti` (texte) | expiration | nombres (ms) |
|
|
776
|
+
| `subject_revocation` | le porteur (texte) | seuil `invalidBefore` | nombres (ms) |
|
|
777
|
+
| `webauthn_credential` | l'identifiant du credential | clé publique, compteur, transports, état de sauvegarde | nombres (ms) |
|
|
778
|
+
| `webhook_endpoint` | l'identifiant `wh_…` (texte) | URL, secret chiffré, événements, état des livraisons | nombres (ms) |
|
|
779
|
+
| `totp_secret` | l'identifiant de l'utilisateur (texte) | secret chiffré, algorithme, chiffres, période, codes de secours, dernier pas utilisé | nombres (ms) |
|
|
780
|
+
| `audit_event` | l'identifiant d'événement (texte) | catégorie, action, issue, acteur, ressource, raison, IP, requête | nombre (`ts`, ms) |
|
|
781
|
+
| `idempotency_key` | la clé d'idempotence (texte) | empreinte de la requête, état, réponse rejouée | expiration (ms) |
|
|
731
782
|
|
|
732
783
|
Deux choix structurants s'y lisent :
|
|
733
784
|
|
|
@@ -740,7 +791,7 @@ natif.
|
|
|
740
791
|
**Les horodatages sont des nombres, pas des dates.** Les contrats du framework portent des `number`
|
|
741
792
|
(millisecondes depuis l'époque) : les stocker tels quels garde la logique de purge **strictement
|
|
742
793
|
identique** à celle de l'adapter SQL. Seule l'entité `User` utilise la gestion automatique de Mongoose
|
|
743
|
-
(`createUserEntity()` (`userEntity.ts:
|
|
794
|
+
(`createUserEntity()` (`userEntity.ts:104`)), parce que son contrat porte des dates.
|
|
744
795
|
|
|
745
796
|
Le contrat expose partout `id: string`, jamais un `ObjectId` : le champ virtuel `id` est activé à la
|
|
746
797
|
sérialisation, sur toutes les entités compilées par l'adapter.
|
|
@@ -757,7 +808,7 @@ heures plus tard.
|
|
|
757
808
|
| `debug` | trace toutes les opérations Mongoose (développement) | `false` |
|
|
758
809
|
| `frameworkEntities` | déclare le schéma framework et rend ses stores sélectionnables | `true` |
|
|
759
810
|
|
|
760
|
-
Les valeurs font foi dans le schéma (`mongooseConfigSchema` (`config.ts:
|
|
811
|
+
Les valeurs font foi dans le schéma (`mongooseConfigSchema` (`config.ts:98`)) ; la surcharge par
|
|
761
812
|
l'environnement est appliquée **après** la validation
|
|
762
813
|
(`applyEnvOverrides()` (`defineModuleConfig.ts:22`)), ce qui garde le schéma pur et publiable en
|
|
763
814
|
JSON Schema pour Studio.
|
|
@@ -785,15 +836,15 @@ Schema). Le module fournit les sondes correspondantes :
|
|
|
785
836
|
|
|
786
837
|
| Sonde | Ce qu'elle renvoie |
|
|
787
838
|
| ---------------------- | --------------------------------------------------------------------------- |
|
|
788
|
-
| `ping()` | un aller-retour réel vers la base (`MongooseOrm.ts:
|
|
789
|
-
| `probe()` | les connexions du serveur et sa version (`MongooseOrm.ts:
|
|
790
|
-
| `describeEntity()` | les champs d'une entité, depuis le schéma compilé (`MongooseOrm.ts:
|
|
791
|
-
| `describeConnection()` | le pilote, la cible et la version de la bibliothèque (`MongooseOrm.ts:
|
|
839
|
+
| `ping()` | un aller-retour réel vers la base (`MongooseOrm.ts:603`) |
|
|
840
|
+
| `probe()` | les connexions du serveur et sa version (`MongooseOrm.ts:617`) |
|
|
841
|
+
| `describeEntity()` | les champs d'une entité, depuis le schéma compilé (`MongooseOrm.ts:649`) |
|
|
842
|
+
| `describeConnection()` | le pilote, la cible et la version de la bibliothèque (`MongooseOrm.ts:676`) |
|
|
792
843
|
|
|
793
844
|
> [!IMPORTANT]
|
|
794
845
|
> **Aucun identifiant ne sort jamais.** La cible affichée est nettoyée de tout `utilisateur:mot de
|
|
795
846
|
passe@` avant d'atteindre le plan d'administration ou les journaux
|
|
796
|
-
> (`MongooseOrm.safeTarget()` (`MongooseOrm.ts:
|
|
847
|
+
> (`MongooseOrm.safeTarget()` (`MongooseOrm.ts:688`)), y compris pour les URI multi-hôtes que
|
|
797
848
|
> l'analyseur d'URL standard ne sait pas découper.
|
|
798
849
|
|
|
799
850
|
## ⚡ Performance et mémoire
|
|
@@ -803,10 +854,10 @@ Le module suit la règle de fond du framework : **ce qui n'est pas observé ne c
|
|
|
803
854
|
- **Instrumentation à coût nul hors observation.** Chaque opération peut alimenter deux sondes (le
|
|
804
855
|
profil par requête de la barre de debug, et le flux agrégé). Les deux drapeaux sont lus **avant**
|
|
805
856
|
toute allocation, et la description de la requête n'est construite que si l'on regarde
|
|
806
|
-
(`MongooseRepository.#prof()` (`MongooseRepository.ts:
|
|
857
|
+
(`MongooseRepository.#prof()` (`MongooseRepository.ts:118`)). En production, le chemin est celui d'un
|
|
807
858
|
appel direct.
|
|
808
859
|
- **Repositories alloués à la demande.** Le cache est créé au premier accès, pas à la connexion
|
|
809
|
-
(`MongooseOrm.getRepository()` (`MongooseOrm.ts:
|
|
860
|
+
(`MongooseOrm.getRepository()` (`MongooseOrm.ts:548`)).
|
|
810
861
|
- **Un aller-retour par écriture.** Les opérations « lire puis écrire » sont exprimées en une seule
|
|
811
862
|
requête atomique — moins de latence _et_ pas de course.
|
|
812
863
|
- **Le comptage reste côté serveur.** Les listings paginés lisent `limit + 1` documents pour savoir
|
|
@@ -827,7 +878,7 @@ Le module suit la règle de fond du framework : **ce qui n'est pas observé ne c
|
|
|
827
878
|
| `many-to-many non portable` | Refus explicite : pas de traduction unique en Mongo | Déclarer la relation via la connexion native |
|
|
828
879
|
| Les sessions disparaissent au redémarrage | `session.store` resté sur `memory` | `session: { store: "mongoose" }`, ou déclarer `NF_DATABASE_URL` et laisser `auto` |
|
|
829
880
|
| `audit store "mongoose" inconnu` — boot avorté en production | Brique **non portée** par Mongo, sélectionnée explicitement | Laisser `auto` (repli annoncé) ou choisir un backend qui la porte |
|
|
830
|
-
| Les comptes ne survivent pas au redémarrage | `provisionUsers` toujours branché sur l'annuaire mémoire | Câbler `MongooseUserRepository.from(orm)` (`MongooseUserRepository.ts:
|
|
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`) |
|
|
831
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 |
|
|
832
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 |
|
|
833
884
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodefony/mongoose",
|
|
3
|
-
"version": "10.0.0-
|
|
3
|
+
"version": "10.0.0-beta.1",
|
|
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",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
"user",
|
|
10
10
|
"tokens",
|
|
11
11
|
"passkeys",
|
|
12
|
-
"
|
|
12
|
+
"totp",
|
|
13
|
+
"audit",
|
|
14
|
+
"webhooks",
|
|
15
|
+
"idempotency"
|
|
13
16
|
]
|
|
14
17
|
},
|
|
15
18
|
"contributors": [],
|
|
@@ -25,9 +28,9 @@
|
|
|
25
28
|
},
|
|
26
29
|
"scripts": {
|
|
27
30
|
"start": "node dist/index.js",
|
|
28
|
-
"build": "
|
|
31
|
+
"build": "rolldown -c rolldown.config.ts && tsgo -p tsconfig.declarations.json",
|
|
29
32
|
"dev": "rolldown -c rolldown.config.ts --watch",
|
|
30
|
-
"clean": "
|
|
33
|
+
"clean": "node --eval \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
31
34
|
"test": "vitest run",
|
|
32
35
|
"coverage": "vitest run --coverage",
|
|
33
36
|
"typecheck": "tsgo --noEmit -p tsconfig.json && tsgo --noEmit -p tsconfig.tests.json"
|
|
@@ -47,27 +50,28 @@
|
|
|
47
50
|
],
|
|
48
51
|
"dependencies": {
|
|
49
52
|
"mongodb": "7.6.0",
|
|
50
|
-
"mongoose": "9.10.
|
|
53
|
+
"mongoose": "9.10.2",
|
|
51
54
|
"tslib": "2.8.1",
|
|
52
55
|
"zod": "^4.6.5"
|
|
53
56
|
},
|
|
54
57
|
"devDependencies": {
|
|
55
|
-
"@nodefony/
|
|
56
|
-
"@nodefony/
|
|
57
|
-
"@nodefony/
|
|
58
|
-
"@nodefony/
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"vitest": "5.0.
|
|
58
|
+
"@nodefony/framework": "^10.0.0-beta.1",
|
|
59
|
+
"@nodefony/http": "^10.0.0-beta.1",
|
|
60
|
+
"@nodefony/orm-core": "^10.0.0-beta.1",
|
|
61
|
+
"@nodefony/security": "^10.0.0-beta.1",
|
|
62
|
+
"@nodefony/user": "^10.0.0-beta.1",
|
|
63
|
+
"@types/node": "26.6.3",
|
|
64
|
+
"mongodb-memory-server": "11.3.0",
|
|
65
|
+
"nodefony": "^10.0.0-beta.1",
|
|
66
|
+
"vitest": "5.0.2"
|
|
64
67
|
},
|
|
65
68
|
"peerDependencies": {
|
|
66
|
-
"@nodefony/
|
|
67
|
-
"@nodefony/
|
|
68
|
-
"@nodefony/
|
|
69
|
-
"@nodefony/
|
|
70
|
-
"nodefony": "^10.0.0-
|
|
69
|
+
"@nodefony/framework": "^10.0.0-beta.1",
|
|
70
|
+
"@nodefony/http": "^10.0.0-beta.1",
|
|
71
|
+
"@nodefony/orm-core": "^10.0.0-beta.1",
|
|
72
|
+
"@nodefony/security": "^10.0.0-beta.1",
|
|
73
|
+
"@nodefony/user": "^10.0.0-beta.1",
|
|
74
|
+
"nodefony": "^10.0.0-beta.1"
|
|
71
75
|
},
|
|
72
76
|
"repository": {
|
|
73
77
|
"type": "git",
|