@nodefony/orm-core 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.
Files changed (38) hide show
  1. package/dist/index.js +4 -3
  2. package/dist/nodefony/src/AbstractCrudService.js +7 -3
  3. package/dist/nodefony/src/ConnectionMonitor.js +41 -9
  4. package/dist/nodefony/src/EntityRegistry.js +9 -3
  5. package/dist/nodefony/src/Orm.js +19 -3
  6. package/dist/nodefony/src/OrmAdminApi.js +54 -12
  7. package/dist/nodefony/src/OrmRegistry.js +1 -1
  8. package/dist/nodefony/src/QueryFlowMonitor.js +2 -2
  9. package/dist/nodefony/src/connectDeadline.js +1 -1
  10. package/dist/nodefony/src/connectionDiagnosis.js +66 -6
  11. package/dist/nodefony/src/criteria.js +4 -3
  12. package/dist/nodefony/src/errors.js +59 -1
  13. package/dist/nodefony/src/omitUndefined.js +30 -0
  14. package/dist/nodefony/src/orphanEntities.js +63 -0
  15. package/dist/types/index.d.ts +5 -4
  16. package/dist/types/nodefony/interfaces/IEntity.d.ts +23 -4
  17. package/dist/types/nodefony/interfaces/IOrm.d.ts +10 -1
  18. package/dist/types/nodefony/interfaces/IOrmFlow.d.ts +1 -1
  19. package/dist/types/nodefony/interfaces/IOrmGraph.d.ts +58 -13
  20. package/dist/types/nodefony/interfaces/IOrmProbe.d.ts +9 -9
  21. package/dist/types/nodefony/interfaces/IPage.d.ts +1 -1
  22. package/dist/types/nodefony/interfaces/IRepository.d.ts +12 -12
  23. package/dist/types/nodefony/interfaces/ITransaction.d.ts +3 -1
  24. package/dist/types/nodefony/interfaces/index.d.ts +2 -2
  25. package/dist/types/nodefony/src/AbstractCrudService.d.ts +9 -5
  26. package/dist/types/nodefony/src/ConnectionMonitor.d.ts +9 -2
  27. package/dist/types/nodefony/src/Entity.d.ts +1 -1
  28. package/dist/types/nodefony/src/Orm.d.ts +9 -1
  29. package/dist/types/nodefony/src/OrmAdminApi.d.ts +17 -1
  30. package/dist/types/nodefony/src/connectionDiagnosis.d.ts +29 -0
  31. package/dist/types/nodefony/src/decorators/entityDecorator.d.ts +1 -1
  32. package/dist/types/nodefony/src/decorators/metadataStore.d.ts +5 -5
  33. package/dist/types/nodefony/src/errors.d.ts +48 -0
  34. package/dist/types/nodefony/src/omitUndefined.d.ts +16 -0
  35. package/dist/types/nodefony/src/orphanEntities.d.ts +38 -0
  36. package/docs/index.md +64 -60
  37. package/docs/tutorial-entity.md +26 -23
  38. package/package.json +8 -8
@@ -0,0 +1,63 @@
1
+ import { ormRegistry } from "./OrmRegistry.js";
2
+ import { entityRegistry } from "./EntityRegistry.js";
3
+ //#region nodefony/src/orphanEntities.ts
4
+ /**
5
+ * Entités inscrites sur un connecteur qu'aucun ORM n'a ouvert.
6
+ *
7
+ * Une telle entité ne sera servie par rien : son dépôt lèvera au premier
8
+ * appel, loin de sa cause. Vécu : sur une infra MongoDB, le `User` d'une
9
+ * application restait écrit en table SQL sur `default` — un connecteur que
10
+ * Drizzle n'ouvre pas sur cette infra — et le boot n'en disait RIEN.
11
+ *
12
+ * @param entities - entités inscrites (registre des entités).
13
+ * @param connectors - connecteurs ouverts (registre des ORM).
14
+ * @returns les orphelines, dans l'ordre d'inscription.
15
+ */
16
+ function findOrphanEntities(entities, connectors) {
17
+ return entities.filter((e) => !connectors.includes(e.connector));
18
+ }
19
+ /**
20
+ * La phrase qui NOMME les orphelines et le remède — `null` s'il n'y en a pas.
21
+ *
22
+ * @param orphans - entités sans connecteur ouvert.
23
+ * @param connectors - connecteurs ouverts, cités pour situer la faute.
24
+ * @param declared - connecteurs DÉCLARÉS par les modules ORM (leur configuration).
25
+ */
26
+ function describeOrphanEntities(orphans, connectors, declared = []) {
27
+ if (orphans.length === 0) return null;
28
+ const name = (e) => `${e.name}${e.module ? `@${e.module}` : ""} → « ${e.connector} »`;
29
+ const open = `Connecteurs ouverts : ${connectors.length > 0 ? connectors.join(", ") : "aucun"}.`;
30
+ const skipped = orphans.filter((e) => declared.includes(e.connector));
31
+ const unknown = orphans.filter((e) => !declared.includes(e.connector));
32
+ const parts = [];
33
+ if (unknown.length > 0) parts.push(`${unknown.length} entité(s) inscrite(s) sur un connecteur qu'aucun ORM n'a ouvert — rien ne les servira, leur dépôt lèvera au premier appel : ${unknown.map(name).join(", ")}. ${open} Une entité doit suivre la base déclarée (NF_DATABASE_URL) : table SQL sur une infra SQL, document sur MongoDB. État réel : npx nodefony inspect entities.`);
34
+ if (skipped.length > 0) parts.push(`${skipped.length} entité(s) sur un connecteur DÉCLARÉ mais jamais ouvert : ${skipped.map(name).join(", ")}. Ce n'est pas leur déclaration qui est en cause : un connecteur a échoué au démarrage (voir l'erreur de connexion plus haut), et ceux qui le suivent n'ont pas été ouverts. ${open}`);
35
+ return parts.join(" ");
36
+ }
37
+ /** Les démarrages déjà rapportés — un Kernel ne le dit qu'une fois. */
38
+ const reported = /* @__PURE__ */ new WeakSet();
39
+ /**
40
+ * Signale UNE fois par démarrage les entités orphelines, au moment où tous les
41
+ * ORM sont ouverts.
42
+ *
43
+ * Appelée par chaque module ORM (Drizzle, Mongoose) à `onReady` : la règle vit
44
+ * ici, une fois, et le premier appel la rend — le second se tait, sans quoi une
45
+ * application qui charge les deux ORM lirait deux fois le même avertissement.
46
+ * La garde porte sur le KERNEL et non sur le process : un process qui démarre
47
+ * plusieurs noyaux (une suite de tests) doit être prévenu à chacun.
48
+ *
49
+ * @param log - journal du module appelant.
50
+ * @param owner - le démarrage concerné (le Kernel), clé de la garde.
51
+ * @param declared - connecteurs que l'appelant a DÉCLARÉS (sa configuration).
52
+ * @returns le message rendu, ou `null` (rien à dire, ou déjà dit).
53
+ */
54
+ function reportOrphanEntities(log, owner, declared = []) {
55
+ if (reported.has(owner)) return null;
56
+ reported.add(owner);
57
+ const connectors = ormRegistry.list();
58
+ const message = describeOrphanEntities(findOrphanEntities(entityRegistry.list(), connectors), connectors, declared);
59
+ if (message !== null) log(message, "WARNING");
60
+ return message;
61
+ }
62
+ //#endregion
63
+ export { describeOrphanEntities, findOrphanEntities, reportOrphanEntities };
@@ -7,15 +7,16 @@
7
7
  * Module, pas d'enregistrement dans `@modules()`. Les drivers concrets sont les
8
8
  * Modules ; ils s'enregistrent eux-mêmes dans le `OrmRegistry` à leur boot.
9
9
  */
10
- export type { IOrm, IEntity, IEntityRelation, IRepository, OrmCriteria, Criteria, FieldCriteria, FieldOperators, UpdateData, UpdateOperators, RepositoryReadOptions, ITransaction, IPage, IPageQuery, PageQuery, } from "./nodefony/interfaces/index.js";
10
+ export type { IOrm, IEntity, IEntityIndex, IEntityRelation, IRepository, OrmCriteria, Criteria, FieldCriteria, FieldOperators, UpdateData, UpdateOperators, RepositoryReadOptions, ITransaction, IPage, IPageQuery, PageQuery, } from "./nodefony/interfaces/index.js";
11
11
  export { OPERATOR_KEYS, isFieldOperators, searchCriteria, LIKE_ESCAPE_CHAR, escapeLikeTerm, likePatternToRegExp, } from "./nodefony/src/criteria.js";
12
12
  export type { OperatorKey } from "./nodefony/src/criteria.js";
13
13
  export { UPDATE_OPERATOR_KEYS, isUpdateOperators, } from "./nodefony/src/criteria.js";
14
14
  export type { UpdateOperatorKey } from "./nodefony/src/criteria.js";
15
- export { UnknownCriteriaField, InvalidOrderOption, } from "./nodefony/src/errors.js";
15
+ export { UnknownCriteriaField, InvalidOrderOption, ReferencedEntityError, SavepointNotSupportedError, } from "./nodefony/src/errors.js";
16
16
  export { assertOrderOption } from "./nodefony/src/readOptions.js";
17
17
  export { OrmRegistry, ormRegistry } from "./nodefony/src/OrmRegistry.js";
18
18
  export { EntityRegistry, entityRegistry } from "./nodefony/src/EntityRegistry.js";
19
+ export { reportOrphanEntities } from "./nodefony/src/orphanEntities.js";
19
20
  export { Orm } from "./nodefony/src/Orm.js";
20
21
  export { Entity } from "./nodefony/src/Entity.js";
21
22
  export { AbstractCrudService } from "./nodefony/src/AbstractCrudService.js";
@@ -25,12 +26,12 @@ export { buildOrmGraph, buildConnectionHealth, buildOrmFlow, toDbml, toJsonSchem
25
26
  export { queryFlowMonitor } from "./nodefony/src/QueryFlowMonitor.js";
26
27
  export { connectionMonitor } from "./nodefony/src/ConnectionMonitor.js";
27
28
  export { buildOrmLeanHealth } from "./nodefony/src/buildOrmLeanHealth.js";
28
- export { diagnoseConnectionFailure, parseConnectionTarget, } from "./nodefony/src/connectionDiagnosis.js";
29
+ export { diagnoseConnectionFailure, describeConnectFailure, parseConnectionTarget, REJECTED_MARKER, } from "./nodefony/src/connectionDiagnosis.js";
29
30
  export type { ConnectionVerdict, IConnectionDiagnosis, IConnectionTarget, } from "./nodefony/src/connectionDiagnosis.js";
30
31
  export { CONNECT_TIMEOUT_MS, withConnectDeadline, } from "./nodefony/src/connectDeadline.js";
31
32
  export { wireOrmAdminPlane, resolveOrmFlowEnabled, reportOrmBootLines, } from "./nodefony/src/ormWiring.js";
32
33
  export type { ISlowQuery, IQueryFlow, IOrmFlowReport, } from "./nodefony/interfaces/IOrmFlow.js";
33
- export type { IColumnInfo, IConnectionInfo, IRelationInfo, IEntityGraphNode, IOrmSummary, IOrmGraph, IConnectionError, IConnectionHealth, } from "./nodefony/interfaces/IOrmGraph.js";
34
+ export type { IColumnInfo, IConnectionInfo, IRelationInfo, IEntityGraphNode, IOrmSummary, IOrmGraph, IConnectionError, IConnectionEvent, IOrmResilience, IConnectionHealth, } from "./nodefony/interfaces/IOrmGraph.js";
34
35
  export type { ILatencyWindow, IOrmStorageProbe, IOrmPoolProbe, IOrmProbe, } from "./nodefony/interfaces/IOrmProbe.js";
35
36
  export type { IOrmMigrationAction, IOrmMigrationEntry, IOrmMigrationSource, IOrmMigrationStatus, IOrmMigrationFailure, IOrmMigrationReply, IOrmPendingMigration, IOrmMigrationPlan, IOrmMigrationPlanReply, IOrmMigrationApplied, IOrmMigrationApplyReply, } from "./nodefony/interfaces/IOrmMigrations.js";
36
37
  export { isMigrationFailure } from "./nodefony/interfaces/IOrmMigrations.js";
@@ -30,6 +30,19 @@ export interface IEntityRelation {
30
30
  * @typeParam S - type du schéma natif du driver.
31
31
  * @typeParam M - type du modèle compilé natif du driver.
32
32
  */
33
+ /**
34
+ * Index COMPOSITE déclaré par une entité, pour l'adapter qui ne sait pas
35
+ * l'exprimer dans son schéma (Mongoose : un `SchemaDefinition` plat n'a d'index
36
+ * que par champ).
37
+ */
38
+ export interface IEntityIndex {
39
+ /** Champs et sens, dans l'ORDRE de l'index (`1` croissant, `-1` décroissant). */
40
+ readonly fields: Readonly<Record<string, 1 | -1>>;
41
+ /** Contrainte d'unicité sur la combinaison. */
42
+ readonly unique?: boolean;
43
+ /** Nom de l'index en base (sinon celui que le driver dérive des champs). */
44
+ readonly name?: string;
45
+ }
33
46
  export interface IEntity<S = unknown, M = unknown> {
34
47
  /** Nom logique de l'entité (clé de lookup, ex. `"User"`). */
35
48
  readonly name: string;
@@ -40,7 +53,7 @@ export interface IEntity<S = unknown, M = unknown> {
40
53
  * **Optionnel** : sert au regroupement dans le graphe canonique / ERD Studio.
41
54
  * Non renseigné → entité non rattachée (groupe « — » côté UI).
42
55
  */
43
- readonly module?: string;
56
+ readonly module?: string | undefined;
44
57
  /**
45
58
  * **Classification** de l'entité (ex. domaine fonctionnel `"facturation"`,
46
59
  * `"comptabilité"`) — axe de regroupement **distinct** du `module`
@@ -48,7 +61,7 @@ export interface IEntity<S = unknown, M = unknown> {
48
61
  * base** (centaines de tables d'un même module) : l'ERD groupe/filtre par
49
62
  * `domain`. **Optionnel** ; non renseigné → retombe sur `module`.
50
63
  */
51
- readonly domain?: string;
64
+ readonly domain?: string | undefined;
52
65
  /** Définition de schéma propre au driver (forme libre). */
53
66
  readonly schema: S;
54
67
  /** Modèle compilé natif, renseigné après connexion de l'ORM. */
@@ -59,7 +72,13 @@ export interface IEntity<S = unknown, M = unknown> {
59
72
  * schema-as-code (Drizzle) les expriment en colonnes → ce flag y est sans effet.
60
73
  * **Optionnel**, défaut `false`.
61
74
  */
62
- readonly timestamps?: boolean;
75
+ readonly timestamps?: boolean | undefined;
63
76
  /** Relations déclarées vers d'autres entités (par nom logique). */
64
- readonly relations?: ReadonlyArray<IEntityRelation>;
77
+ readonly relations?: ReadonlyArray<IEntityRelation> | undefined;
78
+ /**
79
+ * Index composites, appliqués par l'adapter qui les exprime hors schéma
80
+ * (Mongoose `schema.index()`). Les ORM schema-as-code (Drizzle) les déclarent
81
+ * dans leur table → ce champ y est sans effet. **Optionnel**.
82
+ */
83
+ readonly indexes?: ReadonlyArray<IEntityIndex> | undefined;
65
84
  }
@@ -1,6 +1,6 @@
1
1
  import type { IRepository } from "./IRepository.js";
2
2
  import type { ITransaction } from "./ITransaction.js";
3
- import type { IColumnInfo, IConnectionInfo } from "./IOrmGraph.js";
3
+ import type { IOrmResilience, IColumnInfo, IConnectionInfo } from "./IOrmGraph.js";
4
4
  import type { IOrmProbe } from "./IOrmProbe.js";
5
5
  import type { IOrmMigrationApplyReply, IOrmMigrationPlanReply, IOrmMigrationReply } from "./IOrmMigrations.js";
6
6
  /**
@@ -73,6 +73,15 @@ export interface IOrm {
73
73
  * @returns infos de connexion, ou `undefined` si non implémenté.
74
74
  */
75
75
  describeConnection?(): IConnectionInfo;
76
+ /**
77
+ * Décrit le mécanisme de résilience tel qu'il tourne (battement, perte en
78
+ * souffrance) pour le data plane. **Optionnel** : la classe `Orm` l'implémente
79
+ * pour tous ses adapters ; un ORM qui ne dérive pas d'elle laisse le
80
+ * diagnostic sans cette section.
81
+ *
82
+ * @returns l'état du mécanisme.
83
+ */
84
+ describeResilience?(): IOrmResilience;
76
85
  /**
77
86
  * Ping bas-coût de la connexion (round-trip réel vers la base) pour le
78
87
  * diagnostic du data plane. **Optionnel** : un adapter qui ne l'implémente pas
@@ -26,7 +26,7 @@ export interface ISlowQuery {
26
26
  /** Connecteur ORM (clé du registre). */
27
27
  connector: string;
28
28
  /** SQL paramétré et redacté (absent si l'adapter ne sait pas l'extraire). */
29
- sql?: string;
29
+ sql?: string | undefined;
30
30
  }
31
31
  /**
32
32
  * Compteurs de flux d'UN connecteur ORM. Cumuls depuis le boot (le débit/s se
@@ -32,7 +32,7 @@ export interface IRelationInfo {
32
32
  /** Champ portant la relation côté entité courante. */
33
33
  field: string;
34
34
  /** Clé étrangère (explicite ou dérivée déterministe). */
35
- foreignKey?: string;
35
+ foreignKey?: string | undefined;
36
36
  }
37
37
  /** Nœud du graphe : une entité avec ses colonnes et ses relations. */
38
38
  export interface IEntityGraphNode {
@@ -66,11 +66,11 @@ export interface IConnectionInfo {
66
66
  * `host:port/base` (serveur) ou URI redactée. **Jamais de credential** (mot de
67
67
  * passe retiré côté adapter).
68
68
  */
69
- target?: string;
69
+ target?: string | undefined;
70
70
  /** Version du moteur/base (ex. SQLite `3.45.1`), si l'adapter peut l'obtenir. */
71
- version?: string;
71
+ version?: string | undefined;
72
72
  /** Version de la lib ORM elle-même (ex. drizzle-orm `0.44.x`), si connue. */
73
- ormVersion?: string;
73
+ ormVersion?: string | undefined;
74
74
  }
75
75
  /** Résumé d'un ORM/connecteur enregistré. */
76
76
  export interface IOrmSummary {
@@ -82,14 +82,18 @@ export interface IOrmSummary {
82
82
  * `IOrm.vendor` déclaré par chaque adapter (P7.1 industrialisation ORM).
83
83
  */
84
84
  vendor: string;
85
- /** `true` si c'est le connecteur par défaut (`"default"`). */
85
+ /**
86
+ * `true` pour le connecteur par défaut : celui qui porte les stores de
87
+ * l'application (un RÔLE constaté, pas le nom `"default"` — repli sur ce nom
88
+ * seulement quand aucun store n'est porté par un ORM).
89
+ */
86
90
  default: boolean;
87
91
  /** État de la connexion. */
88
92
  connected: boolean;
89
93
  /** Nombre d'entités rattachées à cet ORM. */
90
94
  entityCount: number;
91
95
  /** Connexion sous-jacente (driver + cible), si l'adapter l'expose. */
92
- connection?: IConnectionInfo;
96
+ connection?: IConnectionInfo | undefined;
93
97
  }
94
98
  /** Erreur de connexion horodatée (message **redacté** — jamais de credential). */
95
99
  export interface IConnectionError {
@@ -98,6 +102,37 @@ export interface IConnectionError {
98
102
  /** Horodatage epoch ms. */
99
103
  ts: number;
100
104
  }
105
+ /**
106
+ * Un événement de vie de la connexion : une PERTE ou une REPRISE, constatée
107
+ * par l'adapter (événement du driver) ou par le battement de cœur.
108
+ */
109
+ export interface IConnectionEvent {
110
+ /** `lost` : la base a cessé de répondre ; `restored` : elle répond à nouveau. */
111
+ kind: "lost" | "restored";
112
+ /** Horodatage epoch ms. */
113
+ ts: number;
114
+ /** Cause de la perte (credential déjà retiré) — absente pour une reprise. */
115
+ reason?: string | undefined;
116
+ }
117
+ /**
118
+ * Le mécanisme de résilience d'un connecteur, TEL QU'IL TOURNE dans ce process.
119
+ *
120
+ * Rend lisible ce qui décide de « connecté / perdu / rétabli » : un pilote qui
121
+ * surveille ses serveurs (MongoDB) le sait seul ; les autres ne l'apprennent
122
+ * que par une requête, d'où le battement de cœur que porte la classe `Orm`.
123
+ */
124
+ export interface IOrmResilience {
125
+ /** Période du battement (ms) ; `0` = désactivé (`NF_ORM_HEARTBEAT_MS=0`). */
126
+ heartbeatMs: number;
127
+ /** Délai au-delà duquel un battement sans réponse vaut une perte (ms). */
128
+ heartbeatTimeoutMs: number;
129
+ /** Le battement tourne-t-il en ce moment ? */
130
+ heartbeatActive: boolean;
131
+ /** L'adapter sait-il répondre à `ping()` (condition du battement) ? */
132
+ pingable: boolean;
133
+ /** Une perte est-elle constatée et PAS ENCORE réparée ? */
134
+ lostPending: boolean;
135
+ }
101
136
  /**
102
137
  * Diagnostic d'un connecteur — réponse de `/nodefony/orm/api/connection/health`.
103
138
  * Combine l'état figé ({@link IConnectionInfo}), les compteurs de cycle de vie
@@ -118,11 +153,11 @@ export interface IConnectionHealth {
118
153
  /** Base/driver (`sqlite`, `mongodb`…). */
119
154
  driver: string;
120
155
  /** Cible lisible (chemin relatif / host:port), jamais de credential. */
121
- target?: string;
156
+ target?: string | undefined;
122
157
  /** Version moteur. */
123
- version?: string;
158
+ version?: string | undefined;
124
159
  /** Version lib ORM. */
125
- ormVersion?: string;
160
+ ormVersion?: string | undefined;
126
161
  /** État courant (`isConnected`). */
127
162
  connected: boolean;
128
163
  /** Connecté depuis (epoch ms), `null` si jamais connecté. */
@@ -131,8 +166,18 @@ export interface IConnectionHealth {
131
166
  uptimeMs: number | null;
132
167
  /** Nombre total de connexions réussies. */
133
168
  connectCount: number;
134
- /** Reconnexions (connexions au-delà de la première). */
169
+ /** Reprises CONSTATÉES après une perte (jamais déduites des connexions). */
135
170
  reconnectCount: number;
171
+ /** Pertes constatées depuis le démarrage du process. */
172
+ lostCount: number;
173
+ /** Dernière perte (epoch ms), `null` si aucune. */
174
+ lastLostAt: number | null;
175
+ /** Dernière reprise (epoch ms), `null` si aucune. */
176
+ lastRestoredAt: number | null;
177
+ /** Pertes et reprises récentes (ring borné, plus récentes d'abord). */
178
+ events: IConnectionEvent[];
179
+ /** Mécanisme de détection et de reprise, si l'ORM le décrit. */
180
+ resilience?: IOrmResilience | undefined;
136
181
  /** Nombre total d'erreurs enregistrées (connexion + ping). */
137
182
  errorCount: number;
138
183
  /** Dernière erreur, `null` si aucune. */
@@ -150,11 +195,11 @@ export interface IConnectionHealth {
150
195
  /** Fenêtre glissante de latence (min/moy/max sur les N derniers pings). */
151
196
  latency: ILatencyWindow;
152
197
  /** Sonde de stockage (driver), si l'adapter l'expose. */
153
- storage?: IOrmStorageProbe;
198
+ storage?: IOrmStorageProbe | undefined;
154
199
  /** Sonde de pool de connexions (driver), si l'adapter l'expose. */
155
- pool?: IOrmPoolProbe;
200
+ pool?: IOrmPoolProbe | undefined;
156
201
  /** Métriques driver libres (clé→valeur). */
157
- extra?: Record<string, string | number | boolean>;
202
+ extra?: Record<string, string | number | boolean> | undefined;
158
203
  }
159
204
  /** Graphe complet du modèle de données — réponse de `/nodefony/orm/api/graph`. */
160
205
  export interface IOrmGraph {
@@ -32,15 +32,15 @@ export interface ILatencyWindow {
32
32
  */
33
33
  export interface IOrmStorageProbe {
34
34
  /** Taille de la base (octets) — `pages × pageSize` (SQLite). */
35
- sizeBytes?: number;
35
+ sizeBytes?: number | undefined;
36
36
  /** Nombre de pages allouées. */
37
- pages?: number;
37
+ pages?: number | undefined;
38
38
  /** Taille d'une page (octets). */
39
- pageSize?: number;
39
+ pageSize?: number | undefined;
40
40
  /** Mode de journalisation (SQLite : `wal`, `delete`, `memory`…). */
41
- journalMode?: string;
41
+ journalMode?: string | undefined;
42
42
  /** Pages libres (fragmentation / espace récupérable au VACUUM). */
43
- freePages?: number;
43
+ freePages?: number | undefined;
44
44
  }
45
45
  /**
46
46
  * Sonde de pool de connexions — pertinente pour les bases serveur (Mongo,
@@ -48,13 +48,13 @@ export interface IOrmStorageProbe {
48
48
  */
49
49
  export interface IOrmPoolProbe {
50
50
  /** Taille max configurée du pool. */
51
- size?: number;
51
+ size?: number | undefined;
52
52
  /** Connexions disponibles (idle). */
53
- available?: number;
53
+ available?: number | undefined;
54
54
  /** Connexions en cours d'utilisation. */
55
- borrowed?: number;
55
+ borrowed?: number | undefined;
56
56
  /** Demandes en attente d'une connexion. */
57
- pending?: number;
57
+ pending?: number | undefined;
58
58
  }
59
59
  /**
60
60
  * Sonde profonde driver-spécifique retournée par {@link IOrm.probe}. Tous les
@@ -20,5 +20,5 @@ export type { IPage, IPageQuery } from "nodefony";
20
20
  */
21
21
  export interface PageQuery<T = unknown> extends IPageQuery {
22
22
  /** Filtre typé optionnel appliqué avant la pagination (toutes les lignes si omis). */
23
- criteria?: Criteria<T>;
23
+ criteria?: Criteria<T> | undefined;
24
24
  }
@@ -23,23 +23,23 @@ export type OrmCriteria = Record<string, unknown>;
23
23
  */
24
24
  export interface FieldOperators<V> {
25
25
  /** Égalité stricte (équivalent à passer la valeur nue). */
26
- $eq?: V;
26
+ $eq?: V | undefined;
27
27
  /** Différent de. */
28
- $ne?: V;
28
+ $ne?: V | undefined;
29
29
  /** Strictement supérieur à. */
30
- $gt?: V;
30
+ $gt?: V | undefined;
31
31
  /** Supérieur ou égal à. */
32
- $gte?: V;
32
+ $gte?: V | undefined;
33
33
  /** Strictement inférieur à. */
34
- $lt?: V;
34
+ $lt?: V | undefined;
35
35
  /** Inférieur ou égal à. */
36
- $lte?: V;
36
+ $lte?: V | undefined;
37
37
  /** Appartient à l'ensemble. */
38
- $in?: readonly V[];
38
+ $in?: readonly V[] | undefined;
39
39
  /** N'appartient pas à l'ensemble. */
40
- $nin?: readonly V[];
40
+ $nin?: readonly V[] | undefined;
41
41
  /** Motif SQL `LIKE` (`%`/`_`) — pertinent pour les champs texte uniquement. */
42
- $like?: string;
42
+ $like?: string | undefined;
43
43
  /**
44
44
  * Teste l'**absence de valeur** : `true` → `IS NULL`, `false` → `IS NOT NULL`
45
45
  * (Mongo : `$eq`/`$ne null`, qui couvre aussi le champ absent).
@@ -60,7 +60,7 @@ export interface FieldOperators<V> {
60
60
  * // les PAT qui ont une expiration
61
61
  * repo.find({ expiresAt: { $null: false } });
62
62
  */
63
- $null?: boolean;
63
+ $null?: boolean | undefined;
64
64
  }
65
65
  /**
66
66
  * Opérateurs d'**écriture** applicables à un champ dans le `update` d'un
@@ -154,7 +154,7 @@ export type Criteria<T> = {
154
154
  * // les clés encore utilisables
155
155
  * repo.count({ revokedAt: null, $or: [{ expiresAt: null }, { expiresAt: { $gt: now } }] });
156
156
  */
157
- $or?: ReadonlyArray<Criteria<T>>;
157
+ $or?: ReadonlyArray<Criteria<T>> | undefined;
158
158
  } & OrmCriteria;
159
159
  /**
160
160
  * Options de lecture (`find`/`findOne`) portables cross-ORM.
@@ -181,7 +181,7 @@ export interface RepositoryReadOptions {
181
181
  * la frontière qui la reçoit (`parsePageQuery` accepte `champ:sens` en casse
182
182
  * libre), jamais au repository.
183
183
  */
184
- order?: Array<[string, "ASC" | "DESC"]>;
184
+ order?: Array<[string, "ASC" | "DESC"]> | undefined;
185
185
  }
186
186
  /**
187
187
  * Contrat CRUD minimal exposé par un repository, indépendant de l'ORM sous-jacent.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Obtenue dans le callback de `IOrm.transaction()`. Les transactions cross-ORM
5
5
  * (2PC) ne sont PAS garanties : une transaction porte sur un seul ORM/connexion.
6
- * La sémantique des savepoints dépend du driver (peut être un no-op).
6
+ * Un moteur sans savepoints (MongoDB) les REFUSE : `SavepointNotSupportedError`.
7
7
  */
8
8
  export interface ITransaction {
9
9
  /** Valide définitivement les opérations de la transaction. */
@@ -14,12 +14,14 @@ export interface ITransaction {
14
14
  * Crée un point de sauvegarde nommé pour un rollback partiel ultérieur.
15
15
  *
16
16
  * @param name - identifiant du savepoint.
17
+ * @throws SavepointNotSupportedError si le moteur n'en porte pas (MongoDB).
17
18
  */
18
19
  savepoint(name: string): Promise<void>;
19
20
  /**
20
21
  * Annule jusqu'à un savepoint sans terminer la transaction.
21
22
  *
22
23
  * @param name - savepoint cible.
24
+ * @throws SavepointNotSupportedError si le moteur n'en porte pas (MongoDB).
23
25
  */
24
26
  rollbackTo(name: string): Promise<void>;
25
27
  /**
@@ -1,7 +1,7 @@
1
1
  export type { IOrm } from "./IOrm.js";
2
- export type { IEntity, IEntityRelation } from "./IEntity.js";
2
+ export type { IEntity, IEntityIndex, IEntityRelation } from "./IEntity.js";
3
3
  export type { IRepository, OrmCriteria, Criteria, FieldCriteria, FieldOperators, UpdateData, UpdateOperators, RepositoryReadOptions, } from "./IRepository.js";
4
4
  export type { ITransaction } from "./ITransaction.js";
5
5
  export type { IPage, IPageQuery, PageQuery } from "./IPage.js";
6
- export type { IColumnInfo, IConnectionInfo, IRelationInfo, IEntityGraphNode, IOrmSummary, IOrmGraph, IConnectionError, IConnectionHealth, } from "./IOrmGraph.js";
6
+ export type { IColumnInfo, IConnectionInfo, IRelationInfo, IEntityGraphNode, IOrmSummary, IOrmGraph, IConnectionError, IConnectionEvent, IOrmResilience, IConnectionHealth, } from "./IOrmGraph.js";
7
7
  export type { ILatencyWindow, IOrmStorageProbe, IOrmPoolProbe, IOrmProbe, } from "./IOrmProbe.js";
@@ -1,4 +1,5 @@
1
1
  import { Service } from "nodefony";
2
+ import type { PartialInput } from "nodefony";
2
3
  import type { IRepository, Criteria, RepositoryReadOptions, IPage, PageQuery } from "../interfaces/index.js";
3
4
  import type { ServiceWiring } from "./serviceWiring.js";
4
5
  /**
@@ -102,10 +103,13 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
102
103
  /**
103
104
  * Crée une entité : `beforeCreate` → persistance → `afterCreate` → `onCreated`.
104
105
  *
106
+ * Une clé qui vaut `undefined` vaut clé absente : elle est retirée avant la
107
+ * persistance (cf `omitUndefined`), quel que soit l'adaptateur.
108
+ *
105
109
  * @param data - champs de l'entité à créer.
106
110
  * @returns l'entité persistée. Émet `onCreated`.
107
111
  */
108
- create(data: Partial<T>): Promise<T>;
112
+ create(data: PartialInput<T>): Promise<T>;
109
113
  /**
110
114
  * Met à jour **au plus une** entité : `beforeUpdate` → persistance atomique
111
115
  * → `afterUpdate` → `onUpdated`.
@@ -120,10 +124,10 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
120
124
  * on la remontera dans un service le jour où un usage métier la réclame.
121
125
  *
122
126
  * @param criteria - filtre de sélection (champ inconnu → `UnknownCriteriaField`).
123
- * @param data - champs à modifier.
127
+ * @param data - champs à modifier ; une clé `undefined` n'est pas touchée.
124
128
  * @returns l'entité mise à jour, ou `null` si aucune ne correspond (pas d'event).
125
129
  */
126
- updateOne(criteria: Criteria<T>, data: Partial<T>): Promise<T | null>;
130
+ updateOne(criteria: Criteria<T>, data: PartialInput<T>): Promise<T | null>;
127
131
  /**
128
132
  * Supprime : `beforeDelete` → persistance → `afterDelete` → `onDeleted`.
129
133
  *
@@ -138,7 +142,7 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
138
142
  * @param data - données entrantes.
139
143
  * @returns les données préparées à persister.
140
144
  */
141
- protected beforeCreate(data: Partial<T>): Partial<T> | Promise<Partial<T>>;
145
+ protected beforeCreate(data: PartialInput<T>): PartialInput<T> | Promise<PartialInput<T>>;
142
146
  /** Effet de bord après création (ex. provisionner une ressource liée). */
143
147
  protected afterCreate(_entity: T): void | Promise<void>;
144
148
  /**
@@ -148,7 +152,7 @@ export declare abstract class AbstractCrudService<T, R extends IRepository<T> =
148
152
  * @param data - données entrantes.
149
153
  * @returns les données préparées à persister.
150
154
  */
151
- protected beforeUpdate(_criteria: Criteria<T>, data: Partial<T>): Partial<T> | Promise<Partial<T>>;
155
+ protected beforeUpdate(_criteria: Criteria<T>, data: PartialInput<T>): PartialInput<T> | Promise<PartialInput<T>>;
152
156
  /** Effet de bord après mise à jour. */
153
157
  protected afterUpdate(_entity: T): void | Promise<void>;
154
158
  /** Garde/validation avant suppression (ex. interdire la suppression du dernier admin). */
@@ -1,4 +1,4 @@
1
- import type { IConnectionError } from "../interfaces/IOrmGraph.js";
1
+ import type { IConnectionError, IConnectionEvent } from "../interfaces/IOrmGraph.js";
2
2
  import type { ILatencyWindow } from "../interfaces/IOrmProbe.js";
3
3
  /** Vue figée des compteurs du moniteur pour un connecteur. */
4
4
  export interface IConnectionMonitorCore {
@@ -8,6 +8,12 @@ export interface IConnectionMonitorCore {
8
8
  reconnectCount: number;
9
9
  /** Pertes de connexion constatées depuis le démarrage du process. */
10
10
  lostCount: number;
11
+ /** Dernière perte (epoch ms), `null` si aucune. */
12
+ lastLostAt: number | null;
13
+ /** Dernière reprise (epoch ms), `null` si aucune. */
14
+ lastRestoredAt: number | null;
15
+ /** Pertes et reprises récentes, plus récentes d'abord. */
16
+ events: IConnectionEvent[];
11
17
  errorCount: number;
12
18
  lastConnectMs: number | null;
13
19
  lastError: IConnectionError | null;
@@ -58,8 +64,9 @@ declare class ConnectionMonitor {
58
64
  * preuve de bonne santé.
59
65
  *
60
66
  * @param name - clé du connecteur.
67
+ * @param reason - cause lisible (credential déjà retiré), si connue.
61
68
  */
62
- recordLost(name: string): void;
69
+ recordLost(name: string, reason?: string): void;
63
70
  /**
64
71
  * Enregistre une **reprise** de connexion constatée par l'adapter.
65
72
  *
@@ -25,7 +25,7 @@ export declare abstract class Entity<S = unknown, M = unknown> implements IEntit
25
25
  /** Modèle compilé natif, renseigné après connexion de l'ORM. */
26
26
  model?: M;
27
27
  /** Relations déclarées vers d'autres entités (par nom logique). */
28
- readonly relations?: ReadonlyArray<IEntityRelation>;
28
+ readonly relations?: ReadonlyArray<IEntityRelation> | undefined;
29
29
  /**
30
30
  * Construit la définition de schéma propre au driver.
31
31
  *
@@ -1,6 +1,6 @@
1
1
  import { Service } from "nodefony";
2
2
  import type { Container, Event, DefaultOptionsService } from "nodefony";
3
- import type { IColumnInfo, IConnectionInfo, IOrm, IRepository, ITransaction } from "../interfaces/index.js";
3
+ import type { IColumnInfo, IConnectionInfo, IOrm, IOrmResilience, IRepository, ITransaction } from "../interfaces/index.js";
4
4
  /**
5
5
  * Classe de base abstraite de tout ORM Nodefony — câble {@link Service} (DI,
6
6
  * Syslog, bus d'événements) et le contrat {@link IOrm}, et s'auto-enregistre
@@ -121,6 +121,14 @@ export declare abstract class Orm extends Service implements IOrm {
121
121
  * prochain battement pour rendre la main.
122
122
  */
123
123
  protected startHeartbeat(): void;
124
+ /**
125
+ * Décrit le mécanisme de résilience TEL QU'IL TOURNE — lecture pure, pour
126
+ * le data plane (`connection/health`) : période et montre du battement,
127
+ * battement actif ou non, perte en souffrance.
128
+ *
129
+ * @returns l'état du mécanisme dans ce process.
130
+ */
131
+ describeResilience(): IOrmResilience;
124
132
  /** Arrête le battement et libère la minuterie. Idempotent. */
125
133
  protected stopHeartbeat(): void;
126
134
  /**
@@ -1,6 +1,22 @@
1
- import type { IAdminApi, IAdminRegistry } from "nodefony";
1
+ import type { IAdminApi, IAdminRegistry, IStoreResolution } from "nodefony";
2
2
  import type { IConnectionHealth, IOrmGraph } from "../interfaces/IOrmGraph.js";
3
3
  import type { IOrmFlowReport } from "../interfaces/IOrmFlow.js";
4
+ /** Résumé des ORM enregistrés (statut connexion + nombre d'entités). */
5
+ /**
6
+ * Connecteur qui porte les stores de l'application — le connecteur PAR DÉFAUT.
7
+ *
8
+ * « Défaut » est un RÔLE, pas un nom : sur une infra MongoDB les briques
9
+ * durables vivent sur `nodefony` (Mongoose), et marquer `default` (Drizzle)
10
+ * parce qu'il s'appelle ainsi désignait une base que personne n'utilise. Le
11
+ * rôle se CONSTATE dans le registre des stores (#474) : le connecteur qui porte
12
+ * le plus de briques durables, puis de briques tout court.
13
+ *
14
+ * @param resolutions - registre des stores du kernel (`storeResolutions`).
15
+ * @param names - connecteurs enregistrés ; une brique sur un autre nom est ignorée.
16
+ * @returns le nom du connecteur, ou `null` si aucune brique n'est portée par
17
+ * un ORM (le repli sur le nom `default` s'applique alors).
18
+ */
19
+ export declare function storeCarrier(resolutions: readonly IStoreResolution[], names: readonly string[]): string | null;
4
20
  /** Construit le graphe canonique complet (optionnellement filtré par connecteur). */
5
21
  export declare function buildOrmGraph(connectorFilter?: string): IOrmGraph;
6
22
  /**
@@ -24,6 +24,12 @@ export type ConnectionVerdict =
24
24
  "unreachable"
25
25
  /** Un serveur a répondu, et il refuse — le port est bien tenu par quelqu'un. */
26
26
  | "answered"
27
+ /**
28
+ * La connexion a ABOUTI, puis le serveur a refusé une INSTRUCTION — une
29
+ * table, une contrainte, un type. Ni l'adresse ni l'état de la base ne sont
30
+ * en cause : c'est le schéma envoyé qu'il faut corriger.
31
+ */
32
+ | "rejected"
27
33
  /** L'erreur ne permet de trancher ni dans un sens ni dans l'autre. */
28
34
  | "unknown";
29
35
  /** Diagnostic rendu à l'appelant, qui compose le message final. */
@@ -56,3 +62,26 @@ export declare function parseConnectionTarget(url?: string | null): IConnectionT
56
62
  * @returns le verdict, le code constaté et une explication déjà rédigée.
57
63
  */
58
64
  export declare function diagnoseConnectionFailure(error: unknown, target?: IConnectionTarget, platform?: string): IConnectionDiagnosis;
65
+ /**
66
+ * Marqueur de la phrase « connecté, puis refusé » — lu par `create app`
67
+ * (`migrationFailureCause`, cœur) pour NE PAS conclure à une base injoignable.
68
+ * Le cœur ne peut pas importer ce paquet : sa copie est éprouvée contre
69
+ * celle-ci par un test de parité.
70
+ */
71
+ export declare const REJECTED_MARKER = "s'est connect\u00E9, mais le serveur a refus\u00E9 une instruction \u2014 ";
72
+ /**
73
+ * Compose le message d'échec au démarrage d'un connecteur — UNE rédaction pour
74
+ * tous les adapters.
75
+ *
76
+ * 🔴 Un refus d'instruction (verdict `rejected`) ne commence JAMAIS par « n'a
77
+ * pas pu se connecter » : c'était faux, et la phrase envoyait vérifier
78
+ * l'adresse et l'état d'une base qui répondait très bien. La cause du pilote
79
+ * passe alors EN TÊTE — c'est elle qui nomme la table ou la contrainte.
80
+ *
81
+ * @param subject - sujet de la phrase (`Drizzle : le connecteur "x" (…)`).
82
+ * @param diagnosis - verdict de {@link diagnoseConnectionFailure}.
83
+ * @param cause - message brut du pilote.
84
+ * @param advice - conseil propre à l'adapter, pour un échec de CONNEXION.
85
+ * @returns le message complet.
86
+ */
87
+ export declare function describeConnectFailure(subject: string, diagnosis: IConnectionDiagnosis, cause: string, advice?: string): string;