@nodefony/drizzle 10.0.0-alpha.1 → 10.0.0-alpha.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/dist/nodefony/command/migrateShared.js +15 -1
- package/dist/nodefony/service/DrizzleService.js +24 -8
- package/dist/nodefony/src/orm-core/DrizzleOrm.js +24 -5
- package/dist/types/nodefony/command/migrateShared.d.ts +11 -0
- package/dist/types/nodefony/service/DrizzleService.d.ts +9 -2
- package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +0 -18
- package/package.json +13 -13
|
@@ -4,7 +4,7 @@ import { missingHistoryColumn } from "../src/migrator/history.js";
|
|
|
4
4
|
import { buildMigrator, readMigrationEnv, resolveConnector } from "../src/migrator/resolve.js";
|
|
5
5
|
import { MigrationToolError, describeResolutionRefusal, moduleAbsent } from "../src/migrator/refusals.js";
|
|
6
6
|
import { composeReport } from "../src/migrator/status.js";
|
|
7
|
-
import { Command, resolveColorEnabled } from "nodefony";
|
|
7
|
+
import { CONSOLE_DATA_RUN_PROFILE, Command, resolveColorEnabled } from "nodefony";
|
|
8
8
|
//#region nodefony/command/migrateShared.ts
|
|
9
9
|
/**
|
|
10
10
|
* Socle commun des commandes `orm:*` — ce qui garantit qu'aucune d'elles ne
|
|
@@ -42,6 +42,20 @@ const MODULE_NAME = "drizzle";
|
|
|
42
42
|
* différence entre un outil qu'on sait utiliser et un outil qu'on subit.
|
|
43
43
|
*/
|
|
44
44
|
var OrmMigrateCommand = class extends Command {
|
|
45
|
+
/**
|
|
46
|
+
* Toutes les commandes `orm:*` parlent à la base — c'est leur définition.
|
|
47
|
+
*
|
|
48
|
+
* Déclaré ICI et pas dans le littéral d'options de chacune : la déclaration
|
|
49
|
+
* appartient à la FAMILLE, et une famille qui se déclare six fois finit par
|
|
50
|
+
* oublier la septième — l'oubli serait alors muet (la commande démarrerait
|
|
51
|
+
* sans connexion et ne trouverait pas de quoi migrer). Posé après `super()`,
|
|
52
|
+
* qui a déjà lu `options.runProfile` : une commande qui veut un profil autre
|
|
53
|
+
* peut donc encore le passer dans ses options, et il gagne.
|
|
54
|
+
*/
|
|
55
|
+
constructor(...args) {
|
|
56
|
+
super(...args);
|
|
57
|
+
this.runProfile ??= { ...CONSOLE_DATA_RUN_PROFILE };
|
|
58
|
+
}
|
|
45
59
|
/**
|
|
46
60
|
* Faut-il colorer la sortie ?
|
|
47
61
|
*
|
|
@@ -7,8 +7,8 @@ import { describeDivergence } from "../src/migrator/divergence.js";
|
|
|
7
7
|
import { DrizzleOrm } from "../src/orm-core/DrizzleOrm.js";
|
|
8
8
|
import "../src/orm-core/index.js";
|
|
9
9
|
import { dataLoss, renderDestructive, scanDestructive, summarizeDestructive } from "../src/migrator/destructive.js";
|
|
10
|
-
import { BootConfigurationError, Service } from "nodefony";
|
|
11
|
-
import { queryFlowMonitor, resolveOrmFlowEnabled } from "@nodefony/orm-core";
|
|
10
|
+
import { BootConfigurationError, Service, runNeedsExternalServices } from "nodefony";
|
|
11
|
+
import { diagnoseConnectionFailure, parseConnectionTarget, queryFlowMonitor, resolveOrmFlowEnabled } from "@nodefony/orm-core";
|
|
12
12
|
import fs from "node:fs";
|
|
13
13
|
import path from "node:path";
|
|
14
14
|
//#region nodefony/service/DrizzleService.ts
|
|
@@ -84,7 +84,7 @@ var DrizzleService = class extends Service {
|
|
|
84
84
|
this.module = module;
|
|
85
85
|
this.module.hookKernel("onBoot", async () => {
|
|
86
86
|
queryFlowMonitor.setEnabled(resolveOrmFlowEnabled(this.kernel));
|
|
87
|
-
await this.connectAll().catch((e) => {
|
|
87
|
+
await this.connectAll(runNeedsExternalServices(this.kernel)).catch((e) => {
|
|
88
88
|
this.log(e, "ERROR");
|
|
89
89
|
throw e;
|
|
90
90
|
});
|
|
@@ -98,10 +98,17 @@ var DrizzleService = class extends Service {
|
|
|
98
98
|
#config() {
|
|
99
99
|
return this.module.config;
|
|
100
100
|
}
|
|
101
|
-
/**
|
|
102
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Connecte tous les connecteurs déclarés en config (validée Zod).
|
|
103
|
+
*
|
|
104
|
+
* @param connect - `false` pour ENREGISTRER les ORM sans ouvrir de connexion.
|
|
105
|
+
* Réservé au boot d'un run qui n'a pas déclaré `externalServices` : l'ORM
|
|
106
|
+
* doit exister (c'est lui qui publie le plan d'administration) sans qu'un
|
|
107
|
+
* socket ne s'ouvre. Le défaut `true` fait de l'appel direct un ordre.
|
|
108
|
+
*/
|
|
109
|
+
async connectAll(connect = true) {
|
|
103
110
|
const connectors = this.#config()?.connectors ?? {};
|
|
104
|
-
for (const [name, cfg] of Object.entries(connectors)) await this.#connectOne(name, cfg);
|
|
111
|
+
for (const [name, cfg] of Object.entries(connectors)) await this.#connectOne(name, cfg, connect);
|
|
105
112
|
}
|
|
106
113
|
/**
|
|
107
114
|
* Chemin SQLite par défaut d'un connecteur, résolu AU BOOT (kernel présent —
|
|
@@ -116,7 +123,7 @@ var DrizzleService = class extends Service {
|
|
|
116
123
|
return defaultConnectorFilename(this.kernel, name);
|
|
117
124
|
}
|
|
118
125
|
/** Connecte un connecteur (crée le dossier de la base SQLite si nécessaire). */
|
|
119
|
-
async #connectOne(name, cfg) {
|
|
126
|
+
async #connectOne(name, cfg, connect = true) {
|
|
120
127
|
const dialect = cfg.dialect ?? "sqlite";
|
|
121
128
|
const ddl = resolveDdlMode(cfg.ddl, readMigrationEnv(this.kernel));
|
|
122
129
|
let filename;
|
|
@@ -146,11 +153,20 @@ var DrizzleService = class extends Service {
|
|
|
146
153
|
}
|
|
147
154
|
});
|
|
148
155
|
const target = dialect === "sqlite" ? filename : redactUrl(cfg.url);
|
|
156
|
+
if (!connect) {
|
|
157
|
+
this.#orms.set(name, orm);
|
|
158
|
+
this.log(`Drizzle « ${name} » enregistré SANS connexion (${dialect}: ${target}) — ce run n'a pas déclaré \`externalServices\`. Une commande qui lit ou écrit des données le déclare via son \`runProfile\` (CONSOLE_DATA_RUN_PROFILE).`, "DEBUG");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
149
161
|
try {
|
|
150
162
|
await orm.connect();
|
|
151
163
|
} catch (e) {
|
|
152
164
|
const cause = e instanceof Error ? e.message : String(e);
|
|
153
|
-
|
|
165
|
+
const diagnosis = diagnoseConnectionFailure(e, dialect === "sqlite" ? {
|
|
166
|
+
host: null,
|
|
167
|
+
port: null
|
|
168
|
+
} : parseConnectionTarget(cfg.url));
|
|
169
|
+
throw new BootConfigurationError(`Drizzle : le connecteur "${name}" (${dialect}: ${target}) n'a pas pu se connecter — ${diagnosis.explanation} Si l'adresse est la bonne, vérifier l'infrastructure déclarée (NF_DATABASE_URL / connectors) et que les entités sont portées sur ce dialecte, ou retirer le connecteur. Cause : ${cause}`, { cause: e });
|
|
154
170
|
}
|
|
155
171
|
this.#orms.set(name, orm);
|
|
156
172
|
this.log(`Drizzle « ${name} » connecté (${dialect}: ${target}) — schéma : ${ddl} ${DDL_EXPLAINED[ddl]}`, "INFO");
|
|
@@ -6,7 +6,7 @@ import { failureFrom } from "../migrator/status.js";
|
|
|
6
6
|
import { DrizzleRepository } from "./DrizzleRepository.js";
|
|
7
7
|
import { DrizzleTransaction } from "./DrizzleTransaction.js";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
|
-
import { Orm, entityRegistry } from "@nodefony/orm-core";
|
|
9
|
+
import { CONNECT_TIMEOUT_MS, Orm, entityRegistry, parseConnectionTarget, withConnectDeadline } from "@nodefony/orm-core";
|
|
10
10
|
import fs from "node:fs";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import { is } from "drizzle-orm";
|
|
@@ -50,6 +50,22 @@ function renderCheck(check, dialect) {
|
|
|
50
50
|
* Trappe SQL brut : {@link DrizzleOrm.getNativeConnection} expose le db Drizzle
|
|
51
51
|
* (tag `sql`) pour les jointures arbitraires (ADR-0003 risque #1).
|
|
52
52
|
*/
|
|
53
|
+
/**
|
|
54
|
+
* L'adresse visée, écrite pour un humain et SANS son secret.
|
|
55
|
+
*
|
|
56
|
+
* Une URL de connexion porte le mot de passe : la recopier telle quelle dans
|
|
57
|
+
* un message d'erreur le publie dans les journaux. `parseConnectionTarget` ne
|
|
58
|
+
* rend que l'hôte et le port — c'est tout ce dont on a besoin pour aller voir
|
|
59
|
+
* qui écoute.
|
|
60
|
+
*
|
|
61
|
+
* @param url - l'URL du connecteur, si elle existe.
|
|
62
|
+
* @returns `hôte:port`, ou une désignation neutre quand l'URL ne dit rien.
|
|
63
|
+
*/
|
|
64
|
+
function describeTarget(url) {
|
|
65
|
+
const { host, port } = parseConnectionTarget(url);
|
|
66
|
+
if (host && port !== null) return `${host}:${port}`;
|
|
67
|
+
return host ?? "configuré";
|
|
68
|
+
}
|
|
53
69
|
var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
54
70
|
#client = null;
|
|
55
71
|
/**
|
|
@@ -598,11 +614,13 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
598
614
|
const pool = new PoolCtor({
|
|
599
615
|
connectionString: this.#url,
|
|
600
616
|
keepAlive: true,
|
|
601
|
-
keepAliveInitialDelayMillis: 1e4
|
|
617
|
+
keepAliveInitialDelayMillis: 1e4,
|
|
618
|
+
connectionTimeoutMillis: CONNECT_TIMEOUT_MS,
|
|
619
|
+
query_timeout: CONNECT_TIMEOUT_MS
|
|
602
620
|
});
|
|
603
621
|
this.#wirePgLifecycle(pool);
|
|
604
622
|
try {
|
|
605
|
-
await pool.query("SELECT 1");
|
|
623
|
+
await withConnectDeadline(pool.query("SELECT 1"), `la réponse de postgres ${describeTarget(this.#url)}`);
|
|
606
624
|
} catch (e) {
|
|
607
625
|
this.#unwireAll();
|
|
608
626
|
await pool.end().catch(() => void 0);
|
|
@@ -778,14 +796,15 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
778
796
|
uri: this.#url,
|
|
779
797
|
timezone: "Z",
|
|
780
798
|
enableKeepAlive: true,
|
|
781
|
-
keepAliveInitialDelay: 1e4
|
|
799
|
+
keepAliveInitialDelay: 1e4,
|
|
800
|
+
connectTimeout: CONNECT_TIMEOUT_MS
|
|
782
801
|
});
|
|
783
802
|
this.#wireMysqlLifecycle(pool);
|
|
784
803
|
} catch (e) {
|
|
785
804
|
throw new Error(`DrizzleOrm "${this.name}": the mysql dialect needs the optional driver \`mysql2\` (run \`npm i mysql2\`). ${e.message}`, { cause: e });
|
|
786
805
|
}
|
|
787
806
|
try {
|
|
788
|
-
await pool.query("SELECT 1");
|
|
807
|
+
await withConnectDeadline(pool.query("SELECT 1"), `la réponse de mysql ${describeTarget(this.#url)}`);
|
|
789
808
|
} catch (e) {
|
|
790
809
|
this.#unwireAll();
|
|
791
810
|
await pool.end().catch(() => void 0);
|
|
@@ -21,6 +21,17 @@ export interface IMigrateSharedOptions {
|
|
|
21
21
|
* différence entre un outil qu'on sait utiliser et un outil qu'on subit.
|
|
22
22
|
*/
|
|
23
23
|
export declare abstract class OrmMigrateCommand extends Command {
|
|
24
|
+
/**
|
|
25
|
+
* Toutes les commandes `orm:*` parlent à la base — c'est leur définition.
|
|
26
|
+
*
|
|
27
|
+
* Déclaré ICI et pas dans le littéral d'options de chacune : la déclaration
|
|
28
|
+
* appartient à la FAMILLE, et une famille qui se déclare six fois finit par
|
|
29
|
+
* oublier la septième — l'oubli serait alors muet (la commande démarrerait
|
|
30
|
+
* sans connexion et ne trouverait pas de quoi migrer). Posé après `super()`,
|
|
31
|
+
* qui a déjà lu `options.runProfile` : une commande qui veut un profil autre
|
|
32
|
+
* peut donc encore le passer dans ses options, et il gagne.
|
|
33
|
+
*/
|
|
34
|
+
constructor(...args: ConstructorParameters<typeof Command>);
|
|
24
35
|
/**
|
|
25
36
|
* Faut-il colorer la sortie ?
|
|
26
37
|
*
|
|
@@ -17,8 +17,15 @@ declare class DrizzleService extends Service {
|
|
|
17
17
|
#private;
|
|
18
18
|
module: Module;
|
|
19
19
|
constructor(module: Module);
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Connecte tous les connecteurs déclarés en config (validée Zod).
|
|
22
|
+
*
|
|
23
|
+
* @param connect - `false` pour ENREGISTRER les ORM sans ouvrir de connexion.
|
|
24
|
+
* Réservé au boot d'un run qui n'a pas déclaré `externalServices` : l'ORM
|
|
25
|
+
* doit exister (c'est lui qui publie le plan d'administration) sans qu'un
|
|
26
|
+
* socket ne s'ouvre. Le défaut `true` fait de l'appel direct un ordre.
|
|
27
|
+
*/
|
|
28
|
+
connectAll(connect?: boolean): Promise<void>;
|
|
22
29
|
/** Ferme toutes les connexions. */
|
|
23
30
|
disconnectAll(): Promise<void>;
|
|
24
31
|
/** Retourne l'ORM Drizzle d'un connecteur (défaut : `"default"`). */
|
|
@@ -48,24 +48,6 @@ export interface DrizzleOrmOptions {
|
|
|
48
48
|
/** Qui sait APPLIQUER — refuse hors développement, en le disant. */
|
|
49
49
|
applyMigrations?: () => Promise<IOrmMigrationApplyReply>;
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Adapter Drizzle (driver `better-sqlite3`) **branché sur `@nodefony/orm-core`**
|
|
53
|
-
* — 3ᵉ adapter du banc multi-ORM (P7.4), choix SQL #1 moderne.
|
|
54
|
-
*
|
|
55
|
-
* Particularité vs les autres ORM : Drizzle est **schema-as-code** — il n'y
|
|
56
|
-
* a pas de « compilation » de modèle. `entity.schema` *est* déjà une table
|
|
57
|
-
* Drizzle (`sqliteTable(...)`). L'adapter :
|
|
58
|
-
* - dérive le DDL de chaque table via `getTableConfig()` et le crée (dev/test ;
|
|
59
|
-
* la prod passera par `drizzle-kit`) — pas de `sync()` natif côté Drizzle ;
|
|
60
|
-
* - résout les relations déclaratives ({@link IEntityRelation}) en métadonnées
|
|
61
|
-
* d'**eager-load manuel** (cf {@link DrizzleRepository}), pour rester générique
|
|
62
|
-
* sans imposer la couche `relations()` de Drizzle ;
|
|
63
|
-
* - pilote les transactions à la main (`BEGIN`/`COMMIT`/`ROLLBACK`) car
|
|
64
|
-
* `better-sqlite3` est synchrone (cf {@link DrizzleTransaction}).
|
|
65
|
-
*
|
|
66
|
-
* Trappe SQL brut : {@link DrizzleOrm.getNativeConnection} expose le db Drizzle
|
|
67
|
-
* (tag `sql`) pour les jointures arbitraires (ADR-0003 risque #1).
|
|
68
|
-
*/
|
|
69
51
|
export declare class DrizzleOrm extends Orm {
|
|
70
52
|
#private;
|
|
71
53
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodefony/drizzle",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.3",
|
|
4
4
|
"description": "Moteur SQL de Nodefony sur Drizzle ORM (SQLite, PostgreSQL, MySQL) : dépôts typés, migrations de schéma et adoption d'une base existante",
|
|
5
5
|
"nodefony": {
|
|
6
6
|
"storeKind": "durable",
|
|
@@ -60,30 +60,30 @@
|
|
|
60
60
|
"zod": "^4.4.3"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@nodefony/framework": "
|
|
64
|
-
"@nodefony/http": "
|
|
65
|
-
"@nodefony/orm-core": "
|
|
66
|
-
"@nodefony/security": "
|
|
67
|
-
"@nodefony/user": "
|
|
63
|
+
"@nodefony/framework": "^10.0.0-alpha.3",
|
|
64
|
+
"@nodefony/http": "^10.0.0-alpha.3",
|
|
65
|
+
"@nodefony/orm-core": "^10.0.0-alpha.3",
|
|
66
|
+
"@nodefony/security": "^10.0.0-alpha.3",
|
|
67
|
+
"@nodefony/user": "^10.0.0-alpha.3",
|
|
68
68
|
"@types/better-sqlite3": "9.6.0",
|
|
69
69
|
"@types/node": "26.4.1",
|
|
70
70
|
"@types/pg": "8.23.1",
|
|
71
71
|
"better-sqlite3": "13.0.3",
|
|
72
72
|
"drizzle-kit": "0.31.10",
|
|
73
73
|
"mysql2": "3.24.3",
|
|
74
|
-
"nodefony": "
|
|
74
|
+
"nodefony": "^10.0.0-alpha.3",
|
|
75
75
|
"pg": "8.23.0",
|
|
76
76
|
"rimraf": "6.1.3"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"@nodefony/framework": "
|
|
80
|
-
"@nodefony/http": "
|
|
81
|
-
"@nodefony/orm-core": "
|
|
82
|
-
"@nodefony/security": "
|
|
83
|
-
"@nodefony/user": "
|
|
79
|
+
"@nodefony/framework": "^10.0.0-alpha.3",
|
|
80
|
+
"@nodefony/http": "^10.0.0-alpha.3",
|
|
81
|
+
"@nodefony/orm-core": "^10.0.0-alpha.3",
|
|
82
|
+
"@nodefony/security": "^10.0.0-alpha.3",
|
|
83
|
+
"@nodefony/user": "^10.0.0-alpha.3",
|
|
84
84
|
"better-sqlite3": "^13.0.0",
|
|
85
85
|
"mysql2": "^3.24.0",
|
|
86
|
-
"nodefony": "
|
|
86
|
+
"nodefony": "^10.0.0-alpha.3",
|
|
87
87
|
"pg": "^8.23.0"
|
|
88
88
|
},
|
|
89
89
|
"repository": {
|