@nodefony/orm-core 10.0.0-alpha.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/LICENSE +544 -0
- package/README.md +131 -0
- package/dist/index.js +22 -0
- package/dist/nodefony/interfaces/IEntity.js +1 -0
- package/dist/nodefony/interfaces/IOrm.js +1 -0
- package/dist/nodefony/interfaces/IOrmFlow.js +1 -0
- package/dist/nodefony/interfaces/IOrmGraph.js +1 -0
- package/dist/nodefony/interfaces/IOrmMigrations.js +12 -0
- package/dist/nodefony/interfaces/IOrmProbe.js +1 -0
- package/dist/nodefony/interfaces/IPage.js +1 -0
- package/dist/nodefony/interfaces/IRepository.js +1 -0
- package/dist/nodefony/interfaces/ITransaction.js +1 -0
- package/dist/nodefony/interfaces/index.js +1 -0
- package/dist/nodefony/src/AbstractCrudService.js +199 -0
- package/dist/nodefony/src/ConnectionMonitor.js +181 -0
- package/dist/nodefony/src/Entity.js +42 -0
- package/dist/nodefony/src/EntityRegistry.js +109 -0
- package/dist/nodefony/src/Orm.js +297 -0
- package/dist/nodefony/src/OrmAdminApi.js +491 -0
- package/dist/nodefony/src/OrmRegistry.js +75 -0
- package/dist/nodefony/src/QueryFlowMonitor.js +128 -0
- package/dist/nodefony/src/buildOrmLeanHealth.js +54 -0
- package/dist/nodefony/src/criteria.js +176 -0
- package/dist/nodefony/src/decorators/entitiesDecorator.js +67 -0
- package/dist/nodefony/src/decorators/entityDecorator.js +51 -0
- package/dist/nodefony/src/decorators/index.js +5 -0
- package/dist/nodefony/src/decorators/metadataStore.js +38 -0
- package/dist/nodefony/src/decorators/repositoryDecorator.js +35 -0
- package/dist/nodefony/src/defineEntity.js +27 -0
- package/dist/nodefony/src/errors.js +76 -0
- package/dist/nodefony/src/ormWiring.js +78 -0
- package/dist/nodefony/src/paginate.js +55 -0
- package/dist/nodefony/src/readOptions.js +52 -0
- package/dist/nodefony/src/serviceWiring.js +1 -0
- package/dist/types/index.d.ts +39 -0
- package/dist/types/nodefony/interfaces/IEntity.d.ts +65 -0
- package/dist/types/nodefony/interfaces/IOrm.d.ts +136 -0
- package/dist/types/nodefony/interfaces/IOrmFlow.d.ts +71 -0
- package/dist/types/nodefony/interfaces/IOrmGraph.d.ts +165 -0
- package/dist/types/nodefony/interfaces/IOrmMigrations.d.ts +145 -0
- package/dist/types/nodefony/interfaces/IOrmProbe.d.ts +70 -0
- package/dist/types/nodefony/interfaces/IPage.d.ts +24 -0
- package/dist/types/nodefony/interfaces/IRepository.d.ts +369 -0
- package/dist/types/nodefony/interfaces/ITransaction.d.ts +31 -0
- package/dist/types/nodefony/interfaces/index.d.ts +7 -0
- package/dist/types/nodefony/src/AbstractCrudService.d.ts +158 -0
- package/dist/types/nodefony/src/ConnectionMonitor.d.ts +86 -0
- package/dist/types/nodefony/src/Entity.d.ts +44 -0
- package/dist/types/nodefony/src/EntityRegistry.d.ts +60 -0
- package/dist/types/nodefony/src/Orm.d.ts +197 -0
- package/dist/types/nodefony/src/OrmAdminApi.d.ts +84 -0
- package/dist/types/nodefony/src/OrmRegistry.d.ts +58 -0
- package/dist/types/nodefony/src/QueryFlowMonitor.d.ts +53 -0
- package/dist/types/nodefony/src/buildOrmLeanHealth.d.ts +15 -0
- package/dist/types/nodefony/src/criteria.d.ts +131 -0
- package/dist/types/nodefony/src/decorators/entitiesDecorator.d.ts +48 -0
- package/dist/types/nodefony/src/decorators/entityDecorator.d.ts +49 -0
- package/dist/types/nodefony/src/decorators/index.d.ts +10 -0
- package/dist/types/nodefony/src/decorators/metadataStore.d.ts +54 -0
- package/dist/types/nodefony/src/decorators/repositoryDecorator.d.ts +30 -0
- package/dist/types/nodefony/src/defineEntity.d.ts +43 -0
- package/dist/types/nodefony/src/errors.d.ts +62 -0
- package/dist/types/nodefony/src/ormWiring.d.ts +48 -0
- package/dist/types/nodefony/src/paginate.d.ts +43 -0
- package/dist/types/nodefony/src/readOptions.d.ts +21 -0
- package/dist/types/nodefony/src/serviceWiring.d.ts +24 -0
- package/docs/index.md +791 -0
- package/docs/tutorial-entity.md +577 -0
- package/package.json +73 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { InvalidOrderOption } from "./errors.js";
|
|
2
|
+
//#region nodefony/src/readOptions.ts
|
|
3
|
+
/**
|
|
4
|
+
* Décrit la FORME d'une valeur reçue, sans jamais en révéler le contenu.
|
|
5
|
+
*
|
|
6
|
+
* Le message d'erreur doit aider à corriger un appel, pas recopier une donnée
|
|
7
|
+
* applicative dans un journal.
|
|
8
|
+
*
|
|
9
|
+
* @param value - valeur observée.
|
|
10
|
+
* @returns une description courte du type (`an object`, `a string`, `null`…).
|
|
11
|
+
*/
|
|
12
|
+
const describeShape = (value) => {
|
|
13
|
+
if (value === null) return "null";
|
|
14
|
+
if (Array.isArray(value)) return "an array";
|
|
15
|
+
const t = typeof value;
|
|
16
|
+
return t === "object" ? "an object" : `a ${t}`;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Vérifie que l'option de lecture `order` respecte le contrat portable
|
|
20
|
+
* `Array<[string, "ASC" | "DESC"]>`, ou lève {@link InvalidOrderOption}.
|
|
21
|
+
*
|
|
22
|
+
* Source de vérité **unique** partagée par tous les adapters : chacun l'appelle une
|
|
23
|
+
* fois, en amont de la construction de sa requête, plutôt que de retester la forme à
|
|
24
|
+
* chaque endroit qui pose un tri. Sans elle, un `order` mal formé était sauté en
|
|
25
|
+
* silence (le test historique `options?.order?.length` est faux pour un objet) et la
|
|
26
|
+
* requête partait sans `ORDER BY`.
|
|
27
|
+
*
|
|
28
|
+
* Absence de tri — `undefined`, `null`, ou tableau vide — est acceptée : rien n'y est
|
|
29
|
+
* exprimé qui puisse être trahi. Seule une TENTATIVE de tri mal formée est refusée.
|
|
30
|
+
*
|
|
31
|
+
* Coût : sortie immédiate dans le cas dominant (`undefined`), aucune allocation sur le
|
|
32
|
+
* chemin nominal — la description n'est construite qu'au moment de lever.
|
|
33
|
+
*
|
|
34
|
+
* @param order - valeur de `options.order` telle que reçue de l'appelant.
|
|
35
|
+
* @param entity - nom logique de l'entité ciblée (diagnostic).
|
|
36
|
+
* @throws InvalidOrderOption si `order` est présent et n'est pas un tableau de couples valides.
|
|
37
|
+
*/
|
|
38
|
+
function assertOrderOption(order, entity) {
|
|
39
|
+
if (order === void 0 || order === null) return;
|
|
40
|
+
if (!Array.isArray(order)) throw new InvalidOrderOption(entity, describeShape(order));
|
|
41
|
+
for (let i = 0; i < order.length; i++) {
|
|
42
|
+
const pair = order[i];
|
|
43
|
+
if (!Array.isArray(pair) || pair.length !== 2) throw new InvalidOrderOption(entity, `pair #${i} = ${describeShape(pair)}${Array.isArray(pair) ? ` of length ${pair.length}` : ""}`);
|
|
44
|
+
if (typeof pair[0] !== "string") throw new InvalidOrderOption(entity, `pair #${i} field = ${describeShape(pair[0])}`);
|
|
45
|
+
if (pair[1] !== "ASC" && pair[1] !== "DESC") {
|
|
46
|
+
const got = typeof pair[1] === "string" ? `"${pair[1]}"` : describeShape(pair[1]);
|
|
47
|
+
throw new InvalidOrderOption(entity, `pair #${i} direction = ${got} (expected exactly "ASC" or "DESC")`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { assertOrderOption };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nodefony/orm-core` — fondation multi-ORM de Nodefony.
|
|
3
|
+
*
|
|
4
|
+
* Expose les contrats abstraits (`IOrm`, `IEntity`, `IRepository`,
|
|
5
|
+
* `ITransaction`) consommés par les adapters (`@nodefony/mongoose`,
|
|
6
|
+
* `@nodefony/drizzle`...). Lib pure : aucun runtime
|
|
7
|
+
* Module, pas d'enregistrement dans `@modules()`. Les drivers concrets sont les
|
|
8
|
+
* Modules ; ils s'enregistrent eux-mêmes dans le `OrmRegistry` à leur boot.
|
|
9
|
+
*/
|
|
10
|
+
export type { IOrm, IEntity, IEntityRelation, IRepository, OrmCriteria, Criteria, FieldCriteria, FieldOperators, UpdateData, UpdateOperators, RepositoryReadOptions, ITransaction, IPage, IPageQuery, PageQuery, } from "./nodefony/interfaces/index.js";
|
|
11
|
+
export { OPERATOR_KEYS, isFieldOperators, searchCriteria, LIKE_ESCAPE_CHAR, escapeLikeTerm, likePatternToRegExp, } from "./nodefony/src/criteria.js";
|
|
12
|
+
export type { OperatorKey } from "./nodefony/src/criteria.js";
|
|
13
|
+
export { UPDATE_OPERATOR_KEYS, isUpdateOperators, } from "./nodefony/src/criteria.js";
|
|
14
|
+
export type { UpdateOperatorKey } from "./nodefony/src/criteria.js";
|
|
15
|
+
export { UnknownCriteriaField, InvalidOrderOption, } from "./nodefony/src/errors.js";
|
|
16
|
+
export { assertOrderOption } from "./nodefony/src/readOptions.js";
|
|
17
|
+
export { OrmRegistry, ormRegistry } from "./nodefony/src/OrmRegistry.js";
|
|
18
|
+
export { EntityRegistry, entityRegistry } from "./nodefony/src/EntityRegistry.js";
|
|
19
|
+
export { Orm } from "./nodefony/src/Orm.js";
|
|
20
|
+
export { Entity } from "./nodefony/src/Entity.js";
|
|
21
|
+
export { AbstractCrudService } from "./nodefony/src/AbstractCrudService.js";
|
|
22
|
+
export type { ServiceWiring } from "./nodefony/src/serviceWiring.js";
|
|
23
|
+
export { paginate } from "./nodefony/src/paginate.js";
|
|
24
|
+
export { buildOrmGraph, buildConnectionHealth, buildOrmFlow, toDbml, toJsonSchema, createOrmAdminApi, registerOrmAdminApi, } from "./nodefony/src/OrmAdminApi.js";
|
|
25
|
+
export { queryFlowMonitor } from "./nodefony/src/QueryFlowMonitor.js";
|
|
26
|
+
export { connectionMonitor } from "./nodefony/src/ConnectionMonitor.js";
|
|
27
|
+
export { buildOrmLeanHealth } from "./nodefony/src/buildOrmLeanHealth.js";
|
|
28
|
+
export { wireOrmAdminPlane, resolveOrmFlowEnabled, reportOrmBootLines, } from "./nodefony/src/ormWiring.js";
|
|
29
|
+
export type { ISlowQuery, IQueryFlow, IOrmFlowReport, } from "./nodefony/interfaces/IOrmFlow.js";
|
|
30
|
+
export type { IColumnInfo, IConnectionInfo, IRelationInfo, IEntityGraphNode, IOrmSummary, IOrmGraph, IConnectionError, IConnectionHealth, } from "./nodefony/interfaces/IOrmGraph.js";
|
|
31
|
+
export type { ILatencyWindow, IOrmStorageProbe, IOrmPoolProbe, IOrmProbe, } from "./nodefony/interfaces/IOrmProbe.js";
|
|
32
|
+
export type { IOrmMigrationAction, IOrmMigrationEntry, IOrmMigrationSource, IOrmMigrationStatus, IOrmMigrationFailure, IOrmMigrationReply, IOrmPendingMigration, IOrmMigrationPlan, IOrmMigrationPlanReply, IOrmMigrationApplied, IOrmMigrationApplyReply, } from "./nodefony/interfaces/IOrmMigrations.js";
|
|
33
|
+
export { isMigrationFailure } from "./nodefony/interfaces/IOrmMigrations.js";
|
|
34
|
+
export { entity, entities, DEFAULT_CONNECTOR, repository, } from "./nodefony/src/decorators/index.js";
|
|
35
|
+
export { defineEntity } from "./nodefony/src/defineEntity.js";
|
|
36
|
+
export type { IEntityDefinition } from "./nodefony/src/defineEntity.js";
|
|
37
|
+
export type { EntitiesOptions } from "./nodefony/src/decorators/index.js";
|
|
38
|
+
export { getEntityMeta, hasEntityMeta, getRepositoryMeta, hasRepositoryMeta, } from "./nodefony/src/decorators/index.js";
|
|
39
|
+
export type { EntityOptions, RepositoryOptions, EntityMetadata, RepositoryMetadata, DecoratedClass, } from "./nodefony/src/decorators/index.js";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relation déclarative entre deux entités, indépendante du driver ORM.
|
|
3
|
+
*/
|
|
4
|
+
export interface IEntityRelation {
|
|
5
|
+
/** Cardinalité de la relation. */
|
|
6
|
+
readonly type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
|
|
7
|
+
/** Nom logique de l'entité cible (clé de lookup dans le registre). */
|
|
8
|
+
readonly target: string;
|
|
9
|
+
/** Champ portant la relation côté entité courante. */
|
|
10
|
+
readonly field: string;
|
|
11
|
+
/**
|
|
12
|
+
* Clé étrangère explicite. Si omise, l'adapter en dérive une déterministe
|
|
13
|
+
* (camelCase `<entité>Id`) — évite la divergence avec le défaut du driver
|
|
14
|
+
* (certains ORM génèrent du PascalCase `UserId`).
|
|
15
|
+
*/
|
|
16
|
+
readonly foreignKey?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Description d'une entité enregistrée dans le registre cross-ORM.
|
|
20
|
+
*
|
|
21
|
+
* Une même entité logique (ex. `User`) peut exister sur plusieurs connecteurs :
|
|
22
|
+
* `connector` désigne la connexion nommée cible (celle déclarée dans
|
|
23
|
+
* `connectors: { … }` de la config) et `schema`/`model` portent la définition
|
|
24
|
+
* propre au driver (schéma Mongoose, schéma Drizzle...). `model` n'est
|
|
25
|
+
* disponible qu'après connexion de l'ORM (compilation du modèle).
|
|
26
|
+
*
|
|
27
|
+
* Le mot désigne le **rôle** : une entité est liée à une *connexion*, jamais à
|
|
28
|
+
* un ORM — l'ORM (Drizzle, Mongoose) est le *driver* qui sert cette connexion.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam S - type du schéma natif du driver.
|
|
31
|
+
* @typeParam M - type du modèle compilé natif du driver.
|
|
32
|
+
*/
|
|
33
|
+
export interface IEntity<S = unknown, M = unknown> {
|
|
34
|
+
/** Nom logique de l'entité (clé de lookup, ex. `"User"`). */
|
|
35
|
+
readonly name: string;
|
|
36
|
+
/** Connexion nommée cible, telle que déclarée en config (ex. `"default"`). */
|
|
37
|
+
readonly connector: string;
|
|
38
|
+
/**
|
|
39
|
+
* Module Nodefony propriétaire de l'entité (ex. `"user"`, `"test"`).
|
|
40
|
+
* **Optionnel** : sert au regroupement dans le graphe canonique / ERD Studio.
|
|
41
|
+
* Non renseigné → entité non rattachée (groupe « — » côté UI).
|
|
42
|
+
*/
|
|
43
|
+
readonly module?: string;
|
|
44
|
+
/**
|
|
45
|
+
* **Classification** de l'entité (ex. domaine fonctionnel `"facturation"`,
|
|
46
|
+
* `"comptabilité"`) — axe de regroupement **distinct** du `module`
|
|
47
|
+
* (propriété/qui enregistre). Indispensable pour rendre lisible une **grosse
|
|
48
|
+
* base** (centaines de tables d'un même module) : l'ERD groupe/filtre par
|
|
49
|
+
* `domain`. **Optionnel** ; non renseigné → retombe sur `module`.
|
|
50
|
+
*/
|
|
51
|
+
readonly domain?: string;
|
|
52
|
+
/** Définition de schéma propre au driver (forme libre). */
|
|
53
|
+
readonly schema: S;
|
|
54
|
+
/** Modèle compilé natif, renseigné après connexion de l'ORM. */
|
|
55
|
+
model?: M;
|
|
56
|
+
/**
|
|
57
|
+
* Active la gestion **automatique** des horodatages `createdAt`/`updatedAt` par
|
|
58
|
+
* l'adapter qui la supporte nativement (Mongoose `timestamps: true`). Les ORM
|
|
59
|
+
* schema-as-code (Drizzle) les expriment en colonnes → ce flag y est sans effet.
|
|
60
|
+
* **Optionnel**, défaut `false`.
|
|
61
|
+
*/
|
|
62
|
+
readonly timestamps?: boolean;
|
|
63
|
+
/** Relations déclarées vers d'autres entités (par nom logique). */
|
|
64
|
+
readonly relations?: ReadonlyArray<IEntityRelation>;
|
|
65
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { IRepository } from "./IRepository.js";
|
|
2
|
+
import type { ITransaction } from "./ITransaction.js";
|
|
3
|
+
import type { IColumnInfo, IConnectionInfo } from "./IOrmGraph.js";
|
|
4
|
+
import type { IOrmProbe } from "./IOrmProbe.js";
|
|
5
|
+
import type { IOrmMigrationApplyReply, IOrmMigrationPlanReply, IOrmMigrationReply } from "./IOrmMigrations.js";
|
|
6
|
+
/**
|
|
7
|
+
* Contrat d'une instance ORM gérée par le framework (une par connexion logique).
|
|
8
|
+
*
|
|
9
|
+
* Implémenté par chaque adapter (`@nodefony/mongoose`, `@nodefony/drizzle`...) et enregistré dans `OrmRegistry` sous un nom unique
|
|
10
|
+
* (ex. `"db_principale"`, `"db_logs"`) pour le support multi-ORM simultané.
|
|
11
|
+
*/
|
|
12
|
+
export interface IOrm {
|
|
13
|
+
/** Nom unique de l'instance dans le registre (clé `OrmRegistry.get`). */
|
|
14
|
+
readonly name: string;
|
|
15
|
+
/** Établit la connexion sous-jacente et compile les entités enregistrées. */
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
/** Ferme proprement la connexion et libère le pool. */
|
|
18
|
+
disconnect(): Promise<void>;
|
|
19
|
+
/** Indique si la connexion est actuellement active. */
|
|
20
|
+
isConnected(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Retourne le repository typé d'une entité enregistrée.
|
|
23
|
+
*
|
|
24
|
+
* @param name - nom logique de l'entité (ex. `"User"`).
|
|
25
|
+
* @typeParam T - type de l'entité gérée.
|
|
26
|
+
* @returns le repository CRUD de l'entité.
|
|
27
|
+
* @throws si aucune entité de ce nom n'est enregistrée pour cet ORM.
|
|
28
|
+
*/
|
|
29
|
+
getRepository<T = unknown>(name: string): IRepository<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Exécute un travail dans une transaction : commit si résolu, rollback si rejeté.
|
|
32
|
+
*
|
|
33
|
+
* @param work - callback recevant la transaction active.
|
|
34
|
+
* @typeParam R - type du résultat retourné par le travail.
|
|
35
|
+
*/
|
|
36
|
+
transaction<R>(work: (tx: ITransaction) => Promise<R>): Promise<R>;
|
|
37
|
+
/**
|
|
38
|
+
* Expose la connexion native du driver (trappe SQL/commandes brutes).
|
|
39
|
+
*
|
|
40
|
+
* Anti-blocage indispensable : autorise une requête brute non couverte par
|
|
41
|
+
* l'abstraction (tag `sql` Drizzle, `connection` Mongoose, etc.) — typiquement
|
|
42
|
+
* une projection de colonnes, une CTE ou une agrégation.
|
|
43
|
+
*
|
|
44
|
+
* **Passer le type de l'adapter, jamais rien.** Le paramètre retombe sur
|
|
45
|
+
* `unknown` s'il est omis, et la seule issue devient alors un `as any` : c'est
|
|
46
|
+
* le contraire de ce que cette trappe cherche à offrir. Le type vient de
|
|
47
|
+
* l'adapter employé — `DrizzleDb` exporté par `@nodefony/drizzle`,
|
|
48
|
+
* `Connection` du paquet `mongoose` pour `@nodefony/mongoose` :
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* import type { DrizzleDb } from "@nodefony/drizzle";
|
|
52
|
+
* const db = orm.getNativeConnection<DrizzleDb>();
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @typeParam C - type natif attendu, exporté par l'adapter employé.
|
|
56
|
+
*/
|
|
57
|
+
getNativeConnection<C = unknown>(): C;
|
|
58
|
+
/**
|
|
59
|
+
* Décrit les colonnes normalisées d'une entité pour le graphe canonique
|
|
60
|
+
* (data plane ORM / ERD / contexte IA). **Optionnel** : un adapter qui ne
|
|
61
|
+
* l'implémente pas laisse le graphe sans colonnes (relations seules).
|
|
62
|
+
*
|
|
63
|
+
* @param name - nom logique de l'entité.
|
|
64
|
+
* @returns colonnes normalisées, ou `[]` si inconnu/non implémenté.
|
|
65
|
+
*/
|
|
66
|
+
describeEntity?(name: string): IColumnInfo[];
|
|
67
|
+
/**
|
|
68
|
+
* Décrit la connexion sous-jacente (driver + cible lisible) pour le data plane
|
|
69
|
+
* ORM / dashboard. **Optionnel** : un adapter qui ne l'implémente pas laisse le
|
|
70
|
+
* connecteur sans détail de connexion. Ne DOIT jamais exposer de credential
|
|
71
|
+
* (mot de passe retiré de la cible).
|
|
72
|
+
*
|
|
73
|
+
* @returns infos de connexion, ou `undefined` si non implémenté.
|
|
74
|
+
*/
|
|
75
|
+
describeConnection?(): IConnectionInfo;
|
|
76
|
+
/**
|
|
77
|
+
* Ping bas-coût de la connexion (round-trip réel vers la base) pour le
|
|
78
|
+
* diagnostic du data plane. **Optionnel** : un adapter qui ne l'implémente pas
|
|
79
|
+
* laisse le diagnostic mesurer une latence `null` (état dérivé d'`isConnected`).
|
|
80
|
+
* SQL → `SELECT 1` ; Mongo → `admin().ping`. Doit **rejeter** si la base ne
|
|
81
|
+
* répond pas (la latence et l'erreur alimentent le moniteur de connexion).
|
|
82
|
+
*
|
|
83
|
+
* @throws si la base est injoignable.
|
|
84
|
+
*/
|
|
85
|
+
ping?(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Sonde profonde driver-spécifique (stockage, pool…) pour le contrôle total
|
|
88
|
+
* des ORM via le hub temps réel. **Optionnel** : un adapter qui ne l'implémente
|
|
89
|
+
* pas ne rapporte que les métriques génériques (latence, cycle de vie).
|
|
90
|
+
* Best-effort : ne DOIT jamais throw (retourner un objet partiel/vide).
|
|
91
|
+
*
|
|
92
|
+
* @returns métriques driver, ou objet vide si rien à rapporter.
|
|
93
|
+
*/
|
|
94
|
+
probe?(): Promise<IOrmProbe>;
|
|
95
|
+
/**
|
|
96
|
+
* État des migrations de ce connecteur — **capacité optionnelle**, et son
|
|
97
|
+
* ABSENCE est une réponse.
|
|
98
|
+
*
|
|
99
|
+
* Un ORM qui ne l'implémente pas ne porte pas de migrations par fichiers
|
|
100
|
+
* versionnés : le plan d'administration le DIT en le nommant, plutôt que de
|
|
101
|
+
* rendre une page vide. Les autres bases résorbent l'écart entre le code et
|
|
102
|
+
* le schéma autrement — la question est la même, la réponse n'est pas la
|
|
103
|
+
* même.
|
|
104
|
+
*
|
|
105
|
+
* ⚠️ **Lecture seule, et sans privilège emprunté** : cette méthode ne doit
|
|
106
|
+
* rien appliquer, et ne doit pas emprunter le compte réservé au travail de
|
|
107
|
+
* migration. Un serveur qui répond à une requête d'administration n'a aucune
|
|
108
|
+
* raison de détenir le droit de modifier un schéma.
|
|
109
|
+
*
|
|
110
|
+
* @returns l'état, ou l'empêchement qui explique pourquoi il n'y en a pas.
|
|
111
|
+
*/
|
|
112
|
+
migrationStatus?(): Promise<IOrmMigrationReply>;
|
|
113
|
+
/**
|
|
114
|
+
* Ce qui S'APPLIQUERAIT, avec son SQL — **capacité optionnelle**, lecture
|
|
115
|
+
* seule.
|
|
116
|
+
*
|
|
117
|
+
* Sert la confirmation d'un geste d'application : on ne confirme pas une
|
|
118
|
+
* modification de schéma sur une promesse, on la confirme sur ce qu'elle va
|
|
119
|
+
* exécuter.
|
|
120
|
+
*
|
|
121
|
+
* @returns le plan, ou l'empêchement.
|
|
122
|
+
*/
|
|
123
|
+
migrationPlan?(): Promise<IOrmMigrationPlanReply>;
|
|
124
|
+
/**
|
|
125
|
+
* Applique les migrations en attente — **capacité optionnelle, et ÉCRIVANTE**.
|
|
126
|
+
*
|
|
127
|
+
* ⚠️ Réservée au DÉVELOPPEMENT par l'implémentation, qui doit refuser
|
|
128
|
+
* ailleurs en le disant. Ce n'est pas une précaution d'interface : en
|
|
129
|
+
* production, les migrations s'appliquent dans un travail d'orchestrateur qui
|
|
130
|
+
* se termine AVANT que le premier nouvel exemplaire ne démarre — pas au clic
|
|
131
|
+
* de quelqu'un qui regarde une console pendant que le trafic passe.
|
|
132
|
+
*
|
|
133
|
+
* @returns ce qui a été appliqué, ou l'empêchement.
|
|
134
|
+
*/
|
|
135
|
+
applyMigrations?(): Promise<IOrmMigrationApplyReply>;
|
|
136
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sonde **flux ORM** — débit de requêtes vers la base, agrégé process-wide et
|
|
3
|
+
* **indépendant de l'ALS** (≠ profiler par-requête de la debug bar).
|
|
4
|
+
*
|
|
5
|
+
* Observe « combien de requêtes/s, à quelle latence, et lesquelles sont lentes »
|
|
6
|
+
* pour le panneau Supervision Studio (patron sondes+hub). Alimenté par le même
|
|
7
|
+
* tap que le profiler ({@link IOrm} adapters), mais via un compteur global
|
|
8
|
+
* ({@link QueryFlowMonitor}) au lieu du buffer de scope — donc visible sans
|
|
9
|
+
* requête HTTP en cours.
|
|
10
|
+
*
|
|
11
|
+
* Perf : le débit instantané (queries/s) n'est **pas** stocké ici ; il est dérivé
|
|
12
|
+
* côté consommateur à partir du delta de {@link IQueryFlow.total} entre deux
|
|
13
|
+
* lectures (même technique que le CPU%) → 0 état mutable à la lecture, robuste
|
|
14
|
+
* même quand l'event-loop dérape (le delta couvre alors une fenêtre plus large).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Une requête « lente » capturée (au-delà du seuil). Le SQL est **paramétré**
|
|
18
|
+
* (placeholders, jamais les valeurs) et redacté → 0 credential. Capturé
|
|
19
|
+
* uniquement sur le chemin lent (rare) pour ne rien coûter au cas nominal.
|
|
20
|
+
*/
|
|
21
|
+
export interface ISlowQuery {
|
|
22
|
+
/** Horodatage de la requête (ms epoch). */
|
|
23
|
+
ts: number;
|
|
24
|
+
/** Durée mesurée (ms). */
|
|
25
|
+
durationMs: number;
|
|
26
|
+
/** Connecteur ORM (clé du registre). */
|
|
27
|
+
connector: string;
|
|
28
|
+
/** SQL paramétré et redacté (absent si l'adapter ne sait pas l'extraire). */
|
|
29
|
+
sql?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compteurs de flux d'UN connecteur ORM. Cumuls depuis le boot (le débit/s se
|
|
33
|
+
* dérive du delta de `total`). Latence en deux vues : moyenne sur la vie du
|
|
34
|
+
* process (`avgMs`) et EWMA lissée (`ewmaMs`, suit les variations récentes).
|
|
35
|
+
*/
|
|
36
|
+
export interface IQueryFlow {
|
|
37
|
+
/** Connecteur ORM (clé du registre, ex. `"default"`). */
|
|
38
|
+
connector: string;
|
|
39
|
+
/** Vendor de l'adapter (`drizzle`, `mongoose`), `""` si inconnu. */
|
|
40
|
+
vendor: string;
|
|
41
|
+
/** Requêtes mesurées depuis le boot (le débit/s = Δtotal / Δts côté lecteur). */
|
|
42
|
+
total: number;
|
|
43
|
+
/** Latence moyenne sur la vie du process (ms), `null` si aucune requête. */
|
|
44
|
+
avgMs: number | null;
|
|
45
|
+
/** Latence EWMA lissée (ms) — suit les variations récentes, `null` si aucune. */
|
|
46
|
+
ewmaMs: number | null;
|
|
47
|
+
/** Dernière latence mesurée (ms), `null` si aucune. */
|
|
48
|
+
lastMs: number | null;
|
|
49
|
+
/** Pire latence observée (ms), `0` si aucune. */
|
|
50
|
+
maxMs: number;
|
|
51
|
+
/** Requêtes classées lentes depuis le boot (≥ seuil). */
|
|
52
|
+
slowTotal: number;
|
|
53
|
+
/** Ring borné des requêtes lentes récentes (la plus récente en tête). */
|
|
54
|
+
slow: ISlowQuery[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Rapport de flux complet — un par instance (cloud-native : per-process). Le
|
|
58
|
+
* débit/s n'y figure pas (dérivé du delta de `total` entre deux `ts`).
|
|
59
|
+
*/
|
|
60
|
+
export interface IOrmFlowReport {
|
|
61
|
+
/** La sonde est-elle active ? `false` en production par défaut (coût nul). */
|
|
62
|
+
enabled: boolean;
|
|
63
|
+
/** Horodatage du rapport (ms epoch) — sert au calcul du débit (Δts). */
|
|
64
|
+
ts: number;
|
|
65
|
+
/** Identifiant de l'instance (pid, ou `NF_INSTANCE_ID`). */
|
|
66
|
+
instanceId: string;
|
|
67
|
+
/** Seuil « lent » courant (ms) — au-delà, la requête est capturée. */
|
|
68
|
+
slowMs: number;
|
|
69
|
+
/** Flux par connecteur ORM enregistré. */
|
|
70
|
+
connectors: IQueryFlow[];
|
|
71
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Représentation **canonique et sérialisable** du modèle de données multi-ORM.
|
|
3
|
+
*
|
|
4
|
+
* C'est la pièce maîtresse « IA-first » du data plane ORM : un graphe normalisé
|
|
5
|
+
* (ORM-agnostique) qui sert à la fois :
|
|
6
|
+
* - la **visualisation** (ERD React Flow côté Studio) ;
|
|
7
|
+
* - l'**IA** (contexte d'un agent : text-to-SQL, RAG schéma-aware) ;
|
|
8
|
+
* - l'**interop** (export DBML / JSON Schema / SQL DDL).
|
|
9
|
+
*
|
|
10
|
+
* Une source, plusieurs consommateurs. Le diagramme n'est qu'une projection.
|
|
11
|
+
*/
|
|
12
|
+
import type { ILatencyWindow, IOrmStorageProbe, IOrmPoolProbe } from "./IOrmProbe.js";
|
|
13
|
+
/** Colonne/champ normalisé d'une entité (extrait via {@link IOrm.describeEntity}). */
|
|
14
|
+
export interface IColumnInfo {
|
|
15
|
+
/** Nom de la colonne. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Type natif tel que rapporté par le driver (ex. `text`, `integer`, `String`). */
|
|
18
|
+
type: string;
|
|
19
|
+
/** `true` si clé primaire. */
|
|
20
|
+
primaryKey: boolean;
|
|
21
|
+
/** `true` si la colonne accepte `NULL`. */
|
|
22
|
+
nullable: boolean;
|
|
23
|
+
/** `true` si contrainte d'unicité. */
|
|
24
|
+
unique: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Relation projetée pour le graphe (sous-ensemble sérialisable de `IEntityRelation`). */
|
|
27
|
+
export interface IRelationInfo {
|
|
28
|
+
/** Cardinalité. */
|
|
29
|
+
type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
|
|
30
|
+
/** Entité cible (nom logique). */
|
|
31
|
+
target: string;
|
|
32
|
+
/** Champ portant la relation côté entité courante. */
|
|
33
|
+
field: string;
|
|
34
|
+
/** Clé étrangère (explicite ou dérivée déterministe). */
|
|
35
|
+
foreignKey?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Nœud du graphe : une entité avec ses colonnes et ses relations. */
|
|
38
|
+
export interface IEntityGraphNode {
|
|
39
|
+
/** Nom logique de l'entité. */
|
|
40
|
+
name: string;
|
|
41
|
+
/** Connexion nommée qui porte l'entité (clé de `connectors` en config). */
|
|
42
|
+
connector: string;
|
|
43
|
+
/**
|
|
44
|
+
* Module Nodefony propriétaire (regroupement ERD), `""` si non rattaché.
|
|
45
|
+
*/
|
|
46
|
+
module: string;
|
|
47
|
+
/**
|
|
48
|
+
* Classification (domaine fonctionnel) — axe de regroupement ERD distinct du
|
|
49
|
+
* `module`, `""` si non renseigné. Rend une grosse base navigable.
|
|
50
|
+
*/
|
|
51
|
+
domain: string;
|
|
52
|
+
/** Colonnes normalisées (vide si l'adapter n'implémente pas `describeEntity`). */
|
|
53
|
+
columns: IColumnInfo[];
|
|
54
|
+
/** Relations déclarées. */
|
|
55
|
+
relations: IRelationInfo[];
|
|
56
|
+
}
|
|
57
|
+
/** Connexion sous-jacente d'un ORM (extrait via {@link IOrm.describeConnection}). */
|
|
58
|
+
export interface IConnectionInfo {
|
|
59
|
+
/**
|
|
60
|
+
* Base/driver sous-jacent en minuscules (`sqlite`, `postgres`, `mysql`,
|
|
61
|
+
* `mariadb`, `mongodb`…) — sert à choisir le logo et qualifier le connecteur.
|
|
62
|
+
*/
|
|
63
|
+
driver: string;
|
|
64
|
+
/**
|
|
65
|
+
* Cible lisible : chemin du fichier (SQLite, **relatif** — jamais d'absolu),
|
|
66
|
+
* `host:port/base` (serveur) ou URI redactée. **Jamais de credential** (mot de
|
|
67
|
+
* passe retiré côté adapter).
|
|
68
|
+
*/
|
|
69
|
+
target?: string;
|
|
70
|
+
/** Version du moteur/base (ex. SQLite `3.45.1`), si l'adapter peut l'obtenir. */
|
|
71
|
+
version?: string;
|
|
72
|
+
/** Version de la lib ORM elle-même (ex. drizzle-orm `0.44.x`), si connue. */
|
|
73
|
+
ormVersion?: string;
|
|
74
|
+
}
|
|
75
|
+
/** Résumé d'un ORM/connecteur enregistré. */
|
|
76
|
+
export interface IOrmSummary {
|
|
77
|
+
/** Clé du connecteur dans le `OrmRegistry`. */
|
|
78
|
+
name: string;
|
|
79
|
+
/**
|
|
80
|
+
* Vendor de l'adapter en minuscules (`drizzle`, `mongoose`…),
|
|
81
|
+
* dérivé du nom de classe. `""` si indéterminé. Dette : promouvoir en
|
|
82
|
+
* `IOrm.vendor` déclaré par chaque adapter (P7.1 industrialisation ORM).
|
|
83
|
+
*/
|
|
84
|
+
vendor: string;
|
|
85
|
+
/** `true` si c'est le connecteur par défaut (`"default"`). */
|
|
86
|
+
default: boolean;
|
|
87
|
+
/** État de la connexion. */
|
|
88
|
+
connected: boolean;
|
|
89
|
+
/** Nombre d'entités rattachées à cet ORM. */
|
|
90
|
+
entityCount: number;
|
|
91
|
+
/** Connexion sous-jacente (driver + cible), si l'adapter l'expose. */
|
|
92
|
+
connection?: IConnectionInfo;
|
|
93
|
+
}
|
|
94
|
+
/** Erreur de connexion horodatée (message **redacté** — jamais de credential). */
|
|
95
|
+
export interface IConnectionError {
|
|
96
|
+
/** Message d'erreur (driver), credential déjà retiré. */
|
|
97
|
+
message: string;
|
|
98
|
+
/** Horodatage epoch ms. */
|
|
99
|
+
ts: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Diagnostic d'un connecteur — réponse de `/nodefony/orm/api/connection/health`.
|
|
103
|
+
* Combine l'état figé ({@link IConnectionInfo}), les compteurs de cycle de vie
|
|
104
|
+
* (connexions, **reconnexions**, **erreurs**) du moniteur, et un **ping live**
|
|
105
|
+
* (round-trip réel mesuré à la requête).
|
|
106
|
+
*/
|
|
107
|
+
export interface IConnectionHealth {
|
|
108
|
+
/**
|
|
109
|
+
* Identité de l'**instance** (process) qui rapporte — cloud-native : le
|
|
110
|
+
* diagnostic est per-pod (pool DB local au process). Vue multi-pod = agrégation
|
|
111
|
+
* externe (Prometheus / fan-out Redis P13).
|
|
112
|
+
*/
|
|
113
|
+
instanceId: string;
|
|
114
|
+
/** Clé du connecteur. */
|
|
115
|
+
name: string;
|
|
116
|
+
/** Vendor ORM (`drizzle`, `mongoose`…). */
|
|
117
|
+
vendor: string;
|
|
118
|
+
/** Base/driver (`sqlite`, `mongodb`…). */
|
|
119
|
+
driver: string;
|
|
120
|
+
/** Cible lisible (chemin relatif / host:port), jamais de credential. */
|
|
121
|
+
target?: string;
|
|
122
|
+
/** Version moteur. */
|
|
123
|
+
version?: string;
|
|
124
|
+
/** Version lib ORM. */
|
|
125
|
+
ormVersion?: string;
|
|
126
|
+
/** État courant (`isConnected`). */
|
|
127
|
+
connected: boolean;
|
|
128
|
+
/** Connecté depuis (epoch ms), `null` si jamais connecté. */
|
|
129
|
+
connectedSince: number | null;
|
|
130
|
+
/** Durée depuis la dernière connexion réussie (ms), `null` si jamais. */
|
|
131
|
+
uptimeMs: number | null;
|
|
132
|
+
/** Nombre total de connexions réussies. */
|
|
133
|
+
connectCount: number;
|
|
134
|
+
/** Reconnexions (connexions au-delà de la première). */
|
|
135
|
+
reconnectCount: number;
|
|
136
|
+
/** Nombre total d'erreurs enregistrées (connexion + ping). */
|
|
137
|
+
errorCount: number;
|
|
138
|
+
/** Dernière erreur, `null` si aucune. */
|
|
139
|
+
lastError: IConnectionError | null;
|
|
140
|
+
/** Erreurs récentes (ring borné, plus récentes d'abord). */
|
|
141
|
+
recentErrors: IConnectionError[];
|
|
142
|
+
/** Latence de la dernière connexion réussie (ms), `null` si inconnue. */
|
|
143
|
+
lastConnectMs: number | null;
|
|
144
|
+
/** Latence du ping live (ms), `null` si non pingable / déconnecté. */
|
|
145
|
+
pingMs: number | null;
|
|
146
|
+
/** `true` si le ping live a réussi. */
|
|
147
|
+
pingOk: boolean;
|
|
148
|
+
/** Message d'erreur du ping live, `null` si OK. */
|
|
149
|
+
pingError: string | null;
|
|
150
|
+
/** Fenêtre glissante de latence (min/moy/max sur les N derniers pings). */
|
|
151
|
+
latency: ILatencyWindow;
|
|
152
|
+
/** Sonde de stockage (driver), si l'adapter l'expose. */
|
|
153
|
+
storage?: IOrmStorageProbe;
|
|
154
|
+
/** Sonde de pool de connexions (driver), si l'adapter l'expose. */
|
|
155
|
+
pool?: IOrmPoolProbe;
|
|
156
|
+
/** Métriques driver libres (clé→valeur). */
|
|
157
|
+
extra?: Record<string, string | number | boolean>;
|
|
158
|
+
}
|
|
159
|
+
/** Graphe complet du modèle de données — réponse de `/nodefony/orm/api/graph`. */
|
|
160
|
+
export interface IOrmGraph {
|
|
161
|
+
/** ORMs/connecteurs enregistrés. */
|
|
162
|
+
orms: IOrmSummary[];
|
|
163
|
+
/** Entités (colonnes + relations), éventuellement filtrées par ORM. */
|
|
164
|
+
entities: IEntityGraphNode[];
|
|
165
|
+
}
|