@nodefony/orm-core 10.0.0-beta.1 → 10.0.0-beta.3

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 CHANGED
@@ -5,8 +5,8 @@ plusieurs ORM dans le même processus — par exemple Drizzle pour SQL et Mongoo
5
5
  derrière une API portable, avec une trappe vers le driver natif pour les cas que l'abstraction ne
6
6
  couvre pas.
7
7
 
8
- > Bibliothèque pure : ce module n'expose pas de classe `Module` et n'est pas listé dans
9
- > `@modules()`. Ce sont les **drivers** (`@nodefony/drizzle`, `@nodefony/mongoose`…) qui sont des
8
+ > Bibliothèque pure : ce module n'expose pas de classe `Module` et n'est pas listé dans le
9
+ > manifeste `modules` de `nodefony.config.ts`. Ce sont les **drivers** (`@nodefony/drizzle`, `@nodefony/mongoose`…) qui sont des
10
10
  > modules Nodefony et qui s'enregistrent eux-mêmes dans l'`OrmRegistry`.
11
11
 
12
12
  ## Quel ORM par défaut ?
@@ -36,13 +36,13 @@ d'imposer plusieurs ORM simultanés.
36
36
 
37
37
  ## Contrats exposés
38
38
 
39
- | Interface | Rôle |
40
- | ---------------- | ------------------------------------------------------------------------- |
41
- | `IOrm` | Instance ORM (connexion logique) : `connect`, `repository`, `transaction` |
42
- | `IEntity<S,M>` | Entité enregistrée : nom logique, connecteur cible, schéma, modèle natif |
43
- | `IRepository<T>` | CRUD portable : `find`/`findOne`/`create`/`update`/`delete`/`count` |
44
- | `ITransaction` | Unité de travail : `commit`/`rollback`/`savepoint`/`rollbackTo` |
45
- | `Criteria<T>` | Filtre typé par champ et opérateurs riches ; `OrmCriteria` = échappatoire |
39
+ | Interface | Rôle |
40
+ | ---------------- | --------------------------------------------------------------------------------------------------------- |
41
+ | `IOrm` | Instance ORM (connexion logique) : `connect`, `getRepository`, `transaction` |
42
+ | `IEntity<S,M>` | Entité enregistrée : nom logique, connecteur cible, schéma, modèle natif |
43
+ | `IRepository<T>` | CRUD portable (15 verbes) : `find`/`findOne`/`create`/`updateOne`/`updateMany`/`upsert`/`delete`/`count`… |
44
+ | `ITransaction` | Unité de travail : `commit`/`rollback`/`savepoint`/`rollbackTo` |
45
+ | `Criteria<T>` | Filtre typé par champ et opérateurs riches ; `OrmCriteria` = échappatoire |
46
46
 
47
47
  ### Critères riches (opérateurs typés)
48
48
 
@@ -70,7 +70,7 @@ fenêtrage. C'est le garde-fou qui empêche l'abstraction de devenir un plafond.
70
70
  ## Exemple
71
71
 
72
72
  ```typescript
73
- import { OrmRegistry } from "@nodefony/orm-core";
73
+ import { ormRegistry } from "@nodefony/orm-core";
74
74
  import type { IRepository } from "@nodefony/orm-core";
75
75
 
76
76
  interface User {
@@ -78,14 +78,16 @@ interface User {
78
78
  email: string;
79
79
  }
80
80
 
81
- const orm = OrmRegistry.get("db_principale");
81
+ const orm = ormRegistry.get("db_principale");
82
82
  const users: IRepository<User> = orm.getRepository<User>("User");
83
83
 
84
84
  const created = await users.create({ email: "a@b.c" });
85
85
  const found = await users.findOne({ email: "a@b.c" });
86
86
 
87
87
  await orm.transaction(async (tx) => {
88
- await users.update({ id: created.id }, { email: "x@y.z" });
88
+ await users
89
+ .withTransaction(tx)
90
+ .updateOne({ id: created.id }, { email: "x@y.z" });
89
91
  // validation automatique si la fonction résout, annulation si elle rejette
90
92
  });
91
93
  ```
package/docs/index.md CHANGED
@@ -540,8 +540,8 @@ raisonnement, et le compilateur la refuse.
540
540
  JSON ou un sous-document (`{ meta: { auteur: "…" } }`) reste donc une égalité — c'est ce qui évite
541
541
  qu'une donnée métier soit prise pour une requête.
542
542
 
543
- **Ce que le critère ne couvre pas** : les `OR` logiques, les sous-requêtes, les agrégats, les
544
- jointures arbitraires. Ce n'est pas un oubli — c'est la limite du portable, et la sortie est
543
+ **Ce que le critère ne couvre pas** : les sous-requêtes, les agrégats, les jointures arbitraires
544
+ (la disjonction, elle, s'exprime par `$or: [...]`, portable). Ce n'est pas un oubli — c'est la limite du portable, et la sortie est
545
545
  `getNativeConnection()`. L'erreur `UnknownCriteriaField` (`errors.ts:23`) le dit d'ailleurs
546
546
  explicitement dans son message, avec la liste des champs connus de l'entité (diagnostic d'une faute
547
547
  de frappe).
@@ -610,8 +610,8 @@ d'administration : elle ne charge qu'une page, quelle que soit la taille de la t
610
610
 
611
611
  Deux drivers implémentent les contrats. Le contrat `IRepository` est tenu **en entier** par les
612
612
  deux : les quinze verbes existent des deux côtés — par exemple l'upsert, avec
613
- `DrizzleRepository.upsert()` (`DrizzleRepository.ts:866`) et `MongooseRepository.upsert()`
614
- (`MongooseRepository.ts:422`).
613
+ `DrizzleRepository.upsert()` (`DrizzleRepository.ts:1091`) et `MongooseRepository.upsert()`
614
+ (`MongooseRepository.ts:439`).
615
615
 
616
616
  | Capacité | `@nodefony/drizzle` | `@nodefony/mongoose` |
617
617
  | -------------------------------------- | ----------------------------------- | -------------------------------------------- |
@@ -620,8 +620,8 @@ deux : les quinze verbes existent des deux côtés — par exemple l'upsert, ave
620
620
  | Eager-load `{ relations }` | oui | oui (`populate`) |
621
621
  | Transactions | oui | oui (replica set requis par Mongo) |
622
622
  | Savepoints (rollback partiel) | oui | non — refusés (`SavepointNotSupportedError`) |
623
- | Colonnes pour l'ERD (`describeEntity`) | oui (`DrizzleOrm.ts:1971`) | oui (`MongooseOrm.ts:649`) |
624
- | Sonde de flux (requêtes/s, lentes) | oui — alimente `queryFlowMonitor` | non câblée |
623
+ | Colonnes pour l'ERD (`describeEntity`) | oui (`DrizzleOrm.ts:1982`) | oui (`MongooseOrm.ts:649`) |
624
+ | Sonde de flux (requêtes/s, lentes) | oui — alimente `queryFlowMonitor` | oui — alimente `queryFlowMonitor` |
625
625
  | Sonde profonde (`probe`) | oui (`DrizzleOrm.ts:1873`) | oui (`MongooseOrm.ts:617`) |
626
626
 
627
627
  **Les « stores » du framework** : chaque adapter déclare ce qu'il porte dans son `package.json`, clé
@@ -691,19 +691,22 @@ rendent sûr en production :
691
691
  observe, et le débit par seconde est **dérivé côté lecteur** (delta entre deux relevés), donc rien
692
692
  n'est muté à la lecture.
693
693
 
694
- Le data plane `/nodefony/orm/api/*` (`createOrmAdminApi()`, `OrmAdminApi.ts:541`) expose huit points
694
+ Le data plane `/nodefony/orm/api/*` (`createOrmAdminApi()`, `OrmAdminApi.ts:541`) expose onze points
695
695
  d'entrée, tous filtrables par `?connector=` :
696
696
 
697
- | Point d'entrée | Ce qu'il rend |
698
- | ------------------- | ----------------------------------------------------------------- |
699
- | `orms` | les connecteurs, leur état, leur nombre d'entités |
700
- | `entities` | le modèle complet : colonnes + relations |
701
- | `entity/{name}` | une entité (404 si inconnue) |
702
- | `graph` | le graphe canonique (`buildOrmGraph()`, `OrmAdminApi.ts:227`) |
703
- | `counts` | le nombre de lignes par entité — un `COUNT(*)` par table |
704
- | `connection/health` | état, latence, erreurs, reconnexions, sondes |
705
- | `flow` | débit et requêtes lentes (`buildOrmFlow()`, `OrmAdminApi.ts:360`) |
706
- | `export/{format}` | `dbml` (`toDbml()`, `OrmAdminApi.ts:395`) ou `jsonschema` |
697
+ | Point d'entrée | Ce qu'il rend |
698
+ | ------------------- | ------------------------------------------------------------------ |
699
+ | `orms` | les connecteurs, leur état, leur nombre d'entités |
700
+ | `entities` | le modèle complet : colonnes + relations |
701
+ | `entity/{name}` | une entité (404 si inconnue) |
702
+ | `graph` | le graphe canonique (`buildOrmGraph()`, `OrmAdminApi.ts:227`) |
703
+ | `counts` | le nombre de lignes par entité — un `COUNT(*)` par table |
704
+ | `connection/health` | état, latence, erreurs, reconnexions, sondes |
705
+ | `flow` | débit et requêtes lentes (`buildOrmFlow()`, `OrmAdminApi.ts:360`) |
706
+ | `export/{format}` | `dbml` (`toDbml()`, `OrmAdminApi.ts:395`) ou `jsonschema` |
707
+ | `migrations` | l'état des migrations (même objet que `orm:migrate:status --json`) |
708
+ | `migrations/plan` | le SQL en attente |
709
+ | `migrations/apply` | applique les migrations (développement seulement) |
707
710
 
708
711
  Ce graphe canonique est **la pièce maîtresse**, pas le diagramme : c'est une donnée sérialisable qui
709
712
  sert à la fois l'ERD de Studio, un export vers un outil tiers, et le contexte d'un agent IA
@@ -743,7 +746,7 @@ deux, et pas de course).
743
746
  | Un objet de critère est pris pour une égalité (colonne JSON) | comportement **voulu** : une valeur n'est un filtre que si **toutes** ses clés sont des opérateurs | c'est la protection ; pour filtrer dedans, passer au natif (`criteria.ts:42`) |
744
747
  | `onOrmReady` ne part plus après un ajout dans l'adapter | `connect()` a été surchargé | surcharger `onConnect()` (`Orm.ts:363`), jamais `connect()` |
745
748
  | Une entité déclarée dans un module reste invisible | le module embarque sa **propre copie** du registre (singleton dédoublé) | externaliser `@nodefony/orm-core` dans le `rolldown.config.ts` du module |
746
- | Rien dans `flow` alors que la base travaille | la sonde est éteinte hors développement, ou le driver n'a pas de tap | `NF_ORM_FLOW=1` (`ormWiring.ts:96`) ; le tap n'est câblé que côté Drizzle |
749
+ | Rien dans `flow` alors que la base travaille | la sonde est éteinte hors développement, ou le driver n'a pas de tap | `NF_ORM_FLOW=1` (`ormWiring.ts:96`) ; les deux drivers officiels ont un tap |
747
750
 
748
751
  ## 🧪 Tests & couverture
749
752
 
@@ -769,7 +772,7 @@ suite rejouée par dialecte :
769
772
  > **Un compteur vert ne prouve pas qu'une base a été touchée.** Les bancs sur serveur réel se
770
773
  > **skippent** quand leur variable d'infra est absente — et un test skippé compte comme vert.
771
774
  > PostgreSQL exige `NF_PG_URL`, MySQL/MariaDB `NF_MYSQL_URL`, MongoDB `NF_MONGO_TEST_URI`. La source
772
- > unique de ces variables et des commandes Docker correspondantes est `vitest.gates.ts` à la racine
775
+ > unique de ces variables et des commandes Docker correspondantes est `scripts/test/vitest/gates.ts` à la racine
773
776
  > du dépôt ; les suites concernées affichent un récapitulatif de couverture en fin d'exécution
774
777
  > (`gateReporter`). **Lire ce bloc avant de conclure « vert ».**
775
778
 
@@ -285,14 +285,15 @@ fichier, sans rien contourner.
285
285
 
286
286
  Deux conséquences du DDL dérivé au boot, à connaître dès maintenant :
287
287
 
288
- - la table est créée par un `CREATE TABLE IF NOT EXISTS` — **modifier le schéma n'altère pas une
289
- table déjà créée** (aucun `ALTER` n'est émis) ;
290
- - les **index** déclarés et les `DEFAULT` **SQL** ne sont pas émis. D'où les défauts posés côté
291
- JavaScript (`$defaultFn`), qui s'appliquent quoi qu'il arrive.
288
+ - la table est créée par un `CREATE TABLE IF NOT EXISTS`, avec ses index et ses clés étrangères ;
289
+ au démarrage suivant, seule une colonne ajoutée qui **accepte le vide** est rattrapée — une colonne
290
+ obligatoire ne l'est jamais ;
291
+ - les `DEFAULT` **SQL** ne sont pas émis. D'où les défauts posés côté JavaScript (`$defaultFn`), qui
292
+ s'appliquent quoi qu'il arrive.
292
293
 
293
294
  > [!TIP]
294
- > En développement, la façon la plus rapide de prendre en compte une colonne ajoutée est de
295
- > supprimer le fichier SQLite et de redémarrer. En production, cela relève d'une migration.
295
+ > En développement, une colonne obligatoire ajoutée se prend en compte par `nodefony orm:reset`. En
296
+ > production, cela relève d'une migration (`orm:generate`, puis `orm:migrate`).
296
297
 
297
298
  ### Étape 3 — déclarer l'entité
298
299
 
@@ -465,8 +466,8 @@ Sur une ligne dont on sait qu'elle **existe**, ils sont inutiles :
465
466
 
466
467
  ### Ce que le critère ne couvre pas
467
468
 
468
- Les `OR` logiques, les sous-requêtes, les agrégats et les jointures arbitraires n'en font pas
469
- partie. Ce n'est pas un oubli : c'est la limite de ce qui se porte d'un moteur SQL à MongoDB. La
469
+ Les sous-requêtes, les agrégats et les jointures arbitraires n'en font pas partie (la disjonction,
470
+ elle, s'exprime par `$or: [...]`). Ce n'est pas un oubli : c'est la limite de ce qui se porte d'un moteur SQL à MongoDB. La
470
471
  sortie est `IOrm.getNativeConnection()` (`IOrm.ts:51`), qui rend la connexion brute du driver.
471
472
 
472
473
  Un champ absent de l'entité lève `UnknownCriteriaField` (`errors.ts:23`), et le message liste les
@@ -530,8 +531,8 @@ d'administration, la primitive est `AbstractCrudService.findPage()` (`AbstractCr
530
531
  | `posts.update is not a function` | la méthode `update()` n'existe pas dans le contrat | `updateOne` pour une ligne (`IRepository.ts:269`), `updateMany` pour un lot (`IRepository.ts:312`) |
531
532
  | « no entity registered under "Post" » au premier appel | le fichier d'entité est importé, mais `defineEntity()` est **sans effet de bord** | ajouter l'entité à `@entities([...])` sur le module (`entitiesDecorator.ts:56`) |
532
533
  | La table n'existe pas alors que l'entité est déclarée | inscription faite à `onBoot` → course avec l'ouverture du connecteur | inscrire à `onRegister` — c'est ce que fait `entities()` (`entitiesDecorator.ts:56`) |
533
- | Une colonne ajoutée au schéma reste absente de la table | le DDL du boot est un `CREATE TABLE IF NOT EXISTS` : aucun `ALTER` n'est émis | supprimer la base de développement et redémarrer, ou passer par une migration |
534
- | Un `DEFAULT` SQL ou un index déclaré n'apparaît pas | le DDL dérivé ne les émet pas | poser le défaut côté JavaScript (`$defaultFn`) ; créer l'index par migration |
534
+ | Une colonne ajoutée au schéma reste absente de la table | colonne **obligatoire** : le rattrapage de développement n'ajoute que les colonnes facultatives | `nodefony orm:reset` en développement, ou `orm:generate` puis `orm:migrate` |
535
+ | Un `DEFAULT` SQL déclaré n'apparaît pas | le DDL dérivé ne l'émet pas | poser le défaut côté JavaScript (`$defaultFn`) |
535
536
  | Un filtre « champ vide » ne remonte jamais rien | `colonne = NULL` est toujours faux en SQL | `{ champ: { $null: true } }` ou la valeur nue `{ champ: null }` (`IRepository.ts:65`) |
536
537
  | `UnknownCriteriaField` sur un champ qui « existe pourtant » | faute de frappe, ou champ calculé absent du schéma | lire les champs connus dans le message ; pour du natif, passer par `getNativeConnection()` |
537
538
  | `connector: "sqlite"` ne trouve aucune connexion | `connector` nomme une **connexion**, pas un moteur | mettre la clé de `connectors` (`"default"`) ; le moteur est le `dialect` de la config |
@@ -559,7 +560,7 @@ dialecte — `@nodefony/drizzle` (sqlite en mémoire, PostgreSQL et MySQL en end
559
560
  > [!WARNING]
560
561
  > Un compteur vert ne prouve pas qu'une base a été touchée : les bancs sur serveur réel se
561
562
  > **skippent** faute de leur variable d'infra, et un test skippé compte comme vert. La source
562
- > unique de ces variables et des commandes Docker correspondantes est `vitest.gates.ts` à la
563
+ > unique de ces variables et des commandes Docker correspondantes est `scripts/test/vitest/gates.ts` à la
563
564
  > racine du dépôt ; les suites concernées affichent leur récapitulatif en fin d'exécution.
564
565
 
565
566
  ## 🔗 Pour aller plus loin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodefony/orm-core",
3
- "version": "10.0.0-beta.1",
3
+ "version": "10.0.0-beta.3",
4
4
  "description": "Un contrat de dépôt de données pour Nodefony, plusieurs moteurs : interfaces, registre et classes de base pour le support multi-ORM",
5
5
  "contributors": [],
6
6
  "type": "module",
@@ -37,12 +37,12 @@
37
37
  "esm"
38
38
  ],
39
39
  "peerDependencies": {
40
- "nodefony": "^10.0.0-beta.1"
40
+ "nodefony": "^10.0.0-beta.3"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/node": "26.6.3",
44
- "nodefony": "^10.0.0-beta.1",
45
- "vitest": "5.0.2"
43
+ "@types/node": "26.6.5",
44
+ "nodefony": "^10.0.0-beta.3",
45
+ "vitest": "5.0.4"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",