@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 +14 -12
- package/docs/index.md +22 -19
- package/docs/tutorial-entity.md +12 -11
- package/package.json +5 -5
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
|
-
>
|
|
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`, `
|
|
42
|
-
| `IEntity<S,M>` | Entité enregistrée : nom logique, connecteur cible, schéma, modèle natif
|
|
43
|
-
| `IRepository<T>` | CRUD portable : `find`/`findOne`/`create`/`
|
|
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 {
|
|
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 =
|
|
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
|
|
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
|
|
544
|
-
|
|
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:
|
|
614
|
-
(`MongooseRepository.ts:
|
|
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:
|
|
624
|
-
| Sonde de flux (requêtes/s, lentes) | oui — alimente `queryFlowMonitor` |
|
|
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
|
|
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`) ;
|
|
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
|
|
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
|
|
package/docs/tutorial-entity.md
CHANGED
|
@@ -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
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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,
|
|
295
|
-
>
|
|
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
|
|
469
|
-
|
|
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
|
|
534
|
-
| Un `DEFAULT` SQL
|
|
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
|
|
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.
|
|
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.
|
|
40
|
+
"nodefony": "^10.0.0-beta.3"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/node": "26.6.
|
|
44
|
-
"nodefony": "^10.0.0-beta.
|
|
45
|
-
"vitest": "5.0.
|
|
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",
|