@nodefony/drizzle 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.
- package/README.md +19 -19
- package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/index.js +9 -6
- package/dist/nodefony/command/migrateShared.js +17 -1
- package/dist/nodefony/command/orm-generate.js +14 -10
- package/dist/nodefony/command/orm-migrate-baseline.js +5 -2
- package/dist/nodefony/command/orm-reset.js +1 -0
- package/dist/nodefony/config/defineModuleConfig.js +23 -7
- package/dist/nodefony/entity/userTable.js +4 -1
- package/dist/nodefony/registerStores.js +4 -28
- package/dist/nodefony/service/DrizzleService.js +42 -16
- package/dist/nodefony/src/DrizzleAuditStore.js +14 -2
- package/dist/nodefony/src/DrizzleIdempotencyStore.js +14 -2
- package/dist/nodefony/src/DrizzleTokenStore.js +13 -1
- package/dist/nodefony/src/DrizzleTotpSecretStore.js +13 -1
- package/dist/nodefony/src/DrizzleWebAuthnCredentialStore.js +13 -1
- package/dist/nodefony/src/DrizzleWebhookStore.js +13 -1
- package/dist/nodefony/src/SessionStorage.js +8 -0
- package/dist/nodefony/src/connectorTarget.js +1 -1
- package/dist/nodefony/src/frameworkConnector.js +29 -0
- package/dist/nodefony/src/memoryDatabase.js +13 -0
- package/dist/nodefony/src/migrator/DrizzleMigrator.js +3 -3
- package/dist/nodefony/src/migrator/adopt.js +1 -1
- package/dist/nodefony/src/migrator/appSchema.js +44 -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/drivers/sqliteDriver.js +1 -0
- 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 +41 -6
- package/dist/nodefony/src/migrator/paths.js +55 -1
- package/dist/nodefony/src/migrator/rebuildCopy.js +6 -6
- package/dist/nodefony/src/migrator/refusals.js +50 -1
- package/dist/nodefony/src/migrator/resolve.js +59 -9
- package/dist/nodefony/src/migrator/sources.js +13 -8
- package/dist/nodefony/src/orm-core/DrizzleOrm.js +51 -30
- package/dist/nodefony/src/orm-core/DrizzleRepository.js +38 -26
- package/dist/nodefony/src/queryKit.js +5 -5
- package/dist/nodefony/src/safeTarget.js +1 -0
- package/dist/types/nodefony/command/migrateShared.d.ts +10 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +5 -1
- package/dist/types/nodefony/entity/userTable.d.ts +46 -3
- package/dist/types/nodefony/registerStores.d.ts +4 -3
- package/dist/types/nodefony/src/DrizzleAuditStore.d.ts +6 -0
- package/dist/types/nodefony/src/DrizzleIdempotencyStore.d.ts +8 -1
- package/dist/types/nodefony/src/DrizzleTokenStore.d.ts +6 -0
- package/dist/types/nodefony/src/DrizzleTotpSecretStore.d.ts +6 -0
- package/dist/types/nodefony/src/DrizzleUserRepository.d.ts +1 -1
- package/dist/types/nodefony/src/DrizzleWebAuthnCredentialStore.d.ts +6 -0
- package/dist/types/nodefony/src/DrizzleWebhookStore.d.ts +6 -0
- package/dist/types/nodefony/src/SessionStorage.d.ts +6 -0
- package/dist/types/nodefony/src/connectorTarget.d.ts +1 -1
- package/dist/types/nodefony/src/frameworkConnector.d.ts +24 -0
- package/dist/types/nodefony/src/memoryDatabase.d.ts +10 -0
- package/dist/types/nodefony/src/migrator/DrizzleMigrator.d.ts +4 -4
- package/dist/types/nodefony/src/migrator/appSchema.d.ts +25 -0
- 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/kit.d.ts +15 -0
- package/dist/types/nodefony/src/migrator/name.d.ts +1 -1
- package/dist/types/nodefony/src/migrator/paths.d.ts +35 -0
- package/dist/types/nodefony/src/migrator/refusals.d.ts +36 -1
- package/dist/types/nodefony/src/migrator/resolve.d.ts +44 -1
- package/dist/types/nodefony/src/migrator/types.d.ts +2 -2
- package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +13 -2
- 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 +31 -28
- package/docs/migrations.md +39 -3
- package/package.json +19 -20
|
@@ -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);
|
|
@@ -107,6 +107,16 @@ export declare abstract class OrmMigrateCommand extends Command {
|
|
|
107
107
|
}>;
|
|
108
108
|
config: IDrizzleConfig;
|
|
109
109
|
} | null;
|
|
110
|
+
/**
|
|
111
|
+
* Arrête une commande qui ÉCRIRAIT une migration pour un connecteur
|
|
112
|
+
* secondaire dont le nom est celui d'un dossier de dialecte — son dossier
|
|
113
|
+
* propre se confondrait avec celui des migrations du framework.
|
|
114
|
+
*
|
|
115
|
+
* @param connector - connecteur résolu.
|
|
116
|
+
* @param json - mode machine.
|
|
117
|
+
* @returns `true` si la commande est arrêtée.
|
|
118
|
+
*/
|
|
119
|
+
protected refuseReservedConnector(connector: string, json?: boolean): boolean;
|
|
110
120
|
/**
|
|
111
121
|
* Construit l'applicateur d'un connecteur résolu.
|
|
112
122
|
*
|
|
@@ -13,10 +13,14 @@ import type { IDrizzleConfig, IDrizzleConfigInput } from "../interfaces/IDrizzle
|
|
|
13
13
|
* au boot — cf audit config ORM 2026-06 §3.2.
|
|
14
14
|
*
|
|
15
15
|
* @param config - configuration brute (sections omises = défauts sûrs).
|
|
16
|
+
* @param declared - ce que l'APPLICATION a écrit elle-même. Au boot, `config`
|
|
17
|
+
* arrive déjà fusionné avec les défauts du module (`config.ts`) : on ne peut
|
|
18
|
+
* plus y distinguer un `default` demandé d'un `default` posé par défaut. Omis,
|
|
19
|
+
* `config` fait foi (appel direct, tests).
|
|
16
20
|
* @returns config validée, surchargée par l'env, et gelée.
|
|
17
21
|
* @throws ZodError si la config est invalide.
|
|
18
22
|
*/
|
|
19
|
-
export declare function defineDrizzleConfig(config?: IDrizzleConfigInput): IDrizzleConfig;
|
|
23
|
+
export declare function defineDrizzleConfig(config?: IDrizzleConfigInput, declared?: IDrizzleConfigInput): IDrizzleConfig;
|
|
20
24
|
/**
|
|
21
25
|
* JSON Schema introspectable de la config Drizzle — destiné au formulaire
|
|
22
26
|
* d'édition Studio (futur) et à la documentation générée.
|
|
@@ -1,16 +1,59 @@
|
|
|
1
|
-
import type { SQLiteTable } from "drizzle-orm/sqlite-core";
|
|
1
|
+
import type { AnySQLiteColumn, SQLiteTable } from "drizzle-orm/sqlite-core";
|
|
2
|
+
import type { AnyPgColumn, PgTable } from "drizzle-orm/pg-core";
|
|
3
|
+
import type { AnyMySqlColumn, MySqlTable } from "drizzle-orm/mysql-core";
|
|
2
4
|
import type { IEntity } from "@nodefony/orm-core";
|
|
3
5
|
import type { SqlDialect } from "../interfaces/IDrizzleConfig.js";
|
|
6
|
+
/**
|
|
7
|
+
* Factory de la table `User` — comme {@link FrameworkTableFactory}, mais sa clé
|
|
8
|
+
* primaire reste ATTEIGNABLE au type.
|
|
9
|
+
*
|
|
10
|
+
* 🔴 Sans cela, une application ne peut pas RÉFÉRENCER l'utilisateur, et c'est
|
|
11
|
+
* la première relation que quiconque écrit : `create entity Message
|
|
12
|
+
* "author:ref(User)"` rend `.references(() => userTable.id)`, qui ne compilait
|
|
13
|
+
* pas (`TS2339: Property 'id' does not exist on type 'SQLiteTable<TableConfig>'`).
|
|
14
|
+
* La table est bâtie depuis une spec dont les colonnes sont calculées
|
|
15
|
+
* (`Object.fromEntries`) : l'inférence les perd, et le type rendu ne porte plus
|
|
16
|
+
* aucune colonne — alors qu'elles existent toutes à l'exécution.
|
|
17
|
+
*
|
|
18
|
+
* Cette précision reste LOCALE à `User`, et n'est pas remontée dans le colKit :
|
|
19
|
+
* les tables du framework n'ont pas toutes une colonne `id` (`session` et
|
|
20
|
+
* `idempotency_key` portent une autre clé), donc l'y déclarer mentirait sur
|
|
21
|
+
* elles. `tests/unit/userTableReference.test.ts` garde le fait — par sa
|
|
22
|
+
* COMPILATION, qu'un cas d'exécution ne pourrait pas prouver.
|
|
23
|
+
*/
|
|
24
|
+
export interface UserTableFactory {
|
|
25
|
+
(dialect: "sqlite"): SQLiteTable & {
|
|
26
|
+
readonly id: AnySQLiteColumn;
|
|
27
|
+
};
|
|
28
|
+
(dialect: "postgres"): PgTable & {
|
|
29
|
+
readonly id: AnyPgColumn;
|
|
30
|
+
};
|
|
31
|
+
(dialect: "mysql"): MySqlTable & {
|
|
32
|
+
readonly id: AnyMySqlColumn;
|
|
33
|
+
};
|
|
34
|
+
(dialect?: SqlDialect): (SQLiteTable & {
|
|
35
|
+
readonly id: AnySQLiteColumn;
|
|
36
|
+
}) | (PgTable & {
|
|
37
|
+
readonly id: AnyPgColumn;
|
|
38
|
+
}) | (MySqlTable & {
|
|
39
|
+
readonly id: AnyMySqlColumn;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
4
42
|
/**
|
|
5
43
|
* Factory de la table `User` pour un dialecte donné (mémoïsée — une instance
|
|
6
44
|
* par dialecte). Les trois dialectes sont portés (`sqlite` par défaut).
|
|
7
45
|
*/
|
|
8
|
-
export declare const createUserTable:
|
|
46
|
+
export declare const createUserTable: UserTableFactory;
|
|
9
47
|
/**
|
|
10
48
|
* Variante SQLite de la table `User` (dialecte par défaut) — export conservé
|
|
11
49
|
* pour l'usage direct/banc-test (ex. module mediasoup).
|
|
50
|
+
*
|
|
51
|
+
* Son type n'est PAS réécrit en `SQLiteTable` : l'annoter ainsi effaçait la
|
|
52
|
+
* clé primaire que la factory vient d'exposer, et c'était la moitié du défaut.
|
|
12
53
|
*/
|
|
13
|
-
export declare const userTable: SQLiteTable
|
|
54
|
+
export declare const userTable: SQLiteTable<import("drizzle-orm/sqlite-core").TableConfig> & {
|
|
55
|
+
readonly id: AnySQLiteColumn;
|
|
56
|
+
};
|
|
14
57
|
/**
|
|
15
58
|
* Forme plate d'une ligne `User` renvoyée par le repository de base — le contrat
|
|
16
59
|
* `IUserRow` de `@nodefony/user`, ré-exporté sous son nom historique.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FRAMEWORK_CONNECTOR } from "./src/frameworkConnector.js";
|
|
1
2
|
import type { SqlDialect } from "./config/config.js";
|
|
2
3
|
/**
|
|
3
4
|
* AUTO-ENREGISTREMENT des backends framework portés par Drizzle — « charger le
|
|
@@ -20,10 +21,10 @@ import type { SqlDialect } from "./config/config.js";
|
|
|
20
21
|
* boot (« store inconnu ») — jamais de table fantôme ni d'erreur SQL différée.
|
|
21
22
|
*/
|
|
22
23
|
/**
|
|
23
|
-
* Connecteur conventionnel qui héberge le schéma framework
|
|
24
|
-
* `
|
|
24
|
+
* Connecteur conventionnel qui héberge le schéma framework — défini dans
|
|
25
|
+
* `src/frameworkConnector.ts`, que les migrations lisent aussi.
|
|
25
26
|
*/
|
|
26
|
-
export
|
|
27
|
+
export { FRAMEWORK_CONNECTOR };
|
|
27
28
|
/** Bilan de l'auto-enregistrement (loggé par le module — jamais silencieux). */
|
|
28
29
|
export interface IFrameworkStoresReport {
|
|
29
30
|
/** Entités déclarées par l'auto-register (tables créées au connect). */
|
|
@@ -47,6 +47,12 @@ export declare class DrizzleAuditStore implements IAuditStore {
|
|
|
47
47
|
* — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
|
|
48
48
|
*/
|
|
49
49
|
get location(): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
52
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
53
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
54
|
+
*/
|
|
55
|
+
get connector(): string | undefined;
|
|
50
56
|
/**
|
|
51
57
|
* Construit le store depuis un {@link DrizzleOrm}. Le handle est résolu **lazy**
|
|
52
58
|
* (gardé par `isConnected()` → `null` tant que l'ORM n'est pas/plus connecté).
|
|
@@ -73,8 +73,9 @@ export declare class DrizzleIdempotencyStore implements IIdempotencyStore {
|
|
|
73
73
|
* base ({@link DrizzleOrm.location}) pour Studio. Lazy (comme `resolveDb`) car
|
|
74
74
|
* le store est fabriqué AVANT le connect de l'ORM → l'emplacement n'est lisible
|
|
75
75
|
* qu'une fois l'ORM enregistré (lu au `onReady`, pas à la construction).
|
|
76
|
+
* @param connector - connecteur ORM qui porte la table, publié au registre des stores.
|
|
76
77
|
*/
|
|
77
|
-
constructor(resolveDb: () => DrizzleDb | null, now?: () => number, leaseMs?: number, ttlMs?: number, table?: DrizzleTable, resolveLocation?: () => string | undefined);
|
|
78
|
+
constructor(resolveDb: () => DrizzleDb | null, now?: () => number, leaseMs?: number, ttlMs?: number, table?: DrizzleTable, resolveLocation?: () => string | undefined, connector?: string);
|
|
78
79
|
/**
|
|
79
80
|
* Emplacement physique de la base (fichier SQLite) pour l'écran Studio « Stores »
|
|
80
81
|
* — lu par `readStoreLocation`. Résolu **lazy** (l'ORM n'existe pas à la
|
|
@@ -82,6 +83,12 @@ export declare class DrizzleIdempotencyStore implements IIdempotencyStore {
|
|
|
82
83
|
* ou sur un backend réseau (pg/mysql → voir l'infra déclarée).
|
|
83
84
|
*/
|
|
84
85
|
get location(): string | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
88
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
89
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
90
|
+
*/
|
|
91
|
+
get connector(): string | undefined;
|
|
85
92
|
/**
|
|
86
93
|
* Construit le store depuis un {@link DrizzleOrm}. Le handle est résolu **lazy**
|
|
87
94
|
* (gardé par `isConnected()` → `null` tant que l'ORM n'est pas/plus connecté).
|
|
@@ -46,6 +46,12 @@ export declare class DrizzleTokenStore implements ITokenStore {
|
|
|
46
46
|
* — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
|
|
47
47
|
*/
|
|
48
48
|
get location(): string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
51
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
52
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
53
|
+
*/
|
|
54
|
+
get connector(): string | undefined;
|
|
49
55
|
/**
|
|
50
56
|
* Construit le store depuis un {@link DrizzleOrm} connecté. Les entités
|
|
51
57
|
* (`registerTokenEntities`) doivent avoir été enregistrées **avant**
|
|
@@ -35,6 +35,12 @@ export declare class DrizzleTotpSecretStore implements ITotpSecretStore {
|
|
|
35
35
|
* — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
|
|
36
36
|
*/
|
|
37
37
|
get location(): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
40
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
41
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
42
|
+
*/
|
|
43
|
+
get connector(): string | undefined;
|
|
38
44
|
/**
|
|
39
45
|
* Construit le store depuis un {@link DrizzleOrm} connecté. L'entité
|
|
40
46
|
* (`registerTotpSecretEntity`) doit avoir été enregistrée **avant** `orm.connect()`.
|
|
@@ -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
|
/**
|
|
@@ -34,6 +34,12 @@ export declare class DrizzleWebAuthnCredentialStore implements IWebAuthnCredenti
|
|
|
34
34
|
* — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
|
|
35
35
|
*/
|
|
36
36
|
get location(): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
39
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
40
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
41
|
+
*/
|
|
42
|
+
get connector(): string | undefined;
|
|
37
43
|
/**
|
|
38
44
|
* Construit le store depuis un {@link DrizzleOrm} connecté. L'entité
|
|
39
45
|
* (`registerWebAuthnCredentialEntity`) doit avoir été enregistrée **avant**
|
|
@@ -51,6 +51,12 @@ export declare class DrizzleWebhookStore implements IWebhookStore {
|
|
|
51
51
|
* — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
|
|
52
52
|
*/
|
|
53
53
|
get location(): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
56
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
57
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
58
|
+
*/
|
|
59
|
+
get connector(): string | undefined;
|
|
54
60
|
/**
|
|
55
61
|
* Construit le store depuis un {@link DrizzleOrm} connecté. L'entité
|
|
56
62
|
* (`registerWebhookEndpointEntity`) doit avoir été enregistrée **avant**
|
|
@@ -31,6 +31,12 @@ declare class SessionStorage implements ISessionStorage {
|
|
|
31
31
|
* DÉFENSIVE (getter `location` optionnel) — l'ORM base `IOrm` ne l'expose pas.
|
|
32
32
|
*/
|
|
33
33
|
get location(): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Connecteur ORM qui porte les sessions — lu par `readStoreConnector` pour le
|
|
36
|
+
* registre des stores. `undefined` tant que l'ORM de ce connecteur n'est pas
|
|
37
|
+
* enregistré : on ne publie pas un rattachement que rien ne porte.
|
|
38
|
+
*/
|
|
39
|
+
get connector(): string | undefined;
|
|
34
40
|
read(id: string): Promise<ISerializedSession>;
|
|
35
41
|
start(id: string): Promise<ISerializedSession>;
|
|
36
42
|
write(id: string, data: ISerializedSession): Promise<ISerializedSession>;
|
|
@@ -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.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connecteur conventionnel qui héberge le schéma framework (même convention que
|
|
3
|
+
* `SESSION_CONNECTOR` : `"default"` pour Drizzle, `"nodefony"` pour Mongoose).
|
|
4
|
+
*
|
|
5
|
+
* Vit dans ce fichier sans dépendance parce que DEUX familles le lisent : le
|
|
6
|
+
* registre des stores (où résoudre les briques du framework) et les migrations
|
|
7
|
+
* (qui les reçoit). Une copie du littéral de chaque côté dériverait en silence.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FRAMEWORK_CONNECTOR = "default";
|
|
10
|
+
/**
|
|
11
|
+
* Ce connecteur possède-t-il les migrations du framework et de l'application ?
|
|
12
|
+
*
|
|
13
|
+
* **Seul le connecteur du framework les possède.** Le dossier des migrations de
|
|
14
|
+
* l'application (`migrations/<dialect>`) ne porte aucune notion de connecteur :
|
|
15
|
+
* il décrit UNE base, celle où vivent les briques du framework et les entités
|
|
16
|
+
* de l'application. Les attribuer à tout connecteur faisait qu'une base
|
|
17
|
+
* secondaire (analyse, base d'un module) en mode `migrate` y créait des tables
|
|
18
|
+
* qui ne lui appartiennent pas, et qu'en mode `auto` elle annonçait à chaque
|
|
19
|
+
* démarrage des migrations qui ne la concernent pas.
|
|
20
|
+
*
|
|
21
|
+
* @param connector - nom du connecteur (clé de `connectors`).
|
|
22
|
+
* @returns `true` pour le connecteur du framework, `false` pour tout autre.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ownsSharedMigrations(connector: string): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nom de fichier d'une base SQLite en MÉMOIRE — volatile, elle repart vide à
|
|
3
|
+
* chaque démarrage.
|
|
4
|
+
*
|
|
5
|
+
* Module FEUILLE (aucun import) : l'adapter, le migrateur, les commandes et le
|
|
6
|
+
* service le consultent tous, et le placer dans l'un d'eux ferait importer le
|
|
7
|
+
* migrateur par l'adapter. Une seule écriture du littéral : c'est d'elle que
|
|
8
|
+
* dépend la règle « une base volatile dérive son schéma » (`resolveDdlMode`).
|
|
9
|
+
*/
|
|
10
|
+
export declare const MEMORY_DATABASE = ":memory:";
|
|
@@ -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;
|
|
@@ -221,3 +221,28 @@ export declare function usurpedTables(tables: readonly IDiscoveredTable[], frame
|
|
|
221
221
|
* @returns les entités et leur table, dans l'ordre du registre.
|
|
222
222
|
*/
|
|
223
223
|
export declare function registeredTables(connector: string): IExpectedEntity[];
|
|
224
|
+
/**
|
|
225
|
+
* Tables d'un connecteur parmi celles que les fichiers fournissent — le
|
|
226
|
+
* partage qui fait qu'une migration ne décrit que SA base.
|
|
227
|
+
*
|
|
228
|
+
* Une table appartient au connecteur de l'entité qui la déclare (le REGISTRE
|
|
229
|
+
* le sait, un fichier non). Une table qu'aucune entité ne réclame reste au
|
|
230
|
+
* connecteur du framework, comme avant que les connecteurs secondaires aient
|
|
231
|
+
* leurs migrations. Sans ce partage, la table d'une entité secondaire entrait
|
|
232
|
+
* AUSSI dans les migrations du framework : créée dans une base qui ne s'en sert
|
|
233
|
+
* jamais, et proposée à la suppression le jour où elle n'y figure plus.
|
|
234
|
+
*
|
|
235
|
+
* @param tables - tables fournies par les fichiers de l'application.
|
|
236
|
+
* @param connector - connecteur visé.
|
|
237
|
+
* @param owners - `table → connecteurs` qui la déclarent ; par défaut, le registre.
|
|
238
|
+
* @returns les tables de ce connecteur, dans l'ordre reçu.
|
|
239
|
+
*/
|
|
240
|
+
export declare function tablesOfConnector<T extends {
|
|
241
|
+
tableName: string;
|
|
242
|
+
}>(tables: readonly T[], connector: string, owners?: ReadonlyMap<string, ReadonlySet<string>>): T[];
|
|
243
|
+
/**
|
|
244
|
+
* Pour chaque table enregistrée, les connecteurs dont une entité la déclare.
|
|
245
|
+
*
|
|
246
|
+
* @returns la table de correspondance, lue dans le registre des entités.
|
|
247
|
+
*/
|
|
248
|
+
export declare function registeredOwners(): Map<string, Set<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é.
|