@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.
Files changed (61) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
  2. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
  3. package/dist/index.js +11 -7
  4. package/dist/nodefony/command/migrateShared.js +17 -1
  5. package/dist/nodefony/command/orm-generate.js +81 -13
  6. package/dist/nodefony/command/orm-migrate-baseline.js +5 -2
  7. package/dist/nodefony/command/orm-reset.js +1 -0
  8. package/dist/nodefony/config/defineModuleConfig.js +21 -5
  9. package/dist/nodefony/entity/userTable.js +3 -0
  10. package/dist/nodefony/registerStores.js +2 -26
  11. package/dist/nodefony/service/DrizzleService.js +19 -7
  12. package/dist/nodefony/src/DrizzleAuditStore.js +13 -1
  13. package/dist/nodefony/src/DrizzleIdempotencyStore.js +14 -2
  14. package/dist/nodefony/src/DrizzleTokenStore.js +13 -1
  15. package/dist/nodefony/src/DrizzleTotpSecretStore.js +13 -1
  16. package/dist/nodefony/src/DrizzleWebAuthnCredentialStore.js +13 -1
  17. package/dist/nodefony/src/DrizzleWebhookStore.js +13 -1
  18. package/dist/nodefony/src/SessionStorage.js +8 -0
  19. package/dist/nodefony/src/frameworkConnector.js +29 -0
  20. package/dist/nodefony/src/memoryDatabase.js +13 -0
  21. package/dist/nodefony/src/migrator/DrizzleMigrator.js +17 -1
  22. package/dist/nodefony/src/migrator/appSchema.js +44 -1
  23. package/dist/nodefony/src/migrator/drivers/sqliteDriver.js +1 -0
  24. package/dist/nodefony/src/migrator/explain.js +42 -1
  25. package/dist/nodefony/src/migrator/kit.js +23 -2
  26. package/dist/nodefony/src/migrator/paths.js +55 -1
  27. package/dist/nodefony/src/migrator/refusals.js +50 -1
  28. package/dist/nodefony/src/migrator/resolve.js +126 -6
  29. package/dist/nodefony/src/orm-core/DrizzleOrm.js +123 -22
  30. package/dist/nodefony/src/orm-core/ddlPlan.js +95 -0
  31. package/dist/nodefony/src/orm-core/index.js +2 -1
  32. package/dist/nodefony/src/orm-core/seedRow.js +65 -0
  33. package/dist/nodefony/src/safeTarget.js +1 -0
  34. package/dist/types/index.d.ts +1 -1
  35. package/dist/types/nodefony/command/migrateShared.d.ts +10 -0
  36. package/dist/types/nodefony/command/orm-generate.d.ts +12 -0
  37. package/dist/types/nodefony/config/defineModuleConfig.d.ts +5 -1
  38. package/dist/types/nodefony/entity/userTable.d.ts +46 -3
  39. package/dist/types/nodefony/registerStores.d.ts +4 -3
  40. package/dist/types/nodefony/src/DrizzleAuditStore.d.ts +6 -0
  41. package/dist/types/nodefony/src/DrizzleIdempotencyStore.d.ts +8 -1
  42. package/dist/types/nodefony/src/DrizzleTokenStore.d.ts +6 -0
  43. package/dist/types/nodefony/src/DrizzleTotpSecretStore.d.ts +6 -0
  44. package/dist/types/nodefony/src/DrizzleWebAuthnCredentialStore.d.ts +6 -0
  45. package/dist/types/nodefony/src/DrizzleWebhookStore.d.ts +6 -0
  46. package/dist/types/nodefony/src/SessionStorage.d.ts +6 -0
  47. package/dist/types/nodefony/src/frameworkConnector.d.ts +24 -0
  48. package/dist/types/nodefony/src/memoryDatabase.d.ts +10 -0
  49. package/dist/types/nodefony/src/migrator/appSchema.d.ts +25 -0
  50. package/dist/types/nodefony/src/migrator/explain.d.ts +19 -0
  51. package/dist/types/nodefony/src/migrator/kit.d.ts +15 -0
  52. package/dist/types/nodefony/src/migrator/paths.d.ts +35 -0
  53. package/dist/types/nodefony/src/migrator/refusals.d.ts +36 -1
  54. package/dist/types/nodefony/src/migrator/resolve.d.ts +97 -1
  55. package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +11 -0
  56. package/dist/types/nodefony/src/orm-core/ddlPlan.d.ts +96 -0
  57. package/dist/types/nodefony/src/orm-core/index.d.ts +1 -0
  58. package/dist/types/nodefony/src/orm-core/seedRow.d.ts +23 -0
  59. package/docs/index.md +28 -25
  60. package/docs/migrations.md +69 -4
  61. package/package.json +19 -20
@@ -1,16 +1,59 @@
1
- import type { SQLiteTable } from "drizzle-orm/sqlite-core";
1
+ import type { AnySQLiteColumn, SQLiteTable } from "drizzle-orm/sqlite-core";
2
+ import type { AnyPgColumn, PgTable } from "drizzle-orm/pg-core";
3
+ import type { AnyMySqlColumn, MySqlTable } from "drizzle-orm/mysql-core";
2
4
  import type { IEntity } from "@nodefony/orm-core";
3
5
  import type { SqlDialect } from "../interfaces/IDrizzleConfig.js";
6
+ /**
7
+ * Factory de la table `User` — comme {@link FrameworkTableFactory}, mais sa clé
8
+ * primaire reste ATTEIGNABLE au type.
9
+ *
10
+ * 🔴 Sans cela, une application ne peut pas RÉFÉRENCER l'utilisateur, et c'est
11
+ * la première relation que quiconque écrit : `create entity Message
12
+ * "author:ref(User)"` rend `.references(() => userTable.id)`, qui ne compilait
13
+ * pas (`TS2339: Property 'id' does not exist on type 'SQLiteTable<TableConfig>'`).
14
+ * La table est bâtie depuis une spec dont les colonnes sont calculées
15
+ * (`Object.fromEntries`) : l'inférence les perd, et le type rendu ne porte plus
16
+ * aucune colonne — alors qu'elles existent toutes à l'exécution.
17
+ *
18
+ * Cette précision reste LOCALE à `User`, et n'est pas remontée dans le colKit :
19
+ * les tables du framework n'ont pas toutes une colonne `id` (`session` et
20
+ * `idempotency_key` portent une autre clé), donc l'y déclarer mentirait sur
21
+ * elles. `tests/unit/userTableReference.test.ts` garde le fait — par sa
22
+ * COMPILATION, qu'un cas d'exécution ne pourrait pas prouver.
23
+ */
24
+ export interface UserTableFactory {
25
+ (dialect: "sqlite"): SQLiteTable & {
26
+ readonly id: AnySQLiteColumn;
27
+ };
28
+ (dialect: "postgres"): PgTable & {
29
+ readonly id: AnyPgColumn;
30
+ };
31
+ (dialect: "mysql"): MySqlTable & {
32
+ readonly id: AnyMySqlColumn;
33
+ };
34
+ (dialect?: SqlDialect): (SQLiteTable & {
35
+ readonly id: AnySQLiteColumn;
36
+ }) | (PgTable & {
37
+ readonly id: AnyPgColumn;
38
+ }) | (MySqlTable & {
39
+ readonly id: AnyMySqlColumn;
40
+ });
41
+ }
4
42
  /**
5
43
  * Factory de la table `User` pour un dialecte donné (mémoïsée — une instance
6
44
  * par dialecte). Les trois dialectes sont portés (`sqlite` par défaut).
7
45
  */
8
- export declare const createUserTable: import("./colKit.js").FrameworkTableFactory;
46
+ export declare const createUserTable: UserTableFactory;
9
47
  /**
10
48
  * Variante SQLite de la table `User` (dialecte par défaut) — export conservé
11
49
  * pour l'usage direct/banc-test (ex. module mediasoup).
50
+ *
51
+ * Son type n'est PAS réécrit en `SQLiteTable` : l'annoter ainsi effaçait la
52
+ * clé primaire que la factory vient d'exposer, et c'était la moitié du défaut.
12
53
  */
13
- export declare const userTable: SQLiteTable;
54
+ export declare const userTable: SQLiteTable<import("drizzle-orm/sqlite-core").TableConfig> & {
55
+ readonly id: AnySQLiteColumn;
56
+ };
14
57
  /**
15
58
  * Forme plate d'une ligne `User` renvoyée par le repository de base — le contrat
16
59
  * `IUserRow` de `@nodefony/user`, ré-exporté sous son nom historique.
@@ -1,3 +1,4 @@
1
+ import { FRAMEWORK_CONNECTOR } from "./src/frameworkConnector.js";
1
2
  import type { SqlDialect } from "./config/config.js";
2
3
  /**
3
4
  * AUTO-ENREGISTREMENT des backends framework portés par Drizzle — « charger le
@@ -20,10 +21,10 @@ import type { SqlDialect } from "./config/config.js";
20
21
  * boot (« store inconnu ») — jamais de table fantôme ni d'erreur SQL différée.
21
22
  */
22
23
  /**
23
- * Connecteur conventionnel qui héberge le schéma framework (même convention que
24
- * `SESSION_CONNECTOR` : `"default"` pour Drizzle, `"nodefony"` pour Mongoose).
24
+ * Connecteur conventionnel qui héberge le schéma framework — défini dans
25
+ * `src/frameworkConnector.ts`, que les migrations lisent aussi.
25
26
  */
26
- export declare const FRAMEWORK_CONNECTOR = "default";
27
+ export { FRAMEWORK_CONNECTOR };
27
28
  /** Bilan de l'auto-enregistrement (loggé par le module — jamais silencieux). */
28
29
  export interface IFrameworkStoresReport {
29
30
  /** Entités déclarées par l'auto-register (tables créées au connect). */
@@ -47,6 +47,12 @@ export declare class DrizzleAuditStore implements IAuditStore {
47
47
  * — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
48
48
  */
49
49
  get location(): string | undefined;
50
+ /**
51
+ * Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
52
+ * registre des stores : la console rattache la brique à SON connecteur au
53
+ * lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
54
+ */
55
+ get connector(): string | undefined;
50
56
  /**
51
57
  * Construit le store depuis un {@link DrizzleOrm}. Le handle est résolu **lazy**
52
58
  * (gardé par `isConnected()` → `null` tant que l'ORM n'est pas/plus connecté).
@@ -73,8 +73,9 @@ export declare class DrizzleIdempotencyStore implements IIdempotencyStore {
73
73
  * base ({@link DrizzleOrm.location}) pour Studio. Lazy (comme `resolveDb`) car
74
74
  * le store est fabriqué AVANT le connect de l'ORM → l'emplacement n'est lisible
75
75
  * qu'une fois l'ORM enregistré (lu au `onReady`, pas à la construction).
76
+ * @param connector - connecteur ORM qui porte la table, publié au registre des stores.
76
77
  */
77
- constructor(resolveDb: () => DrizzleDb | null, now?: () => number, leaseMs?: number, ttlMs?: number, table?: DrizzleTable, resolveLocation?: () => string | undefined);
78
+ constructor(resolveDb: () => DrizzleDb | null, now?: () => number, leaseMs?: number, ttlMs?: number, table?: DrizzleTable, resolveLocation?: () => string | undefined, connector?: string);
78
79
  /**
79
80
  * Emplacement physique de la base (fichier SQLite) pour l'écran Studio « Stores »
80
81
  * — lu par `readStoreLocation`. Résolu **lazy** (l'ORM n'existe pas à la
@@ -82,6 +83,12 @@ export declare class DrizzleIdempotencyStore implements IIdempotencyStore {
82
83
  * ou sur un backend réseau (pg/mysql → voir l'infra déclarée).
83
84
  */
84
85
  get location(): string | undefined;
86
+ /**
87
+ * Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
88
+ * registre des stores : la console rattache la brique à SON connecteur au
89
+ * lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
90
+ */
91
+ get connector(): string | undefined;
85
92
  /**
86
93
  * Construit le store depuis un {@link DrizzleOrm}. Le handle est résolu **lazy**
87
94
  * (gardé par `isConnected()` → `null` tant que l'ORM n'est pas/plus connecté).
@@ -46,6 +46,12 @@ export declare class DrizzleTokenStore implements ITokenStore {
46
46
  * — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
47
47
  */
48
48
  get location(): string | undefined;
49
+ /**
50
+ * Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
51
+ * registre des stores : la console rattache la brique à SON connecteur au
52
+ * lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
53
+ */
54
+ get connector(): string | undefined;
49
55
  /**
50
56
  * Construit le store depuis un {@link DrizzleOrm} connecté. Les entités
51
57
  * (`registerTokenEntities`) doivent avoir été enregistrées **avant**
@@ -35,6 +35,12 @@ export declare class DrizzleTotpSecretStore implements ITotpSecretStore {
35
35
  * — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
36
36
  */
37
37
  get location(): string | undefined;
38
+ /**
39
+ * Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
40
+ * registre des stores : la console rattache la brique à SON connecteur au
41
+ * lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
42
+ */
43
+ get connector(): string | undefined;
38
44
  /**
39
45
  * Construit le store depuis un {@link DrizzleOrm} connecté. L'entité
40
46
  * (`registerTotpSecretEntity`) doit avoir été enregistrée **avant** `orm.connect()`.
@@ -34,6 +34,12 @@ export declare class DrizzleWebAuthnCredentialStore implements IWebAuthnCredenti
34
34
  * — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
35
35
  */
36
36
  get location(): string | undefined;
37
+ /**
38
+ * Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
39
+ * registre des stores : la console rattache la brique à SON connecteur au
40
+ * lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
41
+ */
42
+ get connector(): string | undefined;
37
43
  /**
38
44
  * Construit le store depuis un {@link DrizzleOrm} connecté. L'entité
39
45
  * (`registerWebAuthnCredentialEntity`) doit avoir été enregistrée **avant**
@@ -51,6 +51,12 @@ export declare class DrizzleWebhookStore implements IWebhookStore {
51
51
  * — lu par `readStoreLocation`. `undefined` = backend réseau ou `:memory:`.
52
52
  */
53
53
  get location(): string | undefined;
54
+ /**
55
+ * Connecteur ORM qui porte ce store, lu par `readStoreConnector` pour le
56
+ * registre des stores : la console rattache la brique à SON connecteur au
57
+ * lieu de le déduire. `undefined` pour un store construit sans ORM (bancs).
58
+ */
59
+ get connector(): string | undefined;
54
60
  /**
55
61
  * Construit le store depuis un {@link DrizzleOrm} connecté. L'entité
56
62
  * (`registerWebhookEndpointEntity`) doit avoir été enregistrée **avant**
@@ -31,6 +31,12 @@ declare class SessionStorage implements ISessionStorage {
31
31
  * DÉFENSIVE (getter `location` optionnel) — l'ORM base `IOrm` ne l'expose pas.
32
32
  */
33
33
  get location(): string | undefined;
34
+ /**
35
+ * Connecteur ORM qui porte les sessions — lu par `readStoreConnector` pour le
36
+ * registre des stores. `undefined` tant que l'ORM de ce connecteur n'est pas
37
+ * enregistré : on ne publie pas un rattachement que rien ne porte.
38
+ */
39
+ get connector(): string | undefined;
34
40
  read(id: string): Promise<ISerializedSession>;
35
41
  start(id: string): Promise<ISerializedSession>;
36
42
  write(id: string, data: ISerializedSession): Promise<ISerializedSession>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Connecteur conventionnel qui héberge le schéma framework (même convention que
3
+ * `SESSION_CONNECTOR` : `"default"` pour Drizzle, `"nodefony"` pour Mongoose).
4
+ *
5
+ * Vit dans ce fichier sans dépendance parce que DEUX familles le lisent : le
6
+ * registre des stores (où résoudre les briques du framework) et les migrations
7
+ * (qui les reçoit). Une copie du littéral de chaque côté dériverait en silence.
8
+ */
9
+ export declare const FRAMEWORK_CONNECTOR = "default";
10
+ /**
11
+ * Ce connecteur possède-t-il les migrations du framework et de l'application ?
12
+ *
13
+ * **Seul le connecteur du framework les possède.** Le dossier des migrations de
14
+ * l'application (`migrations/<dialect>`) ne porte aucune notion de connecteur :
15
+ * il décrit UNE base, celle où vivent les briques du framework et les entités
16
+ * de l'application. Les attribuer à tout connecteur faisait qu'une base
17
+ * secondaire (analyse, base d'un module) en mode `migrate` y créait des tables
18
+ * qui ne lui appartiennent pas, et qu'en mode `auto` elle annonçait à chaque
19
+ * démarrage des migrations qui ne la concernent pas.
20
+ *
21
+ * @param connector - nom du connecteur (clé de `connectors`).
22
+ * @returns `true` pour le connecteur du framework, `false` pour tout autre.
23
+ */
24
+ export declare function ownsSharedMigrations(connector: string): boolean;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Nom de fichier d'une base SQLite en MÉMOIRE — volatile, elle repart vide à
3
+ * chaque démarrage.
4
+ *
5
+ * Module FEUILLE (aucun import) : l'adapter, le migrateur, les commandes et le
6
+ * service le consultent tous, et le placer dans l'un d'eux ferait importer le
7
+ * migrateur par l'adapter. Une seule écriture du littéral : c'est d'elle que
8
+ * dépend la règle « une base volatile dérive son schéma » (`resolveDdlMode`).
9
+ */
10
+ export declare const MEMORY_DATABASE = ":memory:";
@@ -221,3 +221,28 @@ export declare function usurpedTables(tables: readonly IDiscoveredTable[], frame
221
221
  * @returns les entités et leur table, dans l'ordre du registre.
222
222
  */
223
223
  export declare function registeredTables(connector: string): IExpectedEntity[];
224
+ /**
225
+ * Tables d'un connecteur parmi celles que les fichiers fournissent — le
226
+ * partage qui fait qu'une migration ne décrit que SA base.
227
+ *
228
+ * Une table appartient au connecteur de l'entité qui la déclare (le REGISTRE
229
+ * le sait, un fichier non). Une table qu'aucune entité ne réclame reste au
230
+ * connecteur du framework, comme avant que les connecteurs secondaires aient
231
+ * leurs migrations. Sans ce partage, la table d'une entité secondaire entrait
232
+ * AUSSI dans les migrations du framework : créée dans une base qui ne s'en sert
233
+ * jamais, et proposée à la suppression le jour où elle n'y figure plus.
234
+ *
235
+ * @param tables - tables fournies par les fichiers de l'application.
236
+ * @param connector - connecteur visé.
237
+ * @param owners - `table → connecteurs` qui la déclarent ; par défaut, le registre.
238
+ * @returns les tables de ce connecteur, dans l'ordre reçu.
239
+ */
240
+ export declare function tablesOfConnector<T extends {
241
+ tableName: string;
242
+ }>(tables: readonly T[], connector: string, owners?: ReadonlyMap<string, ReadonlySet<string>>): T[];
243
+ /**
244
+ * Pour chaque table enregistrée, les connecteurs dont une entité la déclare.
245
+ *
246
+ * @returns la table de correspondance, lue dans le registre des entités.
247
+ */
248
+ export declare function registeredOwners(): Map<string, Set<string>>;
@@ -422,3 +422,22 @@ export declare function refusalInMode(code: IMigrationVerdict["code"], ddl: stri
422
422
  meaning: string;
423
423
  actions: IMigrationAction[];
424
424
  } | null;
425
+ /**
426
+ * L'échec vient-il de ce que la base est DÉJÀ dans l'état que la migration vise ?
427
+ *
428
+ * Le cas est fréquent et sa sortie n'était nommée nulle part : le message de
429
+ * reprise proposait de corriger le fichier ou de travailler sur une base
430
+ * d'essai, deux voies qui ne mènent à rien quand le fichier est juste et la
431
+ * base déjà conforme. Un agent placé devant ce refus tourne alors en rond
432
+ * — `repair` lève le marqueur, `migrate` rejoue, le moteur redit la même
433
+ * chose — puis finit par recréer la base, ce qui emporte les données.
434
+ *
435
+ * Constaté sur un agent tiers : un `DROP INDEX messages_content_unique` sur une
436
+ * base dont l'unicité avait été posée INLINE par le DDL de démarrage, donc sans
437
+ * index nommé. Trente-cinq minutes, deux bases détruites, la boucle brisée à la
438
+ * main.
439
+ *
440
+ * @param message - le message d'erreur rendu par le moteur.
441
+ * @returns `true` si l'échec dit « c'est déjà ainsi ».
442
+ */
443
+ export declare function alreadyInTargetState(message: string): boolean;
@@ -129,6 +129,21 @@ export declare function auditMigrationSql(sql: string, dialect: SqlDialect): IMi
129
129
  * @returns `true` si l'échec vient d'une question restée sans terminal.
130
130
  */
131
131
  export declare function isInteractivePromptFailure(output: string): boolean;
132
+ /**
133
+ * Reconnaît le refus de l'outil devant un `await` de premier niveau.
134
+ *
135
+ * C'est l'esbuild de `drizzle-kit` qui le détecte, en transpilant le fichier en
136
+ * CommonJS : sa ligne d'erreur nomme le fichier et la position. On la lit plutôt
137
+ * que d'analyser nous-mêmes les sources — l'outil qui refuse est aussi celui qui
138
+ * sait exactement où, sans faux positif. Fonction PURE, éprouvée sans process.
139
+ *
140
+ * @param output - sortie complète de l'outil.
141
+ * @returns le fichier et la ligne, ou `null` si l'échec est d'une autre nature.
142
+ */
143
+ export declare function topLevelAwaitFailure(output: string): {
144
+ file: string;
145
+ line: number;
146
+ } | null;
132
147
  /**
133
148
  * Pose le marqueur de format en tête des `.sql` d'un dossier qui ne l'ont pas.
134
149
  *
@@ -12,6 +12,37 @@ export declare const FRAMEWORK_RANK = 0;
12
12
  * celles des modules ; l'inverse n'arrive jamais.
13
13
  */
14
14
  export declare const APP_RANK = 1000000;
15
+ /**
16
+ * Noms qui ne peuvent PAS désigner le dossier d'un connecteur secondaire : ce
17
+ * sont ceux des dossiers de dialecte (et du journal) que le connecteur du
18
+ * framework range déjà sous `migrations/`. Un connecteur `postgres` y lirait les
19
+ * migrations de `default` comme les siennes.
20
+ */
21
+ export declare const RESERVED_MIGRATION_DIRS: readonly string[];
22
+ /**
23
+ * Le nom de ce connecteur collisionne-t-il avec un dossier de dialecte ?
24
+ *
25
+ * @param connector - nom du connecteur (clé de `connectors`).
26
+ * @returns `true` pour un connecteur SECONDAIRE nommé comme un dossier réservé.
27
+ */
28
+ export declare function isReservedConnectorName(connector: string): boolean;
29
+ /**
30
+ * Dossier des migrations PROPRES à un connecteur — la seule règle qui dit où
31
+ * elles vivent.
32
+ *
33
+ * Le connecteur du framework garde la racine (`migrations/<dialecte>`) : ses
34
+ * migrations décrivent la base du framework et des entités de l'application.
35
+ * Un connecteur secondaire reçoit son sous-dossier (`migrations/<connecteur>/
36
+ * <dialecte>`) : sa base ne reçoit que ce qui y est écrit, et celle du
37
+ * framework n'en voit rien — le dossier de dialecte qu'elle lit n'est pas le
38
+ * même.
39
+ *
40
+ * @param appDir - dossier de migrations de l'application (`appMigrationsDir`).
41
+ * @param connector - nom du connecteur.
42
+ * @returns le dossier parent des sous-dossiers de dialecte, ou `undefined` sans
43
+ * racine d'application, ou pour un nom réservé ({@link isReservedConnectorName}).
44
+ */
45
+ export declare function connectorMigrationsDir(appDir: string | undefined, connector: string): string | undefined;
15
46
  /**
16
47
  * Dossier des migrations livrées par ce paquet.
17
48
  *
@@ -47,8 +78,12 @@ export declare function frameworkMigrationsDir(): Promise<string>;
47
78
  *
48
79
  * @param appDir - dossier de migrations de l'application, s'il y en a un.
49
80
  * @param options.framework - `false` quand le module est data-only.
81
+ * @param options.connector - connecteur visé ; un connecteur secondaire ne
82
+ * reçoit que SES migrations ({@link connectorMigrationsDir}), jamais celles
83
+ * du framework. Omis : le registre complet (usage direct).
50
84
  * @returns le registre, prêt pour l'applicateur.
51
85
  */
52
86
  export declare function defaultMigrationSources(appDir?: string, options?: {
53
87
  framework?: boolean;
88
+ connector?: string;
54
89
  }): Promise<IMigrationSource[]>;
@@ -37,6 +37,8 @@ export type CommandFailureCode =
37
37
  | "NF_GENERATE_TOOL_MISSING"
38
38
  /** L'outil de génération pose une question, et il n'y a pas de terminal. */
39
39
  | "NF_GENERATE_NEEDS_ANSWER"
40
+ /** Un fichier d'entité porte un `await` de premier niveau, que l'outil ne sait pas lire. */
41
+ | "NF_GENERATE_TOP_LEVEL_AWAIT"
40
42
  /** L'outil de génération s'est arrêté ; ce qu'il a dit est remonté tel quel. */
41
43
  | "NF_GENERATE_TOOL_FAILED"
42
44
  /** La lecture du schéma d'une base existante a échoué. */
@@ -46,7 +48,9 @@ export type CommandFailureCode =
46
48
  /** L'adoption par lecture de la base, demandée alors qu'il existe déjà des migrations. */
47
49
  | "NF_MIGRATE_BASELINE_NOT_EMPTY"
48
50
  /** La table d'historique existe, mais ce n'est pas celle du framework. */
49
- | "NF_MIGRATE_HISTORY_FOREIGN";
51
+ | "NF_MIGRATE_HISTORY_FOREIGN"
52
+ /** Écrire une migration pour un connecteur secondaire au nom réservé (`postgres`…). */
53
+ | "NF_MIGRATE_SECONDARY_CONNECTOR";
50
54
  /**
51
55
  * Ce que la découverte des entités a VU, au moment où la commande a refusé.
52
56
  *
@@ -157,6 +161,22 @@ export declare function formatToolOutput(output: string, maxLines?: number): str
157
161
  * @returns le refus, prêt pour la ligne de commande comme pour l'écran.
158
162
  */
159
163
  export declare function generationNeedsAnswer(label: string, replay: string): IResolutionRefusal;
164
+ /**
165
+ * Un fichier lu par l'outil de génération porte un `await` de premier niveau.
166
+ *
167
+ * Nodefony est ESM, et l'application démarre très bien avec ce fichier. Mais
168
+ * l'outil qui écrit les migrations (`drizzle-kit`) relit les fichiers de schéma
169
+ * HORS de la chaîne de l'application, en les transpilant en CommonJS — où un
170
+ * `await` de premier niveau n'existe pas. Sans ce refus, l'utilisateur recevait
171
+ * une pile d'appels d'esbuild qui ne nomme ni l'outil ni le remède.
172
+ *
173
+ * @param label - ce qui était généré, tel qu'on le cite à l'utilisateur.
174
+ * @param file - fichier fautif, tel que l'outil l'a nommé.
175
+ * @param line - ligne de l'`await`.
176
+ * @param replay - la commande à relancer une fois le fichier corrigé.
177
+ * @returns le refus, prêt pour la ligne de commande comme pour l'écran.
178
+ */
179
+ export declare function generationTopLevelAwait(label: string, file: string, line: number, replay: string): IResolutionRefusal;
160
180
  /**
161
181
  * L'outil de génération s'est arrêté, et ce qu'il a dit est remonté TEL QUEL.
162
182
  *
@@ -218,6 +238,21 @@ export declare function moduleAbsent(): IResolutionRefusal;
218
238
  * @returns le refus, prêt pour la ligne de commande comme pour l'écran.
219
239
  */
220
240
  export declare function notConfigured(connector: string, driver: string): IResolutionRefusal;
241
+ /**
242
+ * Écrire une migration pour un connecteur secondaire dont le NOM est celui d'un
243
+ * dossier de dialecte — refusé.
244
+ *
245
+ * Un connecteur secondaire range ses migrations sous `migrations/<connecteur>/`,
246
+ * à côté des dossiers de dialecte du connecteur du framework. Nommé `postgres`,
247
+ * son dossier SERAIT celui des migrations PostgreSQL du framework : il les
248
+ * lirait comme les siennes, et les siennes seraient appliquées à la base du
249
+ * framework. On le refuse en nommant la collision, plutôt que de le découvrir
250
+ * au premier déploiement.
251
+ *
252
+ * @param connector - connecteur demandé.
253
+ * @returns le refus, prêt pour la ligne de commande comme pour l'écran.
254
+ */
255
+ export declare function reservedConnectorName(connector: string): IResolutionRefusal;
221
256
  /**
222
257
  * Traduit une résolution qui n'est PAS `ready` en refus lisible.
223
258
  *
@@ -50,6 +50,25 @@ export declare function readMigrationEnv(kernel: Kernel | null): IMigrationEnv;
50
50
  * @returns `true` si `orm:reset` est recevable.
51
51
  */
52
52
  export declare function resetAllowed(env: IMigrationEnv): boolean;
53
+ /**
54
+ * `orm:generate --apply` est-il recevable dans cet environnement ?
55
+ *
56
+ * Écrire une migration et l'appliquer dans le même souffle suppose qu'on relit
57
+ * le SQL produit entre les deux — ce que personne ne fait sur un serveur.
58
+ * Ailleurs qu'en développement, la migration a été écrite, relue et versionnée
59
+ * en amont : `orm:migrate` l'applique, et lui seul. Aucun drapeau ne lève ce
60
+ * refus, pour la même raison que {@link resetAllowed} n'en a pas.
61
+ *
62
+ * Même forme que `resetAllowed`, et c'est voulu : les deux disent « geste de
63
+ * développement », et deux rédactions de la même condition finiraient par
64
+ * diverger sans que rien ne le signale.
65
+ *
66
+ * @param env - environnement constaté.
67
+ * @returns `true` si l'enchaînement est recevable.
68
+ */
69
+ export declare function generateApplyAllowed(env: IMigrationEnv): boolean;
70
+ import { MEMORY_DATABASE } from "../memoryDatabase.js";
71
+ export { MEMORY_DATABASE };
53
72
  /**
54
73
  * Le mode de schéma qui s'applique à un connecteur.
55
74
  *
@@ -62,11 +81,70 @@ export declare function resetAllowed(env: IMigrationEnv): boolean;
62
81
  * - tout le reste → `none` : personne ne touche au schéma au démarrage, un
63
82
  * travail externe applique les migrations avant que le trafic n'arrive.
64
83
  *
84
+ * 🔴 **Une application qui VERSIONNE des migrations bascule en `migrate`, même
85
+ * en développement.** Deux fabricants du même schéma ne s'accordent pas, et
86
+ * rien ne le signale tant qu'on ne les fait pas se rencontrer : le DDL dérivé
87
+ * pose une contrainte d'unicité INLINE et anonyme (`content text NOT NULL
88
+ * UNIQUE`), là où le générateur de migrations raisonne sur un index NOMMÉ
89
+ * (`messages_content_unique`). La migration suivante émet alors un
90
+ * `DROP INDEX` d'un index qui n'a jamais existé, échoue sur `no such index`,
91
+ * inscrit son marqueur — et la réparation ne peut rien y faire, puisque le
92
+ * fichier est inapplicable. Vécu sur un agent tiers : trente-cinq minutes de
93
+ * `repair`/`baseline`/`reset`, deux bases détruites, la boucle brisée à la main.
94
+ * La condition est délibérément « l'application EN A », pas « elle en a en
95
+ * attente » : dès la première migration écrite, le schéma leur appartient.
96
+ * Elle est bornée au développement RÉEL, jamais à `NODE_ENV=test` : une suite
97
+ * lance un exemplaire par worker, et appliquer des migrations depuis plusieurs
98
+ * processus au démarrage est précisément ce que la règle ci-dessus refuse.
99
+ * Un `ddl` écrit dans la configuration gagne toujours — c'est un choix, pas une
100
+ * déduction.
101
+ *
65
102
  * @param explicit - valeur écrite dans la configuration du connecteur, si elle l'est.
66
103
  * @param env - environnement constaté.
104
+ * 🔴 **Une base VOLATILE (`:memory:`) dérive toujours son schéma**, en
105
+ * production comprise. Rien d'autre ne peut la peupler : elle repart vide à
106
+ * chaque démarrage, et une migration appliquée ne survit pas au processus. En
107
+ * `none`, elle restait vide, le verdict de schéma tombait à `divergent` et la
108
+ * sonde de disponibilité retenait la mise en service (vécu en CI : le connecteur
109
+ * d'un module de banc, ouvert en production sous dérogation).
110
+ *
111
+ * @param appHasMigrations - l'application versionne au moins une migration.
112
+ * @param volatile - la base est en mémoire (`:memory:`).
67
113
  * @returns le mode effectif.
68
114
  */
69
- export declare function resolveDdlMode(explicit: DdlMode | undefined, env: IMigrationEnv): DdlMode;
115
+ export declare function resolveDdlMode(explicit: DdlMode | undefined, env: IMigrationEnv, appHasMigrations?: boolean, volatile?: boolean): DdlMode;
116
+ /**
117
+ * L'application versionne-t-elle au moins une migration ?
118
+ *
119
+ * Lecture SYNCHRONE et bornée à un `readdirSync` par connecteur au démarrage —
120
+ * hors de tout chemin de requête. Elle doit l'être : le mode de schéma se
121
+ * décide avant la connexion, et le rendre asynchrone propagerait un `await`
122
+ * dans toute la chaîne de résolution pour une réponse qui ne change jamais
123
+ * pendant la vie du processus.
124
+ *
125
+ * Un dossier absent, illisible ou vide répond `false` — l'application n'a alors
126
+ * rien à appliquer, et `auto` reste le bon défaut de démarrage rapide.
127
+ *
128
+ * @param dir - dossier de migrations de l'application ({@link appMigrationsDir}).
129
+ * @returns `true` si au moins un fichier `.sql` y est versionné, à un niveau
130
+ * ou sous un sous-dossier de dialecte (`migrations/postgres/0001_x.sql`).
131
+ */
132
+ export declare function appVersionsMigrations(dir: string | undefined): boolean;
133
+ /**
134
+ * Des migrations versionnées CONCERNENT-elles ce connecteur ?
135
+ *
136
+ * C'est la question que pose la bascule `auto` → `migrate`, et elle n'est pas
137
+ * « l'application versionne-t-elle des migrations ? » : celles de la racine
138
+ * décrivent la base du framework. Posée telle quelle, elle faisait basculer en
139
+ * `migrate` un connecteur secondaire qui n'en recevra jamais une — sa base
140
+ * restait alors sans schéma, ni dérivé ni migré. On regarde donc le dossier
141
+ * DU connecteur ({@link connectorMigrationsDir}).
142
+ *
143
+ * @param connector - nom du connecteur.
144
+ * @param dir - dossier de migrations de l'application ({@link appMigrationsDir}).
145
+ * @returns `true` si le dossier propre au connecteur porte au moins un `.sql`.
146
+ */
147
+ export declare function connectorVersionsMigrations(connector: string, dir: string | undefined): boolean;
70
148
  /**
71
149
  * La conduite de la sonde de disponibilité face à un schéma en retard.
72
150
  *
@@ -80,6 +158,24 @@ export declare function resolveDdlMode(explicit: DdlMode | undefined, env: IMigr
80
158
  * @returns la conduite effective.
81
159
  */
82
160
  export declare function resolveCheckMode(explicit: MigrationCheckMode | undefined, env: IMigrationEnv): MigrationCheckMode;
161
+ /**
162
+ * En schéma `auto`, faut-il ÉNONCER au démarrage les migrations enregistrées
163
+ * que personne n'a appliquées ?
164
+ *
165
+ * Oui en développement, sauf conduite `off` — c'est ce conseil qui évite de
166
+ * conclure à une base incohérente devant une colonne inconnue. Jamais sur une
167
+ * base `:memory:` : elle repart VIDE à chaque démarrage, son historique aussi,
168
+ * si bien que tout y paraît « à appliquer » à chaque fois — un avertissement
169
+ * permanent qui n'apprend rien (vécu : le connecteur de banc `mediasoup`,
170
+ * une fois déclaré, réclamait à chaque démarrage les migrations du framework
171
+ * et de l'application, qui ne le concernent pas).
172
+ *
173
+ * @param check - conduite résolue ({@link resolveCheckMode}).
174
+ * @param env - environnement constaté.
175
+ * @param filename - fichier sqlite du connecteur, s'il en a un.
176
+ * @returns `true` si le conseil doit être rendu.
177
+ */
178
+ export declare function adviseMigrations(check: MigrationCheckMode, env: IMigrationEnv, filename: string | undefined): boolean;
83
179
  /** Ce qu'un connecteur est, du point de vue des migrations. */
84
180
  export type IConnectorResolution = {
85
181
  /** Le connecteur existe et son propriétaire sait migrer. */
@@ -1,3 +1,4 @@
1
+ import type { Container } from "nodefony";
1
2
  import { Orm } from "@nodefony/orm-core";
2
3
  import type { IOrmMigrationApplyReply, IOrmMigrationPlanReply, IOrmMigrationReply } from "@nodefony/orm-core";
3
4
  import { type ISchemaComparison } from "../migrator/schemaDiff.js";
@@ -18,6 +19,16 @@ export interface DrizzleOrmOptions {
18
19
  * `mysql`. Requise pour ces dialectes.
19
20
  */
20
21
  url?: string;
22
+ /**
23
+ * Container du service qui construit l'ORM — il porte le `syslog` du kernel.
24
+ *
25
+ * Sans lui, l'ORM se fabrique un container et un journal à lui, que rien ne
26
+ * relie à la sortie du serveur : « connexion perdue » et « connexion
27
+ * rétablie » partaient dans le vide, et l'exploitant ne voyait que les
28
+ * échecs des sondes, jamais le diagnostic de la reprise. Omis en usage
29
+ * direct (bancs de test) : l'ORM reste alors autonome, comme avant.
30
+ */
31
+ container?: Container;
21
32
  /**
22
33
  * Le schéma doit-il être DÉRIVÉ du code à la connexion ?
23
34
  *