@nodefony/drizzle 10.0.0-alpha.9 → 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.
- package/README.md +19 -19
- package/dist/index.js +3 -3
- package/dist/nodefony/command/orm-generate.js +3 -3
- package/dist/nodefony/config/defineModuleConfig.js +2 -2
- package/dist/nodefony/entity/userTable.js +1 -1
- package/dist/nodefony/registerStores.js +2 -2
- package/dist/nodefony/service/DrizzleService.js +26 -12
- package/dist/nodefony/src/DrizzleAuditStore.js +1 -1
- package/dist/nodefony/src/connectorTarget.js +1 -1
- package/dist/nodefony/src/migrator/DrizzleMigrator.js +3 -3
- package/dist/nodefony/src/migrator/adopt.js +1 -1
- package/dist/nodefony/src/migrator/destructive.js +2 -3
- package/dist/nodefony/src/migrator/drivers/mysqlDriver.js +1 -1
- package/dist/nodefony/src/migrator/drivers/postgresDriver.js +1 -1
- package/dist/nodefony/src/migrator/explain.js +7 -10
- package/dist/nodefony/src/migrator/history.js +25 -2
- package/dist/nodefony/src/migrator/kit.js +23 -9
- package/dist/nodefony/src/migrator/rebuildCopy.js +6 -6
- package/dist/nodefony/src/migrator/resolve.js +4 -4
- package/dist/nodefony/src/migrator/sources.js +13 -8
- package/dist/nodefony/src/orm-core/DrizzleOrm.js +49 -29
- package/dist/nodefony/src/orm-core/DrizzleRepository.js +38 -26
- package/dist/nodefony/src/queryKit.js +5 -5
- package/dist/types/nodefony/src/DrizzleUserRepository.d.ts +1 -1
- package/dist/types/nodefony/src/connectorTarget.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/DrizzleMigrator.d.ts +4 -4
- package/dist/types/nodefony/src/migrator/destructive.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/drivers/index.d.ts +2 -2
- package/dist/types/nodefony/src/migrator/explain.d.ts +9 -9
- package/dist/types/nodefony/src/migrator/name.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/types.d.ts +2 -2
- package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +3 -3
- package/dist/types/nodefony/src/orm-core/DrizzleRepository.d.ts +1 -1
- package/dist/types/nodefony/src/orm-core/ddlPlan.d.ts +2 -2
- package/dist/types/nodefony/src/queryKit.d.ts +9 -9
- package/dist/types/nodefony/src/safeTarget.d.ts +2 -2
- package/docs/index.md +13 -13
- package/docs/migrations.md +1 -1
- package/package.json +14 -14
|
@@ -431,17 +431,18 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
431
431
|
}
|
|
432
432
|
async onConnect() {
|
|
433
433
|
await this.#releasePrevious();
|
|
434
|
-
|
|
434
|
+
const tables = Object.create(null);
|
|
435
|
+
this.#tables = tables;
|
|
435
436
|
this.#relations = Object.create(null);
|
|
436
437
|
const entities = this.#ownEntities();
|
|
437
438
|
switch (this.#dialect) {
|
|
438
439
|
case "sqlite":
|
|
439
|
-
await this.#connectSqlite(entities);
|
|
440
|
+
await this.#connectSqlite(entities, tables);
|
|
440
441
|
break;
|
|
441
442
|
case "postgres":
|
|
442
|
-
await this.#connectPostgres(entities);
|
|
443
|
+
await this.#connectPostgres(entities, tables);
|
|
443
444
|
break;
|
|
444
|
-
case "mysql": await this.#connectMysql(entities);
|
|
445
|
+
case "mysql": await this.#connectMysql(entities, tables);
|
|
445
446
|
}
|
|
446
447
|
if (this.#deriveSchema) {
|
|
447
448
|
await this.#reconcileSchema();
|
|
@@ -461,7 +462,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
461
462
|
* La méthode devient asynchrone pour cette seule raison ; le pilote, lui,
|
|
462
463
|
* reste synchrone une fois chargé (`client.exec`, `client.pragma`).
|
|
463
464
|
*/
|
|
464
|
-
async #connectSqlite(entities) {
|
|
465
|
+
async #connectSqlite(entities, tables) {
|
|
465
466
|
let Sqlite;
|
|
466
467
|
let sqliteDrizzle;
|
|
467
468
|
try {
|
|
@@ -471,7 +472,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
471
472
|
Sqlite = resolved;
|
|
472
473
|
sqliteDrizzle = (await import("drizzle-orm/better-sqlite3")).drizzle;
|
|
473
474
|
} catch (e) {
|
|
474
|
-
throw new Error(`DrizzleOrm "${this.name}": the sqlite dialect needs the optional driver \`better-sqlite3\` (run \`npm i better-sqlite3\`).
|
|
475
|
+
throw new Error(`DrizzleOrm "${this.name}": the sqlite dialect needs the optional driver \`better-sqlite3\` (run \`npm i better-sqlite3\`). ` + e.message, { cause: e });
|
|
475
476
|
}
|
|
476
477
|
const client = new Sqlite(this.#filename);
|
|
477
478
|
if (this.#filename !== ":memory:") {
|
|
@@ -506,7 +507,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
506
507
|
for (const entity of entities) {
|
|
507
508
|
this.#assertDialectTable(entity, SQLiteTable, "sqlite");
|
|
508
509
|
const table = entity.schema;
|
|
509
|
-
|
|
510
|
+
tables[entity.name] = table;
|
|
510
511
|
entity.model = table;
|
|
511
512
|
}
|
|
512
513
|
if (!this.#deriveSchema) return;
|
|
@@ -537,12 +538,12 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
537
538
|
#rawQuery = async (sql, params = []) => {
|
|
538
539
|
switch (this.#dialect) {
|
|
539
540
|
case "sqlite": {
|
|
540
|
-
const statement = this.#
|
|
541
|
+
const statement = this.#sqliteClient().prepare(sql);
|
|
541
542
|
return statement.reader ? statement.all(...params) : [];
|
|
542
543
|
}
|
|
543
|
-
case "postgres": return (await this.#
|
|
544
|
+
case "postgres": return (await this.#pgClient().query(toDollarParams(sql), params)).rows;
|
|
544
545
|
case "mysql": {
|
|
545
|
-
const [rows] = await this.#
|
|
546
|
+
const [rows] = await this.#mysqlClient().query(sql, params);
|
|
546
547
|
return Array.isArray(rows) ? rows : [];
|
|
547
548
|
}
|
|
548
549
|
}
|
|
@@ -555,13 +556,13 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
555
556
|
async #rawExec(sql) {
|
|
556
557
|
switch (this.#dialect) {
|
|
557
558
|
case "sqlite":
|
|
558
|
-
this.#
|
|
559
|
+
this.#sqliteClient().exec(sql);
|
|
559
560
|
return;
|
|
560
561
|
case "postgres":
|
|
561
|
-
await this.#
|
|
562
|
+
await this.#pgClient().query(sql);
|
|
562
563
|
return;
|
|
563
564
|
case "mysql":
|
|
564
|
-
await this.#
|
|
565
|
+
await this.#mysqlClient().query(sql);
|
|
565
566
|
return;
|
|
566
567
|
}
|
|
567
568
|
}
|
|
@@ -640,20 +641,20 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
640
641
|
case "sqlite": {
|
|
641
642
|
const t = table;
|
|
642
643
|
if (empechees.has(getTableConfig(t).name)) continue;
|
|
643
|
-
for (const statement of this.#createIndexesSQL(t)) this.#
|
|
644
|
+
for (const statement of this.#createIndexesSQL(t)) this.#sqliteClient().exec(statement);
|
|
644
645
|
break;
|
|
645
646
|
}
|
|
646
647
|
case "postgres": {
|
|
647
648
|
const t = table;
|
|
648
649
|
if (empechees.has(getTableConfig$1(t).name)) continue;
|
|
649
|
-
for (const statement of this.#createIndexesPgSQL(t)) await this.#
|
|
650
|
+
for (const statement of this.#createIndexesPgSQL(t)) await this.#pgClient().query(statement);
|
|
650
651
|
break;
|
|
651
652
|
}
|
|
652
653
|
case "mysql": {
|
|
653
654
|
const t = table;
|
|
654
655
|
if (empechees.has(getTableConfig$2(t).name)) continue;
|
|
655
656
|
for (const statement of this.#createIndexesMysqlSQL(t)) try {
|
|
656
|
-
await this.#
|
|
657
|
+
await this.#mysqlClient().query(statement);
|
|
657
658
|
} catch (error) {
|
|
658
659
|
if (error.code !== "ER_DUP_KEYNAME") throw error;
|
|
659
660
|
}
|
|
@@ -677,7 +678,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
677
678
|
* déploiement SQLite ne les tire jamais. Pool partagé ; DDL dérivé pour dev/test
|
|
678
679
|
* (prod = drizzle-kit). Échec d'import → message actionnable (`npm i pg`).
|
|
679
680
|
*/
|
|
680
|
-
async #connectPostgres(entities) {
|
|
681
|
+
async #connectPostgres(entities, tables) {
|
|
681
682
|
if (!this.#url) throw new Error(`DrizzleOrm "${this.name}": dialect "postgres" requires a connection \`url\`.`);
|
|
682
683
|
let PoolCtor;
|
|
683
684
|
let pgDrizzle;
|
|
@@ -708,7 +709,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
708
709
|
this.#pgPool = pool;
|
|
709
710
|
this.#db = pgDrizzle(pool);
|
|
710
711
|
try {
|
|
711
|
-
await this.#finishPostgres(entities, pool, pgDrizzle);
|
|
712
|
+
await this.#finishPostgres(entities, tables, pool, pgDrizzle);
|
|
712
713
|
} catch (e) {
|
|
713
714
|
this.#unwireAll();
|
|
714
715
|
await pool.end().catch(() => void 0);
|
|
@@ -719,11 +720,11 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
719
720
|
}
|
|
720
721
|
}
|
|
721
722
|
/** Suite de la connexion postgres — isolée pour rendre `connect()` atomique. */
|
|
722
|
-
async #finishPostgres(entities, pool, pgDrizzle) {
|
|
723
|
+
async #finishPostgres(entities, tables, pool, pgDrizzle) {
|
|
723
724
|
this.#beginTx = async () => {
|
|
724
725
|
const cx = await pool.connect();
|
|
725
726
|
const puitsTx = (err) => {
|
|
726
|
-
this.connectionLost(`pg (transaction) : ${err
|
|
727
|
+
this.connectionLost(`pg (transaction) : ${err instanceof Error ? err.message : String(err)}`);
|
|
727
728
|
};
|
|
728
729
|
cx.on("error", puitsTx);
|
|
729
730
|
/** Rend la connexion, en retirant d'abord NOTRE puits. */
|
|
@@ -748,7 +749,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
748
749
|
for (const entity of entities) {
|
|
749
750
|
this.#assertDialectTable(entity, PgTable, "postgres");
|
|
750
751
|
const table = entity.schema;
|
|
751
|
-
|
|
752
|
+
tables[entity.name] = table;
|
|
752
753
|
entity.model = table;
|
|
753
754
|
}
|
|
754
755
|
if (!this.#deriveSchema) return;
|
|
@@ -809,7 +810,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
809
810
|
*/
|
|
810
811
|
#wirePgLifecycle(pool) {
|
|
811
812
|
const onError = (err) => {
|
|
812
|
-
this.connectionLost(`pg: ${err
|
|
813
|
+
this.connectionLost(`pg: ${err instanceof Error ? err.message : String(err)}`);
|
|
813
814
|
};
|
|
814
815
|
const onConnect = () => {
|
|
815
816
|
this.connectionRestored();
|
|
@@ -853,7 +854,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
853
854
|
this.connectionRestored();
|
|
854
855
|
cx.on("error", (err) => {
|
|
855
856
|
if (this.#mysqlPool !== pool) return;
|
|
856
|
-
this.connectionLost(`mysql: ${err
|
|
857
|
+
this.connectionLost(`mysql: ${err instanceof Error ? err.message : String(err)}`);
|
|
857
858
|
});
|
|
858
859
|
cx.stream?.once("close", () => {
|
|
859
860
|
if (this.#mysqlPool !== pool) return;
|
|
@@ -873,7 +874,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
873
874
|
* `datetime(3)` (kind `dateMs` du colKit) sont écrites/relues en UTC — mêmes
|
|
874
875
|
* instants que `timestamptz` PG, sans dépendre de la timezone du serveur.
|
|
875
876
|
*/
|
|
876
|
-
async #connectMysql(entities) {
|
|
877
|
+
async #connectMysql(entities, tables) {
|
|
877
878
|
if (!this.#url) throw new Error(`DrizzleOrm "${this.name}": dialect "mysql" requires a connection \`url\`.`);
|
|
878
879
|
let pool;
|
|
879
880
|
let mysqlDrizzle;
|
|
@@ -903,7 +904,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
903
904
|
this.#mysqlPool = pool;
|
|
904
905
|
this.#db = mysqlDrizzle(pool);
|
|
905
906
|
try {
|
|
906
|
-
await this.#finishMysql(entities, pool, mysqlDrizzle);
|
|
907
|
+
await this.#finishMysql(entities, tables, pool, mysqlDrizzle);
|
|
907
908
|
} catch (e) {
|
|
908
909
|
this.#unwireAll();
|
|
909
910
|
await pool.end().catch(() => void 0);
|
|
@@ -914,7 +915,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
914
915
|
}
|
|
915
916
|
}
|
|
916
917
|
/** Suite de la connexion mysql — isolée pour rendre `connect()` atomique. */
|
|
917
|
-
async #finishMysql(entities, pool, mysqlDrizzle) {
|
|
918
|
+
async #finishMysql(entities, tables, pool, mysqlDrizzle) {
|
|
918
919
|
this.#beginTx = async () => {
|
|
919
920
|
const cx = await pool.getConnection();
|
|
920
921
|
try {
|
|
@@ -937,7 +938,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
937
938
|
for (const entity of entities) {
|
|
938
939
|
this.#assertDialectTable(entity, MySqlTable, "mysql");
|
|
939
940
|
const table = entity.schema;
|
|
940
|
-
|
|
941
|
+
tables[entity.name] = table;
|
|
941
942
|
entity.model = table;
|
|
942
943
|
}
|
|
943
944
|
if (!this.#deriveSchema) return;
|
|
@@ -977,7 +978,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
977
978
|
const table = this.#tables?.[name];
|
|
978
979
|
if (!this.#db) throw new Error(`DrizzleOrm "${this.name}": non connecté — impossible de rendre un dépôt pour "${name}". Les tables ne sont résolues qu'à la connexion. Si ce run est une commande en ligne, elle n'a pas déclaré \`externalServices\` (CONSOLE_DATA_RUN_PROFILE).`);
|
|
979
980
|
if (!table) throw new Error(`DrizzleOrm "${this.name}": no entity table registered under "${name}".`);
|
|
980
|
-
|
|
981
|
+
this.#repositories ??= Object.create(null);
|
|
981
982
|
let repository = this.#repositories[name];
|
|
982
983
|
if (repository === void 0) {
|
|
983
984
|
repository = new DrizzleRepository(this.#db, table, this.#relations?.[name] ?? {}, this.name, this.#dialect);
|
|
@@ -1010,6 +1011,25 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
1010
1011
|
throw error;
|
|
1011
1012
|
}
|
|
1012
1013
|
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Connexion SQLite courante — lève si le connecteur n'est pas ouvert. Les
|
|
1016
|
+
* appelants ne passent ici qu'une fois `onConnect` engagé : la levée ne sert
|
|
1017
|
+
* qu'à typer, et à dire la cause si l'ordre était un jour rompu.
|
|
1018
|
+
*/
|
|
1019
|
+
#sqliteClient() {
|
|
1020
|
+
if (this.#client === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1021
|
+
return this.#client;
|
|
1022
|
+
}
|
|
1023
|
+
/** Pool PostgreSQL courant — même contrat que {@link #sqliteClient}. */
|
|
1024
|
+
#pgClient() {
|
|
1025
|
+
if (this.#pgPool === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1026
|
+
return this.#pgPool;
|
|
1027
|
+
}
|
|
1028
|
+
/** Pool MySQL courant — même contrat que {@link #sqliteClient}. */
|
|
1029
|
+
#mysqlClient() {
|
|
1030
|
+
if (this.#mysqlPool === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1031
|
+
return this.#mysqlPool;
|
|
1032
|
+
}
|
|
1013
1033
|
getNativeConnection() {
|
|
1014
1034
|
if (!this.#db) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
|
|
1015
1035
|
return this.#db;
|
|
@@ -1123,7 +1143,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
|
|
|
1123
1143
|
* @returns sonde `pool` (taille max, idle, empruntées, en attente).
|
|
1124
1144
|
*/
|
|
1125
1145
|
#probePgPool(pool) {
|
|
1126
|
-
const max = pool.options
|
|
1146
|
+
const max = pool.options.max;
|
|
1127
1147
|
return { pool: {
|
|
1128
1148
|
size: typeof max === "number" ? max : 10,
|
|
1129
1149
|
available: pool.idleCount,
|
|
@@ -83,11 +83,28 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
83
83
|
this.#dialect = dialect;
|
|
84
84
|
this.#transactional = transactional;
|
|
85
85
|
}
|
|
86
|
-
/** Colonne Drizzle d'une table par nom logique. */
|
|
86
|
+
/** Colonne Drizzle d'une table par nom logique ; `undefined` si elle n'existe pas. */
|
|
87
87
|
#col(table, name) {
|
|
88
88
|
return table[name];
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
+
* Colonne EXIGÉE : un nom inconnu lève au lieu de partir dans le SQL.
|
|
92
|
+
*
|
|
93
|
+
* Strict (B2) : champ inconnu = erreur, pas un skip silencieux (qui ferait
|
|
94
|
+
* disparaître la condition → fuite « renvoie tout »), ni un `undefined` glissé
|
|
95
|
+
* dans un `ORDER BY` (rendu `no such column: asc`). Même contrat que Mongoose.
|
|
96
|
+
* Requêtes natives → `getNativeConnection()`.
|
|
97
|
+
*
|
|
98
|
+
* @param name - nom logique du champ (critère, tri, clé étrangère).
|
|
99
|
+
* @param table - table qui doit le porter (défaut : celle du repository).
|
|
100
|
+
* @throws UnknownCriteriaField si la table ne porte pas ce champ.
|
|
101
|
+
*/
|
|
102
|
+
#requireCol(name, table = this.#table) {
|
|
103
|
+
const col = this.#col(table, name);
|
|
104
|
+
if (col === void 0) throw new UnknownCriteriaField(name, getTableName(table), Object.keys(getTableColumns(table)));
|
|
105
|
+
return col;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
91
108
|
* Colonnes de la PK de la table (mémoïsées) : colonnes inline `.primaryKey()`
|
|
92
109
|
* d'abord (introspection core, valide sur tous les dialectes — couvre toutes
|
|
93
110
|
* les entités framework), sinon PK composite déclarée via `primaryKey({
|
|
@@ -128,7 +145,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
128
145
|
const qualified = sql.join(pk, sql.raw(", "));
|
|
129
146
|
const inner = where ? sql`select ${qualified} from ${this.#table} where ${where} limit 1` : sql`select ${qualified} from ${this.#table} limit 1`;
|
|
130
147
|
const output = sql.join(pk.map((col) => sql.identifier(col.name)), sql.raw(", "));
|
|
131
|
-
const first = pk
|
|
148
|
+
const first = pk.at(0);
|
|
132
149
|
const target = pk.length === 1 && first ? sql`${first}` : sql`(${qualified})`;
|
|
133
150
|
return sql`${target} in (select ${output} from (${inner}) as picked)`;
|
|
134
151
|
}
|
|
@@ -229,12 +246,11 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
229
246
|
for (const [field, value] of Object.entries(criteria)) {
|
|
230
247
|
if (field === "$or") {
|
|
231
248
|
if (!Array.isArray(value)) throw new Error(`DrizzleRepository(${getTableName(this.#table)}): $or attend un tableau de critères.`);
|
|
232
|
-
const
|
|
233
|
-
if (
|
|
249
|
+
const [first, ...rest] = value.map((branch) => this.#where(branch)).filter((branch) => branch !== void 0);
|
|
250
|
+
if (first !== void 0) conds.push(rest.length === 0 ? first : or(first, ...rest));
|
|
234
251
|
continue;
|
|
235
252
|
}
|
|
236
|
-
const col = this.#
|
|
237
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
253
|
+
const col = this.#requireCol(field);
|
|
238
254
|
if (isFieldOperators(value)) this.#pushOperators(conds, col, value);
|
|
239
255
|
else if (value === null) conds.push(isNull(col));
|
|
240
256
|
else conds.push(eq(col, value));
|
|
@@ -305,14 +321,13 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
305
321
|
const conds = [];
|
|
306
322
|
let i = 0;
|
|
307
323
|
for (const [field, value] of Object.entries(criteria)) {
|
|
308
|
-
const col = this.#
|
|
309
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
324
|
+
const col = this.#requireCol(field);
|
|
310
325
|
conds.push(value === null ? isNull(col) : eq(col, sql.param(sql.placeholder(`p${i}`), col)));
|
|
311
326
|
i++;
|
|
312
327
|
}
|
|
313
328
|
if (conds.length > 0) query = query.where(conds.length === 1 ? conds[0] : and(...conds));
|
|
314
329
|
}
|
|
315
|
-
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#
|
|
330
|
+
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#requireCol(field)) : asc(this.#requireCol(field))));
|
|
316
331
|
if (options?.limit !== void 0) query = query.limit(sql.placeholder("lim"));
|
|
317
332
|
else if (options?.offset !== void 0 && this.#dialect === "sqlite") query = query.limit(sql`-1`);
|
|
318
333
|
else if (options?.offset !== void 0 && this.#dialect === "mysql") query = query.limit(Number.MAX_SAFE_INTEGER);
|
|
@@ -335,7 +350,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
335
350
|
const shape = this.#selectShape(criteria, options);
|
|
336
351
|
if (shape === null) return null;
|
|
337
352
|
let cache = this.#preparedSelects;
|
|
338
|
-
|
|
353
|
+
cache ??= this.#preparedSelects = /* @__PURE__ */ new Map();
|
|
339
354
|
let entry = cache.get(shape.key);
|
|
340
355
|
if (entry === void 0) {
|
|
341
356
|
if (cache.size >= PREPARED_CACHE_MAX) return null;
|
|
@@ -364,12 +379,12 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
364
379
|
let query = this.#db.select().from(execTable(this.#table)).$dynamic();
|
|
365
380
|
const where = this.#where(criteria);
|
|
366
381
|
if (where) query = query.where(where);
|
|
367
|
-
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#
|
|
382
|
+
if (options?.order?.length) query = query.orderBy(...options.order.map(([field, dir]) => dir === "DESC" ? desc(this.#requireCol(field)) : asc(this.#requireCol(field))));
|
|
368
383
|
if (options?.limit !== void 0) query = query.limit(options.limit);
|
|
369
384
|
else if (options?.offset !== void 0 && this.#dialect === "sqlite") query = query.limit(sql`-1`);
|
|
370
385
|
else if (options?.offset !== void 0 && this.#dialect === "mysql") query = query.limit(Number.MAX_SAFE_INTEGER);
|
|
371
386
|
if (options?.offset !== void 0) query = query.offset(options.offset);
|
|
372
|
-
return
|
|
387
|
+
return this.#prof(query);
|
|
373
388
|
}
|
|
374
389
|
/** Eager-load manuel des relations déclarées (1 requête `IN (...)` par relation). */
|
|
375
390
|
async #populate(rows, relations) {
|
|
@@ -379,7 +394,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
379
394
|
if (!rel) throw new Error(`DrizzleRepository(${name}): relation "${name}" non déclarée.`);
|
|
380
395
|
if (rel.type === "one-to-many") {
|
|
381
396
|
const parentIds = rows.map((row) => row[rel.localKey]);
|
|
382
|
-
const fkCol = this.#
|
|
397
|
+
const fkCol = this.#requireCol(rel.foreignKey, rel.targetTable);
|
|
383
398
|
const children = await this.#prof(this.#db.select().from(execTable(rel.targetTable)).where(inArray(fkCol, parentIds)));
|
|
384
399
|
const byParent = /* @__PURE__ */ new Map();
|
|
385
400
|
for (const child of children) {
|
|
@@ -391,7 +406,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
391
406
|
for (const row of rows) row[name] = byParent.get(row[rel.localKey]) ?? [];
|
|
392
407
|
} else {
|
|
393
408
|
const fkValues = rows.map((row) => row[rel.foreignKey]).filter((value) => value !== null && value !== void 0);
|
|
394
|
-
const idCol = this.#
|
|
409
|
+
const idCol = this.#requireCol(rel.targetKey, rel.targetTable);
|
|
395
410
|
const parents = fkValues.length > 0 ? await this.#prof(this.#db.select().from(execTable(rel.targetTable)).where(inArray(idCol, fkValues))) : [];
|
|
396
411
|
const byId = new Map(parents.map((p) => [p[rel.targetKey], p]));
|
|
397
412
|
for (const row of rows) row[name] = byId.get(row[rel.foreignKey]) ?? null;
|
|
@@ -441,8 +456,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
441
456
|
values[field] = value;
|
|
442
457
|
continue;
|
|
443
458
|
}
|
|
444
|
-
const col = this.#
|
|
445
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
459
|
+
const col = this.#requireCol(field);
|
|
446
460
|
const apply = (fn, v) => {
|
|
447
461
|
set[field] = sql`${sql.raw(fn)}(${col}, ${v})`;
|
|
448
462
|
values[field] = v;
|
|
@@ -457,9 +471,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
457
471
|
}
|
|
458
472
|
async upsert(criteria, update, insertOnly) {
|
|
459
473
|
const target = Object.keys(criteria).map((field) => {
|
|
460
|
-
|
|
461
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
462
|
-
return col;
|
|
474
|
+
return this.#requireCol(field);
|
|
463
475
|
});
|
|
464
476
|
const write = this.#writeSet(update);
|
|
465
477
|
const values = {
|
|
@@ -486,8 +498,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
486
498
|
async increment(criteria, changes) {
|
|
487
499
|
const setObj = {};
|
|
488
500
|
for (const [field, delta] of Object.entries(changes)) {
|
|
489
|
-
const col = this.#
|
|
490
|
-
if (!col) throw new UnknownCriteriaField(field, getTableName(this.#table), Object.keys(getTableColumns(this.#table)));
|
|
501
|
+
const col = this.#requireCol(field);
|
|
491
502
|
setObj[field] = sql`${col} + ${delta}`;
|
|
492
503
|
}
|
|
493
504
|
if (this.#dialect === "mysql") return this.#mysqlUpdateOneReturning(setObj, criteria);
|
|
@@ -511,7 +522,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
511
522
|
/** DELETE atomique d'AU PLUS une ligne (PK via `#pickOne`), RETURNING la ligne. */
|
|
512
523
|
async #deleteOneReturning(criteria) {
|
|
513
524
|
const pick = this.#pickOne(this.#where(criteria));
|
|
514
|
-
return
|
|
525
|
+
return this.#prof(this.#db.delete(execTable(this.#table)).where(pick).returning());
|
|
515
526
|
}
|
|
516
527
|
/** PK obligatoire en mysql (les verbes re-SELECTent par PK). Fail-loud sinon. */
|
|
517
528
|
#requirePk(verb) {
|
|
@@ -522,7 +533,8 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
522
533
|
/** WHERE d'égalité sur la PK (valeurs plates lues par nom de colonne). */
|
|
523
534
|
#pkWhere(pk, values) {
|
|
524
535
|
const conds = pk.map((col) => eq(col, values[col.name]));
|
|
525
|
-
|
|
536
|
+
const [first, ...rest] = conds;
|
|
537
|
+
return first !== void 0 && rest.length === 0 ? first : and(...conds);
|
|
526
538
|
}
|
|
527
539
|
/** SELECT d'UNE ligne par valeurs de PK (relecture post-mutation mysql). */
|
|
528
540
|
async #selectByPk(pk, values) {
|
|
@@ -544,7 +556,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
544
556
|
async #mysqlUpdateOneReturning(set, criteria) {
|
|
545
557
|
const pk = this.#requirePk("updateOne");
|
|
546
558
|
const where = this.#where(criteria);
|
|
547
|
-
const target = (await this.#runSelect(criteria, { limit: 1 }))
|
|
559
|
+
const target = (await this.#runSelect(criteria, { limit: 1 })).at(0);
|
|
548
560
|
if (!target) return null;
|
|
549
561
|
const pkCond = this.#pkWhere(pk, target);
|
|
550
562
|
const result = await this.#prof(this.#db.update(execTable(this.#table)).set(set).where(where ? and(pkCond, where) : pkCond));
|
|
@@ -556,7 +568,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
556
568
|
async #mysqlDeleteOneReturning(criteria) {
|
|
557
569
|
const pk = this.#requirePk("deleteOne");
|
|
558
570
|
const where = this.#where(criteria);
|
|
559
|
-
const target = (await this.#runSelect(criteria, { limit: 1 }))
|
|
571
|
+
const target = (await this.#runSelect(criteria, { limit: 1 })).at(0);
|
|
560
572
|
if (!target) return null;
|
|
561
573
|
const pkCond = this.#pkWhere(pk, target);
|
|
562
574
|
const result = await this.#prof(this.#db.delete(execTable(this.#table)).where(where ? and(pkCond, where) : pkCond));
|
|
@@ -591,7 +603,7 @@ var DrizzleRepository = class DrizzleRepository {
|
|
|
591
603
|
*/
|
|
592
604
|
async countDistinct(field, criteria) {
|
|
593
605
|
const where = this.#where(criteria);
|
|
594
|
-
const builder = this.#db.select({ value: countDistinct(this.#
|
|
606
|
+
const builder = this.#db.select({ value: countDistinct(this.#requireCol(field)) }).from(execTable(this.#table)).$dynamic();
|
|
595
607
|
const rows = await this.#prof(where ? builder.where(where) : builder);
|
|
596
608
|
return Number(rows[0]?.value ?? 0);
|
|
597
609
|
}
|
|
@@ -30,12 +30,12 @@ import { sql } from "drizzle-orm";
|
|
|
30
30
|
*/
|
|
31
31
|
async function findUserIdBySocialProvider(db, dialect, provider, providerId) {
|
|
32
32
|
switch (dialect) {
|
|
33
|
-
case "sqlite": return
|
|
33
|
+
case "sqlite": return db.all(sql`SELECT "id" AS id FROM ${ident("sqlite", USER_TABLE_NAME)}
|
|
34
34
|
WHERE EXISTS (
|
|
35
35
|
SELECT 1 FROM json_each(${ident("sqlite", USER_TABLE_NAME)}."socialProviders")
|
|
36
36
|
WHERE json_extract(value, '$.provider') = ${provider}
|
|
37
37
|
AND json_extract(value, '$.providerId') = ${providerId}
|
|
38
|
-
) LIMIT 1`)
|
|
38
|
+
) LIMIT 1`)[0]?.id ?? null;
|
|
39
39
|
case "postgres": {
|
|
40
40
|
const pattern = JSON.stringify([{
|
|
41
41
|
provider,
|
|
@@ -95,7 +95,7 @@ async function reserveIdempotencyKeyMysql(db, args) {
|
|
|
95
95
|
const [inserted] = await exec.execute(sql`INSERT IGNORE INTO ${sql.raw("`idempotency_key`")}
|
|
96
96
|
(${sql.raw("`key`, `fingerprint`, `state`, `response`, `expiresAt`")})
|
|
97
97
|
VALUES (${key}, ${fingerprint}, 'if', NULL, ${leaseExpiresAt})`);
|
|
98
|
-
if ((inserted
|
|
98
|
+
if ((inserted.affectedRows ?? 0) === 1) return true;
|
|
99
99
|
const [stolen] = await exec.execute(sql`UPDATE ${sql.raw("`idempotency_key`")}
|
|
100
100
|
SET ${sql.raw("`fingerprint`")} = ${fingerprint},
|
|
101
101
|
${sql.raw("`state`")} = 'if',
|
|
@@ -103,7 +103,7 @@ async function reserveIdempotencyKeyMysql(db, args) {
|
|
|
103
103
|
${sql.raw("`expiresAt`")} = ${leaseExpiresAt}
|
|
104
104
|
WHERE ${sql.raw("`key`")} = ${key}
|
|
105
105
|
AND ${sql.raw("`expiresAt`")} < ${now}`);
|
|
106
|
-
return (stolen
|
|
106
|
+
return (stolen.affectedRows ?? 0) > 0;
|
|
107
107
|
}
|
|
108
108
|
/** Identifiant quoté selon le dialecte (`"x"` SQL standard, `` `x` `` MySQL). */
|
|
109
109
|
function ident(dialect, name) {
|
|
@@ -189,7 +189,7 @@ function orderBySql(dialect, order, allowed, fallback, tiebreaker) {
|
|
|
189
189
|
/** Exécute un SELECT et normalise le retour en tableau de lignes (API native divergente). */
|
|
190
190
|
async function runSelect(db, dialect, query) {
|
|
191
191
|
switch (dialect) {
|
|
192
|
-
case "sqlite": return
|
|
192
|
+
case "sqlite": return db.all(query);
|
|
193
193
|
case "postgres": return (await db.execute(query)).rows;
|
|
194
194
|
case "mysql": {
|
|
195
195
|
const [rows] = await db.execute(query);
|
|
@@ -40,7 +40,7 @@ export declare class DrizzleUserRepository implements IUserRepository {
|
|
|
40
40
|
updateMany(criteria: Criteria<IPasswordAuthenticatedUser>, data: Partial<IPasswordAuthenticatedUser>): Promise<number>;
|
|
41
41
|
delete(criteria: Criteria<IPasswordAuthenticatedUser>): Promise<number>;
|
|
42
42
|
count(criteria?: Criteria<IPasswordAuthenticatedUser>): Promise<number>;
|
|
43
|
-
countDistinct(field: keyof IPasswordAuthenticatedUser
|
|
43
|
+
countDistinct(field: Extract<keyof IPasswordAuthenticatedUser, string>, criteria?: Criteria<IPasswordAuthenticatedUser>): Promise<number>;
|
|
44
44
|
withTransaction(tx: ITransaction): IUserRepository;
|
|
45
45
|
findByIdentifier(identifier: string): Promise<IPasswordAuthenticatedUser | null>;
|
|
46
46
|
/**
|
|
@@ -35,7 +35,7 @@ export declare function defaultConnectorFilename(kernel: Kernel | null, name: st
|
|
|
35
35
|
export interface IConnectorTarget {
|
|
36
36
|
dialect: SqlDialect;
|
|
37
37
|
filename?: string;
|
|
38
|
-
url?: string;
|
|
38
|
+
url?: string | undefined;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Résout les coordonnées d'un connecteur : dialecte, et fichier ou URL.
|
|
@@ -10,7 +10,7 @@ export interface IDrizzleMigratorOptions extends IMigrationTarget {
|
|
|
10
10
|
/** Registre de sources, espace de noms OUVERT. */
|
|
11
11
|
sources: readonly IMigrationSource[];
|
|
12
12
|
/** Délai d'attente du verrou (ms). */
|
|
13
|
-
lockTimeoutMs?: number;
|
|
13
|
+
lockTimeoutMs?: number | undefined;
|
|
14
14
|
/** Qui applique — hôte du job par défaut. */
|
|
15
15
|
appliedBy?: string;
|
|
16
16
|
/** Horloge, injectable pour les tests. */
|
|
@@ -71,12 +71,12 @@ export declare class DrizzleMigrator {
|
|
|
71
71
|
* @returns ce qui a été levé et ré-aligné.
|
|
72
72
|
*/
|
|
73
73
|
repair(options?: {
|
|
74
|
-
source?: string;
|
|
75
|
-
updateHashes?: boolean;
|
|
74
|
+
source?: string | undefined;
|
|
75
|
+
updateHashes?: boolean | undefined;
|
|
76
76
|
forget?: readonly {
|
|
77
77
|
source: string;
|
|
78
78
|
tag: string;
|
|
79
|
-
}[];
|
|
79
|
+
}[] | undefined;
|
|
80
80
|
}): Promise<{
|
|
81
81
|
cleared: {
|
|
82
82
|
source: string;
|
|
@@ -8,9 +8,9 @@ export interface IMigrationTarget {
|
|
|
8
8
|
/** Dialecte du connecteur. */
|
|
9
9
|
dialect: SqlDialect;
|
|
10
10
|
/** Fichier SQLite (dialecte `sqlite`). */
|
|
11
|
-
filename?: string;
|
|
11
|
+
filename?: string | undefined;
|
|
12
12
|
/** URL de connexion DIRECTE (dialectes `postgres` et `mysql`). */
|
|
13
|
-
url?: string;
|
|
13
|
+
url?: string | undefined;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Ouvre le pilote à connexion unique du dialecte demandé.
|
|
@@ -108,15 +108,15 @@ export interface IMigrationEntryReport {
|
|
|
108
108
|
/** Où en est cette migration pour CE connecteur. */
|
|
109
109
|
status: "applied" | "pending" | "failed" | "drifted" | "missing";
|
|
110
110
|
/** Début de l'application, en millisecondes — absent si jamais appliquée. */
|
|
111
|
-
appliedAt?: number;
|
|
111
|
+
appliedAt?: number | undefined;
|
|
112
112
|
/** Durée de l'application, en millisecondes. */
|
|
113
|
-
durationMs?: number;
|
|
113
|
+
durationMs?: number | undefined;
|
|
114
114
|
/** Ce qui l'a appliquée, tel que l'historique l'a retenu. */
|
|
115
|
-
appliedBy?: string;
|
|
115
|
+
appliedBy?: string | undefined;
|
|
116
116
|
/** Déploiement qui l'a portée — groupe les migrations d'un même passage. */
|
|
117
|
-
runId?: string;
|
|
117
|
+
runId?: string | undefined;
|
|
118
118
|
/** Motif de l'échec, quand elle a échoué. */
|
|
119
|
-
error?: string;
|
|
119
|
+
error?: string | undefined;
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
122
|
* La charge utile figée — cœur NEUTRE au premier niveau, spécifique du pilote
|
|
@@ -155,7 +155,7 @@ export interface IMigrationReport {
|
|
|
155
155
|
* Son ABSENCE est un fait, pas un oubli : sur une base conforme, il n'y a
|
|
156
156
|
* rien à nommer.
|
|
157
157
|
*/
|
|
158
|
-
divergence?: ISchemaComparison;
|
|
158
|
+
divergence?: ISchemaComparison | undefined;
|
|
159
159
|
/**
|
|
160
160
|
* Ce que le passage a DÉTRUIT, nommé — présent au seul run réel qui a
|
|
161
161
|
* appliqué une migration destructive, absent partout ailleurs.
|
|
@@ -166,7 +166,7 @@ export interface IMigrationReport {
|
|
|
166
166
|
* octet de ce qu'il relit ne la mentionne. Son absence est un fait : rien
|
|
167
167
|
* n'a été détruit.
|
|
168
168
|
*/
|
|
169
|
-
destructive?: IDestructiveFinding[];
|
|
169
|
+
destructive?: IDestructiveFinding[] | undefined;
|
|
170
170
|
/** Tout ce qui est propre au pilote SQL vit ici, et nulle part ailleurs. */
|
|
171
171
|
driver: {
|
|
172
172
|
kind: "sql";
|
|
@@ -174,9 +174,9 @@ export interface IMigrationReport {
|
|
|
174
174
|
ddl: string;
|
|
175
175
|
historyTable: string;
|
|
176
176
|
/** La base visée, sans identifiant ni mot de passe. */
|
|
177
|
-
target?: string;
|
|
177
|
+
target?: string | undefined;
|
|
178
178
|
/** `true` quand la cible vient de `NF_MIGRATE_DATABASE_URL`. */
|
|
179
|
-
fromMigrateUrl?: boolean;
|
|
179
|
+
fromMigrateUrl?: boolean | undefined;
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
/** Grille des codes de sortie — **figée, jamais réaffectée**. */
|
|
@@ -34,7 +34,7 @@ export type MigrationNameCheck = {
|
|
|
34
34
|
* geste qui échoue est pire que ne rien proposer, parce qu'il fait perdre
|
|
35
35
|
* un aller-retour ET la confiance dans les autres suggestions.
|
|
36
36
|
*/
|
|
37
|
-
suggestion?: string;
|
|
37
|
+
suggestion?: string | undefined;
|
|
38
38
|
};
|
|
39
39
|
/**
|
|
40
40
|
* Dérive un nom acceptable d'une saisie qui ne l'est pas.
|
|
@@ -98,9 +98,9 @@ export interface IMigrationVerdict {
|
|
|
98
98
|
/** Connecteur concerné. */
|
|
99
99
|
connector: string;
|
|
100
100
|
/** Source de migrations concernée, quand le refus en désigne une. */
|
|
101
|
-
source?: string;
|
|
101
|
+
source?: string | undefined;
|
|
102
102
|
/** Tag concerné, quand le refus en désigne un. */
|
|
103
|
-
tag?: string;
|
|
103
|
+
tag?: string | undefined;
|
|
104
104
|
/** Faits constatés, sans mise en forme — de quoi rendre la phrase. */
|
|
105
105
|
facts: Record<string, string | number | boolean | readonly string[]>;
|
|
106
106
|
/** Ce qu'il faut faire ensuite, du plus direct au plus assumé. */
|
|
@@ -13,12 +13,12 @@ export interface DrizzleOrmOptions {
|
|
|
13
13
|
*/
|
|
14
14
|
dialect?: SqlDialect;
|
|
15
15
|
/** Fichier SQLite (`":memory:"` par défaut) — dialecte `sqlite` uniquement. */
|
|
16
|
-
filename?: string;
|
|
16
|
+
filename?: string | undefined;
|
|
17
17
|
/**
|
|
18
18
|
* Chaîne de connexion (`postgres://…`, `mysql://…`) — dialectes `postgres`/
|
|
19
19
|
* `mysql`. Requise pour ces dialectes.
|
|
20
20
|
*/
|
|
21
|
-
url?: string;
|
|
21
|
+
url?: string | undefined;
|
|
22
22
|
/**
|
|
23
23
|
* Container du service qui construit l'ORM — il porte le `syslog` du kernel.
|
|
24
24
|
*
|
|
@@ -28,7 +28,7 @@ export interface DrizzleOrmOptions {
|
|
|
28
28
|
* échecs des sondes, jamais le diagnostic de la reprise. Omis en usage
|
|
29
29
|
* direct (bancs de test) : l'ORM reste alors autonome, comme avant.
|
|
30
30
|
*/
|
|
31
|
-
container?: Container;
|
|
31
|
+
container?: Container | undefined;
|
|
32
32
|
/**
|
|
33
33
|
* Le schéma doit-il être DÉRIVÉ du code à la connexion ?
|
|
34
34
|
*
|
|
@@ -15,7 +15,7 @@ import type { Criteria, IRepository, ITransaction, RepositoryReadOptions, Update
|
|
|
15
15
|
* La surface d'exécution native qui DIVERGE (`db.all` vs `db.execute().rows`)
|
|
16
16
|
* est routée par le `queryKit` — jamais ici.
|
|
17
17
|
*/
|
|
18
|
-
export type DrizzleDb = BetterSQLite3Database
|
|
18
|
+
export type DrizzleDb = BetterSQLite3Database;
|
|
19
19
|
/** Table Drizzle multi-dialecte (l'union honnête des variantes colKit). */
|
|
20
20
|
export type DrizzleTable = SQLiteTable | PgTable | MySqlTable;
|
|
21
21
|
/** Colonne Drizzle dialecte-agnostique (classe de base — acceptée par les
|
|
@@ -24,9 +24,9 @@ export interface IResolvedForeignKey {
|
|
|
24
24
|
/** Colonnes visées, dans le même ordre que {@link columns}. */
|
|
25
25
|
readonly foreignColumns: readonly string[];
|
|
26
26
|
/** Politique d'effacement du parent (`restrict`, `set null`, `cascade`…). */
|
|
27
|
-
readonly onDelete?: string;
|
|
27
|
+
readonly onDelete?: string | undefined;
|
|
28
28
|
/** Politique de mise à jour de la clé du parent. */
|
|
29
|
-
readonly onUpdate?: string;
|
|
29
|
+
readonly onUpdate?: string | undefined;
|
|
30
30
|
}
|
|
31
31
|
/** Une table à créer, et les tables que ses clés étrangères désignent. */
|
|
32
32
|
export interface ITableDependency {
|