@nodefony/drizzle 10.0.0-alpha.7 → 10.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_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 +11 -7
- package/dist/nodefony/command/migrateShared.js +17 -1
- package/dist/nodefony/command/orm-generate.js +81 -13
- 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 +21 -5
- package/dist/nodefony/entity/userTable.js +3 -0
- package/dist/nodefony/registerStores.js +2 -26
- package/dist/nodefony/service/DrizzleService.js +19 -7
- package/dist/nodefony/src/DrizzleAuditStore.js +13 -1
- 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/frameworkConnector.js +29 -0
- package/dist/nodefony/src/memoryDatabase.js +13 -0
- package/dist/nodefony/src/migrator/DrizzleMigrator.js +17 -1
- package/dist/nodefony/src/migrator/appSchema.js +44 -1
- package/dist/nodefony/src/migrator/drivers/sqliteDriver.js +1 -0
- package/dist/nodefony/src/migrator/explain.js +42 -1
- package/dist/nodefony/src/migrator/kit.js +23 -2
- package/dist/nodefony/src/migrator/paths.js +55 -1
- package/dist/nodefony/src/migrator/refusals.js +50 -1
- package/dist/nodefony/src/migrator/resolve.js +126 -6
- package/dist/nodefony/src/orm-core/DrizzleOrm.js +123 -22
- package/dist/nodefony/src/orm-core/ddlPlan.js +95 -0
- package/dist/nodefony/src/orm-core/index.js +2 -1
- package/dist/nodefony/src/orm-core/seedRow.js +65 -0
- package/dist/nodefony/src/safeTarget.js +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/nodefony/command/migrateShared.d.ts +10 -0
- package/dist/types/nodefony/command/orm-generate.d.ts +12 -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/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/frameworkConnector.d.ts +24 -0
- package/dist/types/nodefony/src/memoryDatabase.d.ts +10 -0
- package/dist/types/nodefony/src/migrator/appSchema.d.ts +25 -0
- package/dist/types/nodefony/src/migrator/explain.d.ts +19 -0
- package/dist/types/nodefony/src/migrator/kit.d.ts +15 -0
- 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 +97 -1
- package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +11 -0
- package/dist/types/nodefony/src/orm-core/ddlPlan.d.ts +96 -0
- package/dist/types/nodefony/src/orm-core/index.d.ts +1 -0
- package/dist/types/nodefony/src/orm-core/seedRow.d.ts +23 -0
- package/docs/index.md +28 -25
- package/docs/migrations.md +69 -4
- package/package.json +19 -20
|
@@ -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";
|
|
@@ -13,6 +14,7 @@ import { appMigrationsDir, buildMigrator, knownConnectors, readMigrationEnv, res
|
|
|
13
14
|
import { DrizzleRepository } from "./nodefony/src/orm-core/DrizzleRepository.js";
|
|
14
15
|
import { DrizzleTransaction } from "./nodefony/src/orm-core/DrizzleTransaction.js";
|
|
15
16
|
import { DrizzleOrm } from "./nodefony/src/orm-core/DrizzleOrm.js";
|
|
17
|
+
import { seedEntityRow } from "./nodefony/src/orm-core/seedRow.js";
|
|
16
18
|
import "./nodefony/src/orm-core/index.js";
|
|
17
19
|
import DrizzleService from "./nodefony/service/DrizzleService.js";
|
|
18
20
|
import { TOKEN_ENTITY_NAMES, accessTokenTable, createAccessTokenTable, createDeniedJtiTable, createSubjectRevocationTable, createTokenEntities, deniedJtiTable, registerTokenEntities, subjectRevocationTable } from "./nodefony/entity/tokenEntity.js";
|
|
@@ -29,15 +31,15 @@ import { DrizzleWebAuthnCredentialStore } from "./nodefony/src/DrizzleWebAuthnCr
|
|
|
29
31
|
import { DrizzleTotpSecretStore } from "./nodefony/src/DrizzleTotpSecretStore.js";
|
|
30
32
|
import { DrizzleWebhookStore } from "./nodefony/src/DrizzleWebhookStore.js";
|
|
31
33
|
import { DrizzleIdempotencyStore } from "./nodefony/src/DrizzleIdempotencyStore.js";
|
|
32
|
-
import {
|
|
34
|
+
import { isFrameworkFallbackEntity, registerDrizzleFrameworkStores } from "./nodefony/registerStores.js";
|
|
33
35
|
import OrmGenerate from "./nodefony/command/orm-generate.js";
|
|
34
36
|
import OrmMigrate from "./nodefony/command/orm-migrate.js";
|
|
35
37
|
import OrmMigrateStatus from "./nodefony/command/orm-migrate-status.js";
|
|
36
38
|
import OrmMigrateBaseline from "./nodefony/command/orm-migrate-baseline.js";
|
|
37
39
|
import OrmMigrateRepair from "./nodefony/command/orm-migrate-repair.js";
|
|
38
40
|
import OrmReset from "./nodefony/command/orm-reset.js";
|
|
39
|
-
import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.
|
|
40
|
-
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";
|
|
41
43
|
import SessionStorage from "./nodefony/src/SessionStorage.js";
|
|
42
44
|
import "./nodefony/src/migrator/index.js";
|
|
43
45
|
import { DrizzleUserRepository } from "./nodefony/src/DrizzleUserRepository.js";
|
|
@@ -75,11 +77,13 @@ let Drizzle = class Drizzle extends Module {
|
|
|
75
77
|
* (convention Zod, alignée sur `@nodefony/mongoose` — audit config ORM 2026-06).
|
|
76
78
|
*/
|
|
77
79
|
async onKernelRegister() {
|
|
78
|
-
const validated = defineDrizzleConfig(this.options ?? {});
|
|
80
|
+
const validated = defineDrizzleConfig(this.options ?? {}, this.appOptions ?? {});
|
|
79
81
|
this.options = validated;
|
|
80
82
|
registerUserStore("drizzle");
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
const frameworkHost = validated.connectors.default;
|
|
84
|
+
if (validated.frameworkEntities !== false && 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 !== false && frameworkHost !== void 0) {
|
|
86
|
+
const dialect = frameworkHost.dialect;
|
|
83
87
|
const report = registerDrizzleFrameworkStores(dialect);
|
|
84
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");
|
|
85
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");
|
|
@@ -102,4 +106,4 @@ let Drizzle = class Drizzle extends Module {
|
|
|
102
106
|
Drizzle = __decorate([services([DrizzleService]), __decorateMetadata("design:paramtypes", [typeof Kernel === "undefined" ? Object : Kernel])], Drizzle);
|
|
103
107
|
var drizzle_default = Drizzle;
|
|
104
108
|
//#endregion
|
|
105
|
-
export { APP_RANK, APP_SOURCE, AUDIT_ENTITY_NAMES, DDL_MODES, DEFAULT_LOCK_TIMEOUT_MS, DIVERGENCE_MODES, DrizzleAuditStore, DrizzleIdempotencyStore, DrizzleMigrator, DrizzleOrm, DrizzleRepository, DrizzleService, DrizzleTokenStore, DrizzleTotpSecretStore, DrizzleTransaction, DrizzleUserRepository, DrizzleWebAuthnCredentialStore, DrizzleWebhookStore, EXIT, FORMAT_MARKER, FRAMEWORK_CONNECTOR, FRAMEWORK_RANK, FRAMEWORK_SOURCE, HISTORY_TABLE, IDEMPOTENCY_ENTITY_NAME, MIGRATE_URL_ENV, MIGRATION_CHECK_MODES, MIGRATION_FORMAT_VERSION, MYSQL_LOCK_NAME_SQL, MigrationVerdictError, PG_LOCK_KEY, SESSION_CONNECTOR, SESSION_ENTITY_NAME, SessionStorage, TOKEN_ENTITY_NAMES, TOTP_SECRET_ENTITY, WEBAUTHN_CREDENTIAL_ENTITY, WEBHOOK_ENDPOINT_ENTITY, accessTokenTable, action, appMigrationsDir, auditEventTable, buildMigrator, buildReport, createAccessTokenTable, createAuditEntities, createAuditEventTable, createDeniedJtiTable, createIdempotencyEntities, createIdempotencyTable, createSessionEntity, createSessionTable, createSubjectRevocationTable, createTokenEntities, createTotpSecretEntity, createTotpSecretTable, createUserEntity, createUserTable, createWebAuthnCredentialEntity, createWebAuthnCredentialTable, createWebhookEndpointEntity, createWebhookEndpointTable, drizzle_default as default, defaultMigrationSources, defineDrizzleConfig, deniedJtiTable, drizzleConfigJsonSchema, drizzleConfigSchema, exitCodeOf, frameworkMigrationsDir, idempotencyKeyTable, isFrameworkFallbackEntity, knownConnectors, loadSources, meaningOf, migrationHash, openMigrationDriver, orderSources, readMigrationEnv, registerAuditEntities, registerDrizzleFrameworkStores, registerIdempotencyEntities, registerSessionEntity, registerTokenEntities, registerTotpSecretEntity, registerUserEntity, registerWebAuthnCredentialEntity, registerWebhookEndpointEntity, renderRefusal, renderStatus, resetAllowed, resolveCheckMode, resolveConnector, resolveDdlMode, resolveDivergenceMode, sessionTable, styleFor, subjectRevocationTable, totpSecretTable, userTable, verdictOf, webAuthnCredentialTable, webhookEndpointTable };
|
|
109
|
+
export { APP_RANK, APP_SOURCE, AUDIT_ENTITY_NAMES, DDL_MODES, DEFAULT_LOCK_TIMEOUT_MS, DIVERGENCE_MODES, DrizzleAuditStore, DrizzleIdempotencyStore, DrizzleMigrator, DrizzleOrm, DrizzleRepository, DrizzleService, DrizzleTokenStore, DrizzleTotpSecretStore, DrizzleTransaction, DrizzleUserRepository, DrizzleWebAuthnCredentialStore, DrizzleWebhookStore, EXIT, FORMAT_MARKER, FRAMEWORK_CONNECTOR, FRAMEWORK_RANK, FRAMEWORK_SOURCE, HISTORY_TABLE, IDEMPOTENCY_ENTITY_NAME, MIGRATE_URL_ENV, MIGRATION_CHECK_MODES, MIGRATION_FORMAT_VERSION, MYSQL_LOCK_NAME_SQL, MigrationVerdictError, PG_LOCK_KEY, SESSION_CONNECTOR, SESSION_ENTITY_NAME, SessionStorage, TOKEN_ENTITY_NAMES, TOTP_SECRET_ENTITY, WEBAUTHN_CREDENTIAL_ENTITY, WEBHOOK_ENDPOINT_ENTITY, accessTokenTable, action, appMigrationsDir, auditEventTable, buildMigrator, buildReport, createAccessTokenTable, createAuditEntities, createAuditEventTable, createDeniedJtiTable, createIdempotencyEntities, createIdempotencyTable, createSessionEntity, createSessionTable, createSubjectRevocationTable, createTokenEntities, createTotpSecretEntity, createTotpSecretTable, createUserEntity, createUserTable, createWebAuthnCredentialEntity, createWebAuthnCredentialTable, createWebhookEndpointEntity, createWebhookEndpointTable, drizzle_default as default, defaultMigrationSources, defineDrizzleConfig, deniedJtiTable, drizzleConfigJsonSchema, drizzleConfigSchema, exitCodeOf, frameworkMigrationsDir, idempotencyKeyTable, isFrameworkFallbackEntity, knownConnectors, loadSources, meaningOf, migrationHash, openMigrationDriver, orderSources, readMigrationEnv, registerAuditEntities, registerDrizzleFrameworkStores, registerIdempotencyEntities, registerSessionEntity, registerTokenEntities, registerTotpSecretEntity, registerUserEntity, registerWebAuthnCredentialEntity, registerWebhookEndpointEntity, renderRefusal, renderStatus, resetAllowed, resolveCheckMode, resolveConnector, resolveDdlMode, resolveDivergenceMode, seedEntityRow, sessionTable, styleFor, subjectRevocationTable, totpSecretTable, userTable, verdictOf, webAuthnCredentialTable, webhookEndpointTable };
|
|
@@ -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
|
-
import { appMigrationsDir } from "../src/migrator/resolve.js";
|
|
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";
|
|
@@ -62,6 +63,17 @@ const options = {
|
|
|
62
63
|
* nodefony orm:migrate # ou : le travail de déploiement
|
|
63
64
|
* ```
|
|
64
65
|
*
|
|
66
|
+
* @example En développement — écrire ET appliquer en un seul geste
|
|
67
|
+
* ```bash
|
|
68
|
+
* nodefony orm:generate --name ajout_du_titre --apply
|
|
69
|
+
* ```
|
|
70
|
+
* `--apply` n'est rien d'autre qu'un `orm:migrate` enchaîné : la commande
|
|
71
|
+
* DÉLÈGUE, elle ne rejoue pas l'application pour son compte. Toutes les gardes
|
|
72
|
+
* du verbe d'application valent donc à l'identique — refus du destructif sans
|
|
73
|
+
* `--allow-destructive`, verrou, historique. Refusé hors développement : un
|
|
74
|
+
* déploiement n'écrit pas ses migrations au moment de les appliquer, il
|
|
75
|
+
* applique celles que la revue de code a validées.
|
|
76
|
+
*
|
|
65
77
|
* @example Ce que le modèle déclaratif ne peut pas déduire
|
|
66
78
|
* ```bash
|
|
67
79
|
* nodefony orm:generate --custom --name vue_des_ventes
|
|
@@ -75,6 +87,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
75
87
|
this.addOption("-n, --name <nom>", "nom de la migration, en minuscules et « _ » — il entre dans le tag, qui est immuable une fois publié");
|
|
76
88
|
this.addOption("--custom", "écrit un fichier SQL VIDE et son entrée de journal, sans rien déduire : vues, déclencheurs, clés étrangères, remplissages");
|
|
77
89
|
this.addOption("--allow-destructive", "accepte une migration qui SUPPRIME des données — à ne poser qu'après avoir relu le fichier produit");
|
|
90
|
+
this.addOption("--apply", "applique la migration dans la foulée (développement seulement) — écrire puis appliquer en un seul geste");
|
|
78
91
|
}
|
|
79
92
|
/**
|
|
80
93
|
* Racine de l'application — c'est depuis là que l'outil est lancé.
|
|
@@ -101,6 +114,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
101
114
|
if (!resolved) return this;
|
|
102
115
|
const { resolution, config } = resolved;
|
|
103
116
|
const connector = resolution.connector;
|
|
117
|
+
if (this.refuseReservedConnector(connector, opts.json)) return this;
|
|
104
118
|
const name = this.#nameOrFail(opts, connector);
|
|
105
119
|
if (name === null) return this;
|
|
106
120
|
const root = this.#projectRoot();
|
|
@@ -109,7 +123,8 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
109
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);
|
|
110
124
|
return this;
|
|
111
125
|
}
|
|
112
|
-
const
|
|
126
|
+
const connectorDir = connectorMigrationsDir(migrationsDir, connector) ?? migrationsDir;
|
|
127
|
+
const outDir = path.join(connectorDir, resolution.dialect);
|
|
113
128
|
const relative = (p) => path.relative(root, p).split(path.sep).join("/");
|
|
114
129
|
try {
|
|
115
130
|
if (opts.custom === true) {
|
|
@@ -118,7 +133,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
118
133
|
dialect: resolution.dialect,
|
|
119
134
|
name
|
|
120
135
|
});
|
|
121
|
-
return this.#emit({
|
|
136
|
+
return await this.#emit({
|
|
122
137
|
formatVersion: 1,
|
|
123
138
|
connector,
|
|
124
139
|
generated: true,
|
|
@@ -157,8 +172,9 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
157
172
|
const frameworkRoot = path.dirname(await frameworkMigrationsDir());
|
|
158
173
|
const belongsToFramework = (file) => !path.relative(frameworkRoot, file).startsWith("..");
|
|
159
174
|
const mine = found.filter((t) => !belongsToFramework(t.file));
|
|
160
|
-
const
|
|
161
|
-
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) => ({
|
|
162
178
|
table: t.tableName,
|
|
163
179
|
dialect: t.dialect ?? "inconnu",
|
|
164
180
|
file: relative(t.file)
|
|
@@ -169,7 +185,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
169
185
|
otherDialect,
|
|
170
186
|
unreadable
|
|
171
187
|
};
|
|
172
|
-
const framework = new Set(await frameworkTables(dialect));
|
|
188
|
+
const framework = new Set(ownsSharedMigrations(connector) ? await frameworkTables(dialect) : []);
|
|
173
189
|
const usurped = usurpedTables(tables, framework);
|
|
174
190
|
if (usurped.length > 0) {
|
|
175
191
|
const list = usurped.map((t) => ` • « ${t.tableName} », exportée par ${relative(t.file)} (${t.exportName})`).join("\n");
|
|
@@ -211,7 +227,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
211
227
|
configRel: relative(configFile),
|
|
212
228
|
name,
|
|
213
229
|
label: `le connecteur « ${connector} » (${dialect})`,
|
|
214
|
-
regenerateCommand: `nodefony orm:generate --name ${name}`
|
|
230
|
+
regenerateCommand: ownsSharedMigrations(connector) ? `nodefony orm:generate --name ${name}` : `nodefony orm:generate --connector ${connector} --name ${name}`
|
|
215
231
|
});
|
|
216
232
|
} finally {
|
|
217
233
|
await fs.rm(work, {
|
|
@@ -230,7 +246,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
230
246
|
], opts.json, EXIT.actionRequired);
|
|
231
247
|
return this;
|
|
232
248
|
}
|
|
233
|
-
return this.#emit({
|
|
249
|
+
return await this.#emit({
|
|
234
250
|
formatVersion: 1,
|
|
235
251
|
connector,
|
|
236
252
|
generated: false,
|
|
@@ -273,7 +289,7 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
273
289
|
], opts.json, EXIT.actionRequired, discovery);
|
|
274
290
|
return this;
|
|
275
291
|
}
|
|
276
|
-
return this.#emit({
|
|
292
|
+
return await this.#emit({
|
|
277
293
|
formatVersion: 1,
|
|
278
294
|
connector,
|
|
279
295
|
generated: true,
|
|
@@ -384,17 +400,69 @@ var OrmGenerate = class extends OrmMigrateCommand {
|
|
|
384
400
|
*
|
|
385
401
|
* @returns `this`.
|
|
386
402
|
*/
|
|
387
|
-
|
|
403
|
+
/**
|
|
404
|
+
* Écrit le compte rendu de génération, puis enchaîne l'application si
|
|
405
|
+
* `--apply` est posé.
|
|
406
|
+
*
|
|
407
|
+
* 🔴 **En sortie machine, `--apply` ne produit qu'UN document** : celui de
|
|
408
|
+
* l'application, qui porte le verdict final. Le compte rendu de génération
|
|
409
|
+
* part alors sur la sortie d'ERREUR — libre par contrat — plutôt que d'écrire
|
|
410
|
+
* deux objets à la suite sur la sortie standard, ce qui casserait tout `| jq`
|
|
411
|
+
* sans qu'aucune erreur ne le dise.
|
|
412
|
+
*
|
|
413
|
+
* @param report - le rapport de génération.
|
|
414
|
+
* @param opts - options reçues.
|
|
415
|
+
* @param headline - la première ligne, déjà rédigée par l'appelant.
|
|
416
|
+
* @returns `this`, la sortie ayant été écrite.
|
|
417
|
+
*/
|
|
418
|
+
async #emit(report, opts, headline) {
|
|
388
419
|
const style = this.style;
|
|
389
420
|
let human = `${style.green("✓")} ${headline}\n`;
|
|
390
421
|
if (report.files.length > 0) human += `\n${report.files.map((f) => ` + ${f}`).join("\n")}\n`;
|
|
391
422
|
if (report.otherDialect.length > 0) human += `\n${style.dim(`Écrites pour un autre moteur, donc hors de cette migration :`)}\n` + report.otherDialect.map((o) => ` • ${o.table} (${o.dialect}) — ${o.file}`).join("\n") + "\n";
|
|
392
423
|
if (report.unreadable.length > 0) human += `\n${style.dim("Fichiers d'entités non lus (aucune entité enregistrée n'en dépend) :")}\n` + report.unreadable.map((u) => ` • ${u.file} — ${u.cause}`).join("\n") + "\n";
|
|
393
424
|
if (report.warnings.length > 0) human += `\n${style.bold("⚠️ À REGARDER avant d'appliquer")} (rien n'est détruit — mais l'application peut cesser de répondre le temps de l'opération, ou la migration ÉCHOUER si la table porte déjà des lignes) :\n` + report.warnings.map((r) => ` • ${r.id} : ${r.what}\n → ${r.todo}`).join("\n") + "\n";
|
|
394
|
-
if (report.generated) human += `\n${style.bold("À faire :")}\n ${style.dim("relire le fichier, puis")} ${style.green("nodefony orm:migrate")}\n`;
|
|
425
|
+
if (report.generated && opts.apply !== true) human += `\n${style.bold("À faire :")}\n ${style.dim("relire le fichier, puis")} ${style.green("nodefony orm:migrate")}\n ${style.dim("ou, en développement, les deux d'un geste :")} ${style.green("nodefony orm:generate --apply")}\n`;
|
|
426
|
+
if (report.generated && opts.apply === true) {
|
|
427
|
+
if (opts.json === true) process.stderr.write(human.endsWith("\n") ? human : `${human}\n`);
|
|
428
|
+
else process.stdout.write(human.endsWith("\n") ? human : `${human}\n`);
|
|
429
|
+
await this.#applyNow(opts, report.connector);
|
|
430
|
+
return this;
|
|
431
|
+
}
|
|
395
432
|
this.respond(report, human, EXIT.ok, opts.json);
|
|
396
433
|
return this;
|
|
397
434
|
}
|
|
435
|
+
/**
|
|
436
|
+
* Enchaîne l'application quand `--apply` est posé.
|
|
437
|
+
*
|
|
438
|
+
* DÉLÈGUE au verbe `orm:migrate` par son point d'entrée public
|
|
439
|
+
* ({@link Cli.getCommand}) : rejouer ici l'application aurait produit une
|
|
440
|
+
* SECONDE implémentation du même geste, qui aurait divergé de la première au
|
|
441
|
+
* premier réglage — et c'est précisément elle qui porte le refus du
|
|
442
|
+
* destructif, le verrou et l'écriture de l'historique.
|
|
443
|
+
*
|
|
444
|
+
* Refusé hors développement, sans drapeau pour le lever : générer puis
|
|
445
|
+
* appliquer dans le même souffle suppose qu'on relit le SQL entre les deux,
|
|
446
|
+
* ce que personne ne fait sur un serveur. En production, la migration a été
|
|
447
|
+
* écrite, relue et versionnée ailleurs ; `orm:migrate` l'applique.
|
|
448
|
+
*
|
|
449
|
+
* @param opts - options reçues, passées telles quelles au verbe d'application.
|
|
450
|
+
* @param connector - connecteur résolu, pour le message de refus.
|
|
451
|
+
* @returns `true` si la main a été passée (la sortie est déjà écrite).
|
|
452
|
+
*/
|
|
453
|
+
async #applyNow(opts, connector) {
|
|
454
|
+
if (!generateApplyAllowed(readMigrationEnv(this.kernel))) {
|
|
455
|
+
this.fail(connector, "NF_MIGRATE_NOT_DEVELOPMENT", "« --apply » n'existe qu'en développement.", "Écrire une migration et l'appliquer dans le même geste suppose qu'on relit le SQL produit entre les deux — ce que personne ne fait sur un serveur. Hors développement, la migration a été écrite, relue et versionnée ailleurs : c'est « orm:migrate » qui l'applique, et lui seul.", [action("nodefony orm:migrate")], opts.json, EXIT.actionRequired);
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
const migrate = (this.kernel?.getModule("drizzle"))?.commands?.["orm:migrate"];
|
|
459
|
+
if (!migrate) {
|
|
460
|
+
this.fail(connector, "NF_MIGRATE_UNAVAILABLE", "Le verbe « orm:migrate » est introuvable : la migration a été ÉCRITE mais n'a pas été appliquée.", "« --apply » ne fait qu'enchaîner ce verbe ; sans lui, il n'y a rien à enchaîner.", [action("nodefony orm:migrate")], opts.json);
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
await migrate.generate(opts);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
398
466
|
};
|
|
399
467
|
//#endregion
|
|
400
468
|
export { OrmGenerate as default };
|
|
@@ -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,11 +10,23 @@ 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;
|
|
26
|
+
if (database && database.family !== "sql" && !declaredDefault) {
|
|
27
|
+
delete config.connectors.default;
|
|
28
|
+
return config;
|
|
29
|
+
}
|
|
18
30
|
if (database && database.family === "sql" && database.dialect) {
|
|
19
31
|
const target = config.connectors.default ? "default" : Object.keys(config.connectors)[0];
|
|
20
32
|
const connector = target ? config.connectors[target] : void 0;
|
|
@@ -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
|
/**
|
|
@@ -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",
|
|
@@ -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, readMigrationEnv, resetAllowed, resolveCheckMode, resolveDdlMode } from "../src/migrator/resolve.js";
|
|
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
|
};
|
|
@@ -86,6 +88,9 @@ var DrizzleService = class extends Service {
|
|
|
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(() => {});
|
|
@@ -122,13 +127,14 @@ var DrizzleService = class extends Service {
|
|
|
122
127
|
/** Connecte un connecteur (crée le dossier de la base SQLite si nécessaire). */
|
|
123
128
|
async #connectOne(name, cfg, connect = true) {
|
|
124
129
|
const dialect = cfg.dialect ?? "sqlite";
|
|
125
|
-
const ddl = resolveDdlMode(cfg.ddl, readMigrationEnv(this.kernel));
|
|
130
|
+
const ddl = resolveDdlMode(cfg.ddl, readMigrationEnv(this.kernel), connectorVersionsMigrations(name, appMigrationsDir(this.kernel, this.#config().migrations?.dir ?? "migrations")), cfg.filename === MEMORY_DATABASE);
|
|
126
131
|
let filename;
|
|
127
132
|
if (dialect === "sqlite") {
|
|
128
133
|
filename = cfg.filename ?? this.#defaultFilename(name);
|
|
129
134
|
if (filename !== ":memory:") fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
130
135
|
}
|
|
131
136
|
const orm = new DrizzleOrm(name, {
|
|
137
|
+
container: this.container ?? void 0,
|
|
132
138
|
dialect,
|
|
133
139
|
filename,
|
|
134
140
|
url: cfg.url,
|
|
@@ -163,7 +169,7 @@ var DrizzleService = class extends Service {
|
|
|
163
169
|
host: null,
|
|
164
170
|
port: null
|
|
165
171
|
} : parseConnectionTarget(cfg.url));
|
|
166
|
-
throw new BootConfigurationError(`Drizzle : le connecteur "${name}" (${dialect}: ${target})
|
|
172
|
+
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
173
|
}
|
|
168
174
|
this.#orms.set(name, orm);
|
|
169
175
|
this.log(`Drizzle « ${name} » connecté (${dialect}: ${target}) — schéma : ${ddl} ${DDL_EXPLAINED[ddl]}`, "INFO");
|
|
@@ -191,13 +197,19 @@ var DrizzleService = class extends Service {
|
|
|
191
197
|
const env = readMigrationEnv(kernel);
|
|
192
198
|
const check = resolveCheckMode(config.migrations?.check, env);
|
|
193
199
|
const adviseOnly = ddl === "auto";
|
|
194
|
-
|
|
200
|
+
const appDir = appMigrationsDir(kernel, config.migrations?.dir ?? "migrations");
|
|
201
|
+
const hasMigrations = ownsSharedMigrations(name) || connectorVersionsMigrations(name, appDir);
|
|
202
|
+
if (adviseOnly && (!hasMigrations || !adviseMigrations(check, env, filename))) return;
|
|
203
|
+
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
204
|
const target = {
|
|
196
205
|
dialect,
|
|
197
206
|
filename,
|
|
198
207
|
url: cfg.url
|
|
199
208
|
};
|
|
200
|
-
const sources = await defaultMigrationSources(
|
|
209
|
+
const sources = await defaultMigrationSources(appDir, {
|
|
210
|
+
framework: config.frameworkEntities !== false,
|
|
211
|
+
connector: name
|
|
212
|
+
});
|
|
201
213
|
const migrator = new DrizzleMigrator({
|
|
202
214
|
connector: name,
|
|
203
215
|
...target,
|
|
@@ -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();
|
|
@@ -104,8 +104,10 @@ var DrizzleIdempotencyStore = class DrizzleIdempotencyStore {
|
|
|
104
104
|
* base ({@link DrizzleOrm.location}) pour Studio. Lazy (comme `resolveDb`) car
|
|
105
105
|
* le store est fabriqué AVANT le connect de l'ORM → l'emplacement n'est lisible
|
|
106
106
|
* qu'une fois l'ORM enregistré (lu au `onReady`, pas à la construction).
|
|
107
|
+
* @param connector - connecteur ORM qui porte la table, publié au registre des stores.
|
|
107
108
|
*/
|
|
108
|
-
constructor(resolveDb, now = Date.now, leaseMs = DEFAULT_LEASE_MS, ttlMs = DEFAULT_TTL_MS, table = idempotencyKeyTable, resolveLocation) {
|
|
109
|
+
constructor(resolveDb, now = Date.now, leaseMs = DEFAULT_LEASE_MS, ttlMs = DEFAULT_TTL_MS, table = idempotencyKeyTable, resolveLocation, connector) {
|
|
110
|
+
this.#connector = connector;
|
|
109
111
|
this.#resolveDb = resolveDb;
|
|
110
112
|
this.#table = table;
|
|
111
113
|
this.#c = execTable(table);
|
|
@@ -124,6 +126,16 @@ var DrizzleIdempotencyStore = class DrizzleIdempotencyStore {
|
|
|
124
126
|
get location() {
|
|
125
127
|
return this.#resolveLocation?.();
|
|
126
128
|
}
|
|
129
|
+
/** Connecteur ORM qui porte ce store — reçu du constructeur. */
|
|
130
|
+
#connector;
|
|
131
|
+
/**
|
|
132
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
133
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
134
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
135
|
+
*/
|
|
136
|
+
get connector() {
|
|
137
|
+
return this.#connector;
|
|
138
|
+
}
|
|
127
139
|
/**
|
|
128
140
|
* Construit le store depuis un {@link DrizzleOrm}. Le handle est résolu **lazy**
|
|
129
141
|
* (gardé par `isConnected()` → `null` tant que l'ORM n'est pas/plus connecté).
|
|
@@ -137,7 +149,7 @@ var DrizzleIdempotencyStore = class DrizzleIdempotencyStore {
|
|
|
137
149
|
* @param ttlMs - rétention d'une réponse mémorisée (ms).
|
|
138
150
|
*/
|
|
139
151
|
static from(orm, now, leaseMs, ttlMs) {
|
|
140
|
-
return new DrizzleIdempotencyStore(() => orm.isConnected() ? orm.getNativeConnection() : null, now, leaseMs, ttlMs, createIdempotencyTable(orm.dialect), () => orm.location);
|
|
152
|
+
return new DrizzleIdempotencyStore(() => orm.isConnected() ? orm.getNativeConnection() : null, now, leaseMs, ttlMs, createIdempotencyTable(orm.dialect), () => orm.location, orm.name);
|
|
141
153
|
}
|
|
142
154
|
/**
|
|
143
155
|
* Approximation **per-pod, best-effort** : compteur local des réservations
|
|
@@ -75,6 +75,16 @@ var DrizzleTokenStore = class DrizzleTokenStore {
|
|
|
75
75
|
get location() {
|
|
76
76
|
return this.#location;
|
|
77
77
|
}
|
|
78
|
+
/** Connecteur ORM qui porte ce store — posé par {@link DrizzleTokenStore.from}. */
|
|
79
|
+
#connector;
|
|
80
|
+
/**
|
|
81
|
+
* Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
|
|
82
|
+
* registre des stores : la console rattache la brique à SON connecteur au
|
|
83
|
+
* lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
|
|
84
|
+
*/
|
|
85
|
+
get connector() {
|
|
86
|
+
return this.#connector;
|
|
87
|
+
}
|
|
78
88
|
/**
|
|
79
89
|
* Construit le store depuis un {@link DrizzleOrm} connecté. Les entités
|
|
80
90
|
* (`registerTokenEntities`) doivent avoir été enregistrées **avant**
|
|
@@ -85,7 +95,9 @@ var DrizzleTokenStore = class DrizzleTokenStore {
|
|
|
85
95
|
* @param retentionRevokedMs - rétention des PAT révoqués sans `exp`.
|
|
86
96
|
*/
|
|
87
97
|
static from(orm, now, retentionRevokedMs) {
|
|
88
|
-
|
|
98
|
+
const store = new DrizzleTokenStore(orm.getRepository(TOKEN_ENTITY_NAMES.records), orm.getRepository(TOKEN_ENTITY_NAMES.denied), orm.getRepository(TOKEN_ENTITY_NAMES.revocations), now, retentionRevokedMs, orm.location);
|
|
99
|
+
store.#connector = orm.name;
|
|
100
|
+
return store;
|
|
89
101
|
}
|
|
90
102
|
/**
|
|
91
103
|
* Insère ou remplace un record (PAT / refresh) — 1 requête, `upsert` atomique
|