@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
package/README.md
CHANGED
|
@@ -16,29 +16,29 @@ npm install @nodefony/drizzle@alpha drizzle-orm better-sqlite3
|
|
|
16
16
|
|
|
17
17
|
## Utilisation comme module Nodefony (bootable)
|
|
18
18
|
|
|
19
|
-
Ajouter `@nodefony/drizzle`
|
|
20
|
-
au boot un ORM par connecteur configuré (c'est l'ORM SQL par défaut recommandé).
|
|
19
|
+
Ajouter `@nodefony/drizzle` au manifeste `modules` de `nodefony.config.ts` : le `DrizzleService`
|
|
20
|
+
connecte au boot un ORM par connecteur configuré (c'est l'ORM SQL par défaut recommandé).
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
// nodefony.config.ts
|
|
24
|
+
import { defineConfig, use } from "nodefony";
|
|
25
|
+
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
modules: [
|
|
28
|
+
"@nodefony/http",
|
|
29
|
+
// connecte au boot, ferme au shutdown ; sans `filename`, la base vit sous var/databases/
|
|
30
|
+
use("@nodefony/drizzle", {
|
|
31
|
+
connectors: { default: { dialect: "sqlite" } },
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
});
|
|
29
35
|
```
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
`nodefony/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
connectors: {
|
|
37
|
-
default: { filename: "<root>/var/databases/nodefony-drizzle.db" },
|
|
38
|
-
// ":memory:" ou absent → base éphémère
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
```
|
|
37
|
+
Le schéma (`nodefony/config/config.ts`) porte les défauts ; l'application les surcharge par
|
|
38
|
+
`use("@nodefony/drizzle", { … })`, par `NF_DATABASE_URL` (dialecte déduit du schéma d'URL) ou par
|
|
39
|
+
l'override générique `NF__DRIZZLE__<CHEMIN>`. Sans `filename`, un connecteur SQLite écrit dans
|
|
40
|
+
`<app>/var/databases/nodefony-drizzle.db` (`nodefony-<connecteur>.db` pour les autres) ;
|
|
41
|
+
`filename: ":memory:"` donne une base éphémère, pour les tests.
|
|
42
42
|
|
|
43
43
|
Au runtime, l'ORM est récupérable via le registre :
|
|
44
44
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
|
|
2
2
|
function __decorate(decorators, target, key, desc) {
|
|
3
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorateMetadata.js
|
|
2
2
|
function __decorateMetadata(k, v) {
|
|
3
3
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
4
4
|
}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { EXIT, MIGRATION_FORMAT_VERSION, action, buildReport, exitCodeOf, meanin
|
|
|
5
5
|
import { MYSQL_LOCK_NAME_SQL } from "./nodefony/src/migrator/drivers/mysqlDriver.js";
|
|
6
6
|
import { PG_LOCK_KEY } from "./nodefony/src/migrator/drivers/postgresDriver.js";
|
|
7
7
|
import { openMigrationDriver } from "./nodefony/src/migrator/drivers/index.js";
|
|
8
|
+
import { FRAMEWORK_CONNECTOR } from "./nodefony/src/frameworkConnector.js";
|
|
8
9
|
import { APP_RANK, APP_SOURCE, FRAMEWORK_RANK, FRAMEWORK_SOURCE, defaultMigrationSources, frameworkMigrationsDir } from "./nodefony/src/migrator/paths.js";
|
|
9
10
|
import { migrationHash } from "./nodefony/src/migrator/hash.js";
|
|
10
11
|
import { loadSources, orderSources } from "./nodefony/src/migrator/sources.js";
|
|
@@ -30,15 +31,15 @@ import { DrizzleWebAuthnCredentialStore } from "./nodefony/src/DrizzleWebAuthnCr
|
|
|
30
31
|
import { DrizzleTotpSecretStore } from "./nodefony/src/DrizzleTotpSecretStore.js";
|
|
31
32
|
import { DrizzleWebhookStore } from "./nodefony/src/DrizzleWebhookStore.js";
|
|
32
33
|
import { DrizzleIdempotencyStore } from "./nodefony/src/DrizzleIdempotencyStore.js";
|
|
33
|
-
import {
|
|
34
|
+
import { isFrameworkFallbackEntity, registerDrizzleFrameworkStores } from "./nodefony/registerStores.js";
|
|
34
35
|
import OrmGenerate from "./nodefony/command/orm-generate.js";
|
|
35
36
|
import OrmMigrate from "./nodefony/command/orm-migrate.js";
|
|
36
37
|
import OrmMigrateStatus from "./nodefony/command/orm-migrate-status.js";
|
|
37
38
|
import OrmMigrateBaseline from "./nodefony/command/orm-migrate-baseline.js";
|
|
38
39
|
import OrmMigrateRepair from "./nodefony/command/orm-migrate-repair.js";
|
|
39
40
|
import OrmReset from "./nodefony/command/orm-reset.js";
|
|
40
|
-
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.
|
|
41
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.
|
|
41
|
+
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
|
|
42
|
+
import __decorate from "./_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
|
|
42
43
|
import SessionStorage from "./nodefony/src/SessionStorage.js";
|
|
43
44
|
import "./nodefony/src/migrator/index.js";
|
|
44
45
|
import { DrizzleUserRepository } from "./nodefony/src/DrizzleUserRepository.js";
|
|
@@ -76,11 +77,13 @@ let Drizzle = class Drizzle extends Module {
|
|
|
76
77
|
* (convention Zod, alignée sur `@nodefony/mongoose` — audit config ORM 2026-06).
|
|
77
78
|
*/
|
|
78
79
|
async onKernelRegister() {
|
|
79
|
-
const validated = defineDrizzleConfig(this.options ?? {});
|
|
80
|
+
const validated = defineDrizzleConfig(this.options, this.appOptions ?? {});
|
|
80
81
|
this.options = validated;
|
|
81
82
|
registerUserStore("drizzle");
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
const frameworkHost = validated.connectors.default;
|
|
84
|
+
if (validated.frameworkEntities && frameworkHost === void 0) this.log(`pas de connecteur "${FRAMEWORK_CONNECTOR}" (infrastructure déclarée non SQL) : schéma framework non déclaré sur drizzle — les stores durables vont au backend de cette infrastructure`, "INFO");
|
|
85
|
+
if (validated.frameworkEntities && frameworkHost !== void 0) {
|
|
86
|
+
const dialect = frameworkHost.dialect;
|
|
84
87
|
const report = registerDrizzleFrameworkStores(dialect);
|
|
85
88
|
if (report.unported.length) this.log(`schéma framework : entités non portées sur "${dialect}" → stores drizzle indisponibles pour [${report.unported.join(", ")}] (chantier multi-dialecte Ph.2.1)`, "WARNING");
|
|
86
89
|
if (report.appOwned.length) this.log(`schéma framework : entités déjà enregistrées par l'app [${report.appOwned.join(", ")}] — auto-register respecte l'app`, "DEBUG");
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { HISTORY_TABLE, MigrationVerdictError } from "../src/migrator/types.js";
|
|
2
2
|
import { EXIT, action, describeDiscovery, refusalInMode, renderRefusal, styleFor } from "../src/migrator/explain.js";
|
|
3
3
|
import { missingHistoryColumn } from "../src/migrator/history.js";
|
|
4
|
+
import { isReservedConnectorName } from "../src/migrator/paths.js";
|
|
4
5
|
import { buildMigrator, readMigrationEnv, resolveConnector } from "../src/migrator/resolve.js";
|
|
5
|
-
import { MigrationToolError, describeResolutionRefusal, moduleAbsent } from "../src/migrator/refusals.js";
|
|
6
|
+
import { MigrationToolError, describeResolutionRefusal, moduleAbsent, reservedConnectorName } from "../src/migrator/refusals.js";
|
|
6
7
|
import { composeReport } from "../src/migrator/status.js";
|
|
7
8
|
import { CONSOLE_DATA_RUN_PROFILE, Command, resolveColorEnabled } from "nodefony";
|
|
8
9
|
//#region nodefony/command/migrateShared.ts
|
|
@@ -180,6 +181,21 @@ var OrmMigrateCommand = class extends Command {
|
|
|
180
181
|
};
|
|
181
182
|
}
|
|
182
183
|
/**
|
|
184
|
+
* Arrête une commande qui ÉCRIRAIT une migration pour un connecteur
|
|
185
|
+
* secondaire dont le nom est celui d'un dossier de dialecte — son dossier
|
|
186
|
+
* propre se confondrait avec celui des migrations du framework.
|
|
187
|
+
*
|
|
188
|
+
* @param connector - connecteur résolu.
|
|
189
|
+
* @param json - mode machine.
|
|
190
|
+
* @returns `true` si la commande est arrêtée.
|
|
191
|
+
*/
|
|
192
|
+
refuseReservedConnector(connector, json) {
|
|
193
|
+
if (!isReservedConnectorName(connector)) return false;
|
|
194
|
+
const refusal = reservedConnectorName(connector);
|
|
195
|
+
this.fail(connector, refusal.code, refusal.summary, refusal.meaning, refusal.nextActions, json, refusal.exitCode);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
183
199
|
* Construit l'applicateur d'un connecteur résolu.
|
|
184
200
|
*
|
|
185
201
|
* @param resolution - connecteur prêt.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { HISTORY_TABLE } from "../src/migrator/types.js";
|
|
2
2
|
import { EXIT, action } from "../src/migrator/explain.js";
|
|
3
3
|
import { openMigrationDriver } from "../src/migrator/drivers/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { ownsSharedMigrations } from "../src/frameworkConnector.js";
|
|
5
|
+
import { connectorMigrationsDir, frameworkMigrationsDir } from "../src/migrator/paths.js";
|
|
5
6
|
import { createdTables, frameworkTables, loadSources } from "../src/migrator/sources.js";
|
|
6
7
|
import { appMigrationsDir, generateApplyAllowed, readMigrationEnv } from "../src/migrator/resolve.js";
|
|
7
8
|
import { summarizeGap } from "../src/migrator/schemaDiff.js";
|
|
8
9
|
import { gapAgainstDeclared } from "../src/migrator/divergence.js";
|
|
9
10
|
import { auditMigrationSql, runGenerate, stampFormatMarker } from "../src/migrator/kit.js";
|
|
10
|
-
import { collectTables, entityFilesOf, missingProviders, registeredTables, usurpedTables, writeCustomMigration, writeKitConfig, writeSchemaModule } from "../src/migrator/appSchema.js";
|
|
11
|
+
import { collectTables, entityFilesOf, missingProviders, registeredTables, tablesOfConnector, usurpedTables, writeCustomMigration, writeKitConfig, writeSchemaModule } from "../src/migrator/appSchema.js";
|
|
11
12
|
import { checkMigrationName } from "../src/migrator/name.js";
|
|
12
13
|
import { repairRebuildCopy } from "../src/migrator/rebuildCopy.js";
|
|
13
14
|
import { readJournal, tablesPresentIn } from "../src/migrator/adopt.js";
|
|
@@ -113,6 +114,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
113
114
|
if (!resolved) return this;
|
|
114
115
|
const { resolution, config } = resolved;
|
|
115
116
|
const connector = resolution.connector;
|
|
117
|
+
if (this.refuseReservedConnector(connector, opts.json)) return this;
|
|
116
118
|
const name = this.#nameOrFail(opts, connector);
|
|
117
119
|
if (name === null) return this;
|
|
118
120
|
const root = this.#projectRoot();
|
|
@@ -121,7 +123,8 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
121
123
|
this.fail(connector, "NF_MIGRATE_UNAVAILABLE", "La racine de l'application n'a pas pu être résolue : impossible de savoir où écrire les migrations.", "Le dossier des migrations se résout depuis la racine de l'application, jamais depuis le répertoire courant — sans quoi la commande serait juste ou fausse selon l'endroit d'où on la tape.", [action("nodefony inspect config --json")], opts.json);
|
|
122
124
|
return this;
|
|
123
125
|
}
|
|
124
|
-
const
|
|
126
|
+
const connectorDir = connectorMigrationsDir(migrationsDir, connector) ?? migrationsDir;
|
|
127
|
+
const outDir = path.join(connectorDir, resolution.dialect);
|
|
125
128
|
const relative = (p) => path.relative(root, p).split(path.sep).join("/");
|
|
126
129
|
try {
|
|
127
130
|
if (opts.custom === true) {
|
|
@@ -169,8 +172,9 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
169
172
|
const frameworkRoot = path.dirname(await frameworkMigrationsDir());
|
|
170
173
|
const belongsToFramework = (file) => !path.relative(frameworkRoot, file).startsWith("..");
|
|
171
174
|
const mine = found.filter((t) => !belongsToFramework(t.file));
|
|
172
|
-
const
|
|
173
|
-
const
|
|
175
|
+
const ofConnector = tablesOfConnector(mine, connector);
|
|
176
|
+
const tables = ofConnector.filter((t) => t.dialect === dialect);
|
|
177
|
+
const otherDialect = ofConnector.filter((t) => t.dialect !== dialect).map((t) => ({
|
|
174
178
|
table: t.tableName,
|
|
175
179
|
dialect: t.dialect ?? "inconnu",
|
|
176
180
|
file: relative(t.file)
|
|
@@ -181,7 +185,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
181
185
|
otherDialect,
|
|
182
186
|
unreadable
|
|
183
187
|
};
|
|
184
|
-
const framework = new Set(await frameworkTables(dialect));
|
|
188
|
+
const framework = new Set(ownsSharedMigrations(connector) ? await frameworkTables(dialect) : []);
|
|
185
189
|
const usurped = usurpedTables(tables, framework);
|
|
186
190
|
if (usurped.length > 0) {
|
|
187
191
|
const list = usurped.map((t) => ` • « ${t.tableName} », exportée par ${relative(t.file)} (${t.exportName})`).join("\n");
|
|
@@ -223,7 +227,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
223
227
|
configRel: relative(configFile),
|
|
224
228
|
name,
|
|
225
229
|
label: `le connecteur « ${connector} » (${dialect})`,
|
|
226
|
-
regenerateCommand: `nodefony orm:generate --name ${name}`
|
|
230
|
+
regenerateCommand: ownsSharedMigrations(connector) ? `nodefony orm:generate --name ${name}` : `nodefony orm:generate --connector ${connector} --name ${name}`
|
|
227
231
|
});
|
|
228
232
|
} finally {
|
|
229
233
|
await fs.rm(work, {
|
|
@@ -242,7 +246,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
242
246
|
], opts.json, EXIT.actionRequired);
|
|
243
247
|
return this;
|
|
244
248
|
}
|
|
245
|
-
return
|
|
249
|
+
return this.#emit({
|
|
246
250
|
formatVersion: 1,
|
|
247
251
|
connector,
|
|
248
252
|
generated: false,
|
|
@@ -285,11 +289,11 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
285
289
|
], opts.json, EXIT.actionRequired, discovery);
|
|
286
290
|
return this;
|
|
287
291
|
}
|
|
288
|
-
return
|
|
292
|
+
return this.#emit({
|
|
289
293
|
formatVersion: 1,
|
|
290
294
|
connector,
|
|
291
295
|
generated: true,
|
|
292
|
-
tag: added
|
|
296
|
+
tag: added.at(-1) ?? null,
|
|
293
297
|
files: written,
|
|
294
298
|
warnings,
|
|
295
299
|
unreadable,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { HISTORY_TABLE } from "../src/migrator/types.js";
|
|
2
2
|
import { EXIT, MIGRATION_FORMAT_VERSION, action, renderStatus } from "../src/migrator/explain.js";
|
|
3
|
+
import { ownsSharedMigrations } from "../src/frameworkConnector.js";
|
|
4
|
+
import { connectorMigrationsDir } from "../src/migrator/paths.js";
|
|
3
5
|
import { frameworkTables } from "../src/migrator/sources.js";
|
|
4
6
|
import { appMigrationsDir } from "../src/migrator/resolve.js";
|
|
5
7
|
import { summarizeGap } from "../src/migrator/schemaDiff.js";
|
|
@@ -119,13 +121,14 @@ var OrmMigrateBaseline = class extends OrmMigrateCommand {
|
|
|
119
121
|
* @returns ce qui a été écrit, ou `null` si la commande a déjà refusé.
|
|
120
122
|
*/
|
|
121
123
|
async #fromDatabase(opts, resolution, config) {
|
|
124
|
+
if (this.refuseReservedConnector(resolution.connector, opts.json)) return null;
|
|
122
125
|
const root = this.kernel.path;
|
|
123
126
|
const dir = appMigrationsDir(this.kernel, config.migrations.dir);
|
|
124
127
|
if (dir === void 0) {
|
|
125
128
|
this.fail(resolution.connector, "NF_MIGRATE_UNAVAILABLE", "La racine de l'application n'a pas pu être résolue : impossible de savoir où écrire la migration de référence.", "Le dossier des migrations se résout depuis la racine de l'application, jamais depuis le répertoire courant — sans quoi la commande serait juste ou fausse selon l'endroit d'où on la tape.", [action("nodefony inspect config --json")], opts.json);
|
|
126
129
|
return null;
|
|
127
130
|
}
|
|
128
|
-
const outDir = path.join(dir, resolution.dialect);
|
|
131
|
+
const outDir = path.join(connectorMigrationsDir(dir, resolution.connector) ?? dir, resolution.dialect);
|
|
129
132
|
const journal = await readJournal(outDir);
|
|
130
133
|
if (journal !== null && journal.entries.length > 0) {
|
|
131
134
|
this.fail(resolution.connector, "NF_MIGRATE_BASELINE_NOT_EMPTY", `Rien n'a été écrit : ce dossier porte déjà ${journal.entries.length} migration(s) (${outDir}).`, "La lecture de la base ne sert qu'à DÉMARRER un historique qui n'existe pas encore. Ici il en existe un : c'est lui qui fait foi, et la base s'adopte alors telle quelle. Si ces migrations décrivent bien l'état de la base, `orm:migrate:baseline` sans option les inscrit ; si elles décrivent autre chose, c'est un écart à regarder, pas une référence à réécrire.", [action(`nodefony orm:migrate:status --connector ${resolution.connector} --json`), action(`nodefony orm:migrate:baseline --connector ${resolution.connector}`)], opts.json, EXIT.actionRequired);
|
|
@@ -141,7 +144,7 @@ var OrmMigrateBaseline = class extends OrmMigrateCommand {
|
|
|
141
144
|
outDir,
|
|
142
145
|
dialect: resolution.dialect,
|
|
143
146
|
target: resolution.target,
|
|
144
|
-
excludedTables: [...await frameworkTables(resolution.dialect), HISTORY_TABLE],
|
|
147
|
+
excludedTables: [...ownsSharedMigrations(resolution.connector) ? await frameworkTables(resolution.dialect) : [], HISTORY_TABLE],
|
|
145
148
|
declaredTables: registeredTables(resolution.connector).map((t) => t.table),
|
|
146
149
|
name: verdict.name,
|
|
147
150
|
workDir: path.join(root, "node_modules", ".cache", "nodefony", "orm-adopt")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { action } from "../src/migrator/explain.js";
|
|
2
|
+
import "../src/memoryDatabase.js";
|
|
2
3
|
import { openMigrationDriver } from "../src/migrator/drivers/index.js";
|
|
3
4
|
import { readHistory } from "../src/migrator/history.js";
|
|
4
5
|
import { readMigrationEnv, resetAllowed } from "../src/migrator/resolve.js";
|
|
@@ -10,13 +10,25 @@ import { z } from "zod";
|
|
|
10
10
|
*
|
|
11
11
|
* - Infra `database` (`NF_DATABASE_URL`, alias `DATABASE_URL`) de famille SQL →
|
|
12
12
|
* dialecte + cible du connecteur primaire (`default`, sinon le premier) :
|
|
13
|
-
* `sqlite:…` → `filename` ; `postgres://…`/`mysql://…` → `url`.
|
|
14
|
-
*
|
|
13
|
+
* `sqlite:…` → `filename` ; `postgres://…`/`mysql://…` → `url`.
|
|
14
|
+
* - Infra `database` d'une AUTRE famille (`mongodb://…`) : l'URL appartient à
|
|
15
|
+
* `@nodefony/mongoose`, Drizzle ne la lit pas — et il ne crée pas non plus le
|
|
16
|
+
* connecteur `default` que son schéma pose par défaut. Ce connecteur-là, personne
|
|
17
|
+
* ne l'a demandé : il ouvrait une base SQLite parallèle où le schéma du
|
|
18
|
+
* framework se déclarait en double. Un `default` ÉCRIT par l'application, lui,
|
|
19
|
+
* est gardé — c'est ce qui sépare « par défaut » de « demandé ».
|
|
20
|
+
*
|
|
21
|
+
* @param config - config validée (défauts du schéma posés)
|
|
22
|
+
* @param declaredDefault - l'application a-t-elle écrit un connecteur `default` ?
|
|
15
23
|
*/
|
|
16
|
-
function applyEnvOverrides(config) {
|
|
24
|
+
function applyEnvOverrides(config, declaredDefault) {
|
|
17
25
|
const database = resolveInfra(process.env).database;
|
|
18
|
-
if (database && database.family
|
|
19
|
-
|
|
26
|
+
if (database && database.family !== "sql" && !declaredDefault) {
|
|
27
|
+
delete config.connectors.default;
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
30
|
+
if (database?.family === "sql" && database.dialect) {
|
|
31
|
+
const target = Object.hasOwn(config.connectors, "default") ? "default" : Object.keys(config.connectors)[0];
|
|
20
32
|
const connector = target ? config.connectors[target] : void 0;
|
|
21
33
|
if (connector) {
|
|
22
34
|
connector.dialect = database.dialect;
|
|
@@ -45,12 +57,16 @@ function applyEnvOverrides(config) {
|
|
|
45
57
|
* au boot — cf audit config ORM 2026-06 §3.2.
|
|
46
58
|
*
|
|
47
59
|
* @param config - configuration brute (sections omises = défauts sûrs).
|
|
60
|
+
* @param declared - ce que l'APPLICATION a écrit elle-même. Au boot, `config`
|
|
61
|
+
* arrive déjà fusionné avec les défauts du module (`config.ts`) : on ne peut
|
|
62
|
+
* plus y distinguer un `default` demandé d'un `default` posé par défaut. Omis,
|
|
63
|
+
* `config` fait foi (appel direct, tests).
|
|
48
64
|
* @returns config validée, surchargée par l'env, et gelée.
|
|
49
65
|
* @throws ZodError si la config est invalide.
|
|
50
66
|
*/
|
|
51
|
-
function defineDrizzleConfig(config = {}) {
|
|
67
|
+
function defineDrizzleConfig(config = {}, declared = config) {
|
|
52
68
|
const parsed = parseModuleConfig(drizzleConfigSchema, config, "@nodefony/drizzle");
|
|
53
|
-
return Object.freeze(applyEnvOverrides(parsed));
|
|
69
|
+
return Object.freeze(applyEnvOverrides(parsed, declared.connectors?.default !== void 0));
|
|
54
70
|
}
|
|
55
71
|
/**
|
|
56
72
|
* JSON Schema introspectable de la config Drizzle — destiné au formulaire
|
|
@@ -81,6 +81,9 @@ const createUserTable = createFrameworkTableFactory(USER_TABLE_SPEC);
|
|
|
81
81
|
/**
|
|
82
82
|
* Variante SQLite de la table `User` (dialecte par défaut) — export conservé
|
|
83
83
|
* pour l'usage direct/banc-test (ex. module mediasoup).
|
|
84
|
+
*
|
|
85
|
+
* Son type n'est PAS réécrit en `SQLiteTable` : l'annoter ainsi effaçait la
|
|
86
|
+
* clé primaire que la factory vient d'exposer, et c'était la moitié du défaut.
|
|
84
87
|
*/
|
|
85
88
|
const userTable = createUserTable("sqlite");
|
|
86
89
|
/**
|
|
@@ -133,7 +136,7 @@ function userTableColumns(table, dialect) {
|
|
|
133
136
|
const columns = dialect === "postgres" ? getTableConfig$1(table).columns : dialect === "mysql" ? getTableConfig$2(table).columns : getTableConfig(table).columns;
|
|
134
137
|
return new Map(columns.map((column) => [column.name, {
|
|
135
138
|
notNull: column.notNull,
|
|
136
|
-
isUnique: column.isUnique
|
|
139
|
+
isUnique: column.isUnique,
|
|
137
140
|
primary: column.primary
|
|
138
141
|
}]));
|
|
139
142
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FRAMEWORK_CONNECTOR } from "./src/frameworkConnector.js";
|
|
1
2
|
import { DrizzleOrm } from "./src/orm-core/DrizzleOrm.js";
|
|
2
3
|
import "./src/orm-core/index.js";
|
|
3
4
|
import { TOKEN_ENTITY_NAMES, registerTokenEntities } from "./entity/tokenEntity.js";
|
|
@@ -19,31 +20,6 @@ import { assertUserContract } from "@nodefony/user";
|
|
|
19
20
|
import { getAuditStoreFactory, getTokenStoreFactory, getTotpStoreFactory, getWebAuthnStoreFactory, getWebhookStoreFactory, registerAuditStore, registerTokenStore, registerTotpStore, registerWebAuthnStore, registerWebhookStore } from "@nodefony/security";
|
|
20
21
|
import { getIdempotencyStoreFactory, registerIdempotencyStore } from "@nodefony/framework";
|
|
21
22
|
//#region nodefony/registerStores.ts
|
|
22
|
-
/**
|
|
23
|
-
* AUTO-ENREGISTREMENT des backends framework portés par Drizzle — « charger le
|
|
24
|
-
* module = ses backends deviennent sélectionnables par simple nom ».
|
|
25
|
-
*
|
|
26
|
-
* Appelé par `Drizzle.onKernelRegister` (config validée → dialecte du connecteur
|
|
27
|
-
* `default` connu, AVANT le connect de `onBoot` → les tables sont créées au
|
|
28
|
-
* connect). Remplace l'« approche B » historique où chaque application devait
|
|
29
|
-
* câbler `registerXStore(...)` + `registerXEntities(...)` à la main.
|
|
30
|
-
*
|
|
31
|
-
* Deux garde-fous préservent la main de l'application (customisation) :
|
|
32
|
-
* - **entité** : `entityRegistry.has(name, orm)` → une entité déjà enregistrée
|
|
33
|
-
* par l'app est respectée (jamais de doublon-throw) ;
|
|
34
|
-
* - **fabrique** : `getXStoreFactory("drizzle")` → une fabrique déjà posée par
|
|
35
|
-
* l'app garde la main (le registre est premier-arrivé-premier-servi ici).
|
|
36
|
-
*
|
|
37
|
-
* Une brique dont l'entité n'est PAS portée sur le dialecte configuré n'est NI
|
|
38
|
-
* déclarée NI fabricable : les registres reflètent le RÉEL (`listXStores()` ne
|
|
39
|
-
* promet jamais un backend qui échouerait), et la sélectionner échoue franc au
|
|
40
|
-
* boot (« store inconnu ») — jamais de table fantôme ni d'erreur SQL différée.
|
|
41
|
-
*/
|
|
42
|
-
/**
|
|
43
|
-
* Connecteur conventionnel qui héberge le schéma framework (même convention que
|
|
44
|
-
* `SESSION_CONNECTOR` : `"default"` pour Drizzle, `"nodefony"` pour Mongoose).
|
|
45
|
-
*/
|
|
46
|
-
const FRAMEWORK_CONNECTOR = "default";
|
|
47
23
|
/** Portage par entité (chantier multi-dialecte — Ph.2.1 allume les cases). */
|
|
48
24
|
const ALL_DIALECTS = [
|
|
49
25
|
"sqlite",
|
|
@@ -133,7 +109,7 @@ function registerDrizzleFrameworkStores(dialect) {
|
|
|
133
109
|
if (getTokenStoreFactory("drizzle")) return;
|
|
134
110
|
registerTokenStore("drizzle", (ctx) => {
|
|
135
111
|
const orm = resolveConnectedOrm(`tokenStore "drizzle"`, dialect);
|
|
136
|
-
const days = ctx
|
|
112
|
+
const days = ctx.config.tokenStore.retentionRevokedDays;
|
|
137
113
|
return DrizzleTokenStore.from(orm, void 0, typeof days === "number" ? days * 864e5 : void 0);
|
|
138
114
|
});
|
|
139
115
|
});
|
|
@@ -141,7 +117,7 @@ function registerDrizzleFrameworkStores(dialect) {
|
|
|
141
117
|
if (getAuditStoreFactory("drizzle")) return;
|
|
142
118
|
registerAuditStore("drizzle", (ctx) => {
|
|
143
119
|
const orm = resolveConnectedOrm(`audit.store "drizzle"`, dialect);
|
|
144
|
-
const days = ctx
|
|
120
|
+
const days = ctx.config.audit.retentionDays;
|
|
145
121
|
return DrizzleAuditStore.from(orm, void 0, typeof days === "number" ? days * 864e5 : void 0);
|
|
146
122
|
});
|
|
147
123
|
});
|
|
@@ -177,7 +153,7 @@ function registerDrizzleFrameworkStores(dialect) {
|
|
|
177
153
|
} catch {
|
|
178
154
|
return;
|
|
179
155
|
}
|
|
180
|
-
});
|
|
156
|
+
}, FRAMEWORK_CONNECTOR);
|
|
181
157
|
});
|
|
182
158
|
});
|
|
183
159
|
if (report.appOwned.includes("User")) assertAppUserEntityHonoursContract(dialect);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { buildReport, isAheadOnly, meaningOf } from "../src/migrator/explain.js";
|
|
2
2
|
import { defaultConnectorFilename } from "../src/connectorTarget.js";
|
|
3
|
+
import { MEMORY_DATABASE } from "../src/memoryDatabase.js";
|
|
4
|
+
import { FRAMEWORK_CONNECTOR, ownsSharedMigrations } from "../src/frameworkConnector.js";
|
|
3
5
|
import { defaultMigrationSources } from "../src/migrator/paths.js";
|
|
4
6
|
import { DrizzleMigrator } from "../src/migrator/DrizzleMigrator.js";
|
|
5
|
-
import { appMigrationsDir,
|
|
7
|
+
import { adviseMigrations, appMigrationsDir, connectorVersionsMigrations, readMigrationEnv, resetAllowed, resolveCheckMode, resolveDdlMode } from "../src/migrator/resolve.js";
|
|
6
8
|
import { describeDivergence } from "../src/migrator/divergence.js";
|
|
7
9
|
import { DrizzleOrm } from "../src/orm-core/DrizzleOrm.js";
|
|
8
10
|
import "../src/orm-core/index.js";
|
|
9
11
|
import { dataLoss, renderDestructive, scanDestructive, summarizeDestructive } from "../src/migrator/destructive.js";
|
|
10
12
|
import { BootConfigurationError, Service, runNeedsExternalServices } from "nodefony";
|
|
11
|
-
import { diagnoseConnectionFailure, parseConnectionTarget, queryFlowMonitor, resolveOrmFlowEnabled } from "@nodefony/orm-core";
|
|
13
|
+
import { describeConnectFailure, diagnoseConnectionFailure, parseConnectionTarget, queryFlowMonitor, reportOrphanEntities, resolveOrmFlowEnabled } from "@nodefony/orm-core";
|
|
12
14
|
import fs from "node:fs";
|
|
13
15
|
import path from "node:path";
|
|
14
16
|
//#region nodefony/service/DrizzleService.ts
|
|
@@ -35,7 +37,7 @@ const READINESS_POLL_MS = 15e3;
|
|
|
35
37
|
* suffit.
|
|
36
38
|
*/
|
|
37
39
|
const DDL_EXPLAINED = {
|
|
38
|
-
auto: "(dérivé du code au démarrage
|
|
40
|
+
auto: "(dérivé du code au démarrage ; les colonnes manquantes qui acceptent le vide sont ajoutées)",
|
|
39
41
|
migrate: "(migrations appliquées au démarrage, sous verrou — un seul exemplaire assumé)",
|
|
40
42
|
none: "(personne ne touche au schéma ici — un travail externe lance « nodefony orm:migrate »)"
|
|
41
43
|
};
|
|
@@ -80,22 +82,39 @@ var DrizzleService = class extends Service {
|
|
|
80
82
|
*/
|
|
81
83
|
#watchers = null;
|
|
82
84
|
constructor(module) {
|
|
83
|
-
super(serviceName, module.container, module.notificationsCenter, module.options
|
|
85
|
+
super(serviceName, module.container, module.notificationsCenter, module.options);
|
|
84
86
|
this.module = module;
|
|
85
87
|
this.module.hookKernel("onBoot", async () => {
|
|
86
88
|
queryFlowMonitor.setEnabled(resolveOrmFlowEnabled(this.kernel));
|
|
87
89
|
await this.connectAll(runNeedsExternalServices(this.kernel));
|
|
88
90
|
});
|
|
91
|
+
this.module.hookKernel("onReady", () => {
|
|
92
|
+
reportOrphanEntities((message, severity) => this.log(message, severity), this.kernel ?? this, Object.keys(this.#config()?.connectors ?? {}));
|
|
93
|
+
});
|
|
89
94
|
this.kernel?.once("onTerminate", async () => {
|
|
90
95
|
this.#stopWatchers();
|
|
91
96
|
await this.disconnectAll().catch(() => {});
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Config du module telle que le service la TROUVE.
|
|
101
|
+
*
|
|
102
|
+
* Validée par Zod au boot du kernel ; mais le service se construit aussi hors
|
|
103
|
+
* kernel (bancs, usage direct) avec une config partielle, voire absente — et
|
|
104
|
+
* « config absente → rien à connecter » est un contrat couvert par
|
|
105
|
+
* `DrizzleService.test.ts`. Le type le dit, donc chaque lecture garde.
|
|
106
|
+
*/
|
|
95
107
|
#config() {
|
|
96
108
|
return this.module.config;
|
|
97
109
|
}
|
|
98
110
|
/**
|
|
111
|
+
* Config VALIDÉE, pour les lecteurs de migrations du plan d'administration :
|
|
112
|
+
* seul un kernel les appelle, et un kernel a validé la config au boot.
|
|
113
|
+
*/
|
|
114
|
+
#validatedConfig() {
|
|
115
|
+
return this.module.config;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
99
118
|
* Connecte tous les connecteurs déclarés en config (validée Zod).
|
|
100
119
|
*
|
|
101
120
|
* @param connect - `false` pour ENREGISTRER les ORM sans ouvrir de connexion.
|
|
@@ -122,30 +141,31 @@ var DrizzleService = class extends Service {
|
|
|
122
141
|
/** Connecte un connecteur (crée le dossier de la base SQLite si nécessaire). */
|
|
123
142
|
async #connectOne(name, cfg, connect = true) {
|
|
124
143
|
const dialect = cfg.dialect ?? "sqlite";
|
|
125
|
-
const ddl = resolveDdlMode(cfg.ddl, readMigrationEnv(this.kernel),
|
|
144
|
+
const ddl = resolveDdlMode(cfg.ddl, readMigrationEnv(this.kernel), connectorVersionsMigrations(name, appMigrationsDir(this.kernel, this.#config()?.migrations?.dir ?? "migrations")), cfg.filename === MEMORY_DATABASE);
|
|
126
145
|
let filename;
|
|
127
146
|
if (dialect === "sqlite") {
|
|
128
147
|
filename = cfg.filename ?? this.#defaultFilename(name);
|
|
129
148
|
if (filename !== ":memory:") fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
130
149
|
}
|
|
131
150
|
const orm = new DrizzleOrm(name, {
|
|
151
|
+
container: this.container ?? void 0,
|
|
132
152
|
dialect,
|
|
133
153
|
filename,
|
|
134
154
|
url: cfg.url,
|
|
135
155
|
deriveSchema: ddl === "auto",
|
|
136
156
|
migrationStatus: async () => {
|
|
137
157
|
const { migrationStatusFor } = await import("../src/migrator/status.js");
|
|
138
|
-
const result = await migrationStatusFor(name, this.#
|
|
158
|
+
const result = await migrationStatusFor(name, this.#validatedConfig(), this.kernel);
|
|
139
159
|
return result.ok ? result.report : result.failure;
|
|
140
160
|
},
|
|
141
161
|
migrationPlan: async () => {
|
|
142
162
|
const { migrationPlanFor } = await import("../src/migrator/status.js");
|
|
143
|
-
const result = await migrationPlanFor(name, this.#
|
|
163
|
+
const result = await migrationPlanFor(name, this.#validatedConfig(), this.kernel);
|
|
144
164
|
return result.ok ? result.plan : result.failure;
|
|
145
165
|
},
|
|
146
166
|
applyMigrations: async () => {
|
|
147
167
|
const { applyMigrationsFor } = await import("../src/migrator/status.js");
|
|
148
|
-
const result = await applyMigrationsFor(name, this.#
|
|
168
|
+
const result = await applyMigrationsFor(name, this.#validatedConfig(), this.kernel);
|
|
149
169
|
return result.ok ? result.run : result.failure;
|
|
150
170
|
}
|
|
151
171
|
});
|
|
@@ -163,7 +183,7 @@ var DrizzleService = class extends Service {
|
|
|
163
183
|
host: null,
|
|
164
184
|
port: null
|
|
165
185
|
} : parseConnectionTarget(cfg.url));
|
|
166
|
-
throw new BootConfigurationError(`Drizzle : le connecteur "${name}" (${dialect}: ${target})
|
|
186
|
+
throw new BootConfigurationError(describeConnectFailure(`Drizzle : le connecteur "${name}" (${dialect}: ${target})`, diagnosis, cause, " 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: e });
|
|
167
187
|
}
|
|
168
188
|
this.#orms.set(name, orm);
|
|
169
189
|
this.log(`Drizzle « ${name} » connecté (${dialect}: ${target}) — schéma : ${ddl} ${DDL_EXPLAINED[ddl]}`, "INFO");
|
|
@@ -189,20 +209,26 @@ var DrizzleService = class extends Service {
|
|
|
189
209
|
const kernel = this.kernel;
|
|
190
210
|
const config = this.#config();
|
|
191
211
|
const env = readMigrationEnv(kernel);
|
|
192
|
-
const check = resolveCheckMode(config
|
|
212
|
+
const check = resolveCheckMode(config?.migrations?.check, env);
|
|
193
213
|
const adviseOnly = ddl === "auto";
|
|
194
|
-
|
|
214
|
+
const appDir = appMigrationsDir(kernel, config?.migrations?.dir ?? "migrations");
|
|
215
|
+
const hasMigrations = ownsSharedMigrations(name) || connectorVersionsMigrations(name, appDir);
|
|
216
|
+
if (adviseOnly && (!hasMigrations || !adviseMigrations(check, env, filename))) return;
|
|
217
|
+
if (!hasMigrations) this.log(`Drizzle « ${name} » : \`ddl: "migrate"\` sans migration à appliquer — « ${name} » n'est pas le connecteur du framework (« ${FRAMEWORK_CONNECTOR} ») et aucune migration n'est versionnée sous son dossier. Son schéma n'est ni dérivé ni migré par le framework : \`nodefony orm:generate --connector ${name}\` écrit sa première migration.`, "WARNING");
|
|
195
218
|
const target = {
|
|
196
219
|
dialect,
|
|
197
220
|
filename,
|
|
198
221
|
url: cfg.url
|
|
199
222
|
};
|
|
200
|
-
const sources = await defaultMigrationSources(
|
|
223
|
+
const sources = await defaultMigrationSources(appDir, {
|
|
224
|
+
framework: config?.frameworkEntities !== false,
|
|
225
|
+
connector: name
|
|
226
|
+
});
|
|
201
227
|
const migrator = new DrizzleMigrator({
|
|
202
228
|
connector: name,
|
|
203
229
|
...target,
|
|
204
230
|
sources,
|
|
205
|
-
lockTimeoutMs: config
|
|
231
|
+
lockTimeoutMs: config?.migrations?.lockTimeoutMs
|
|
206
232
|
});
|
|
207
233
|
if (adviseOnly) {
|
|
208
234
|
await this.#publishReadiness(name, migrator, ddl, "warn");
|
|
@@ -244,7 +270,7 @@ var DrizzleService = class extends Service {
|
|
|
244
270
|
if (!kernel) return;
|
|
245
271
|
try {
|
|
246
272
|
const plan = await migrator.status();
|
|
247
|
-
const mode = this.#config()
|
|
273
|
+
const mode = this.#config()?.migrations?.divergence ?? "report";
|
|
248
274
|
const report = buildReport(plan, {
|
|
249
275
|
ddl,
|
|
250
276
|
divergence: mode === "off" ? null : await describeDivergence(plan),
|
|
@@ -269,7 +295,7 @@ var DrizzleService = class extends Service {
|
|
|
269
295
|
*/
|
|
270
296
|
#watch(name, migrator, ddl, check) {
|
|
271
297
|
if (check === "off") return;
|
|
272
|
-
|
|
298
|
+
this.#watchers ??= /* @__PURE__ */ new Map();
|
|
273
299
|
const timer = setInterval(() => {
|
|
274
300
|
this.#publishReadiness(name, migrator, ddl, check).catch(() => void 0);
|
|
275
301
|
}, READINESS_POLL_MS);
|
|
@@ -71,6 +71,16 @@ var DrizzleAuditStore = class DrizzleAuditStore {
|
|
|
71
71
|
get location() {
|
|
72
72
|
return this.#location;
|
|
73
73
|
}
|
|
74
|
+
/** Connecteur ORM qui porte ce store — posé par {@link DrizzleAuditStore.from}. */
|
|
75
|
+
#connector;
|
|
76
|
+
/**
|
|
77
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
78
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
79
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
80
|
+
*/
|
|
81
|
+
get connector() {
|
|
82
|
+
return this.#connector;
|
|
83
|
+
}
|
|
74
84
|
/**
|
|
75
85
|
* Construit le store depuis un {@link DrizzleOrm}. Le handle est résolu **lazy**
|
|
76
86
|
* (gardé par `isConnected()` → `null` tant que l'ORM n'est pas/plus connecté).
|
|
@@ -83,7 +93,9 @@ var DrizzleAuditStore = class DrizzleAuditStore {
|
|
|
83
93
|
* @param retentionMs - fenêtre de rétention (ms).
|
|
84
94
|
*/
|
|
85
95
|
static from(orm, now, retentionMs) {
|
|
86
|
-
|
|
96
|
+
const store = new DrizzleAuditStore(() => orm.isConnected() ? orm.getNativeConnection() : null, now, retentionMs, createAuditEventTable(orm.dialect), orm.location);
|
|
97
|
+
store.#connector = orm.name;
|
|
98
|
+
return store;
|
|
87
99
|
}
|
|
88
100
|
async append(event) {
|
|
89
101
|
const db = this.#resolveDb();
|
|
@@ -133,7 +145,7 @@ var DrizzleAuditStore = class DrizzleAuditStore {
|
|
|
133
145
|
const hasNext = rows.length > limit;
|
|
134
146
|
const pageRows = hasNext ? rows.slice(0, limit) : rows;
|
|
135
147
|
const items = pageRows.map((row) => this.#toEvent(row));
|
|
136
|
-
const last = pageRows
|
|
148
|
+
const last = pageRows.at(-1);
|
|
137
149
|
return {
|
|
138
150
|
items,
|
|
139
151
|
limit,
|