@nodefony/drizzle 10.0.0-alpha.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/LICENSE +544 -0
- package/README.md +162 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
- package/dist/index.js +105 -0
- package/dist/nodefony/command/migrateShared.js +247 -0
- package/dist/nodefony/command/orm-generate.js +356 -0
- package/dist/nodefony/command/orm-migrate-baseline.js +208 -0
- package/dist/nodefony/command/orm-migrate-repair.js +114 -0
- package/dist/nodefony/command/orm-migrate-status.js +67 -0
- package/dist/nodefony/command/orm-migrate.js +141 -0
- package/dist/nodefony/command/orm-reset.js +166 -0
- package/dist/nodefony/config/config.js +107 -0
- package/dist/nodefony/config/defineModuleConfig.js +63 -0
- package/dist/nodefony/entity/auditEventEntity.js +93 -0
- package/dist/nodefony/entity/colKit.js +260 -0
- package/dist/nodefony/entity/idempotencyEntity.js +74 -0
- package/dist/nodefony/entity/sessionEntity.js +75 -0
- package/dist/nodefony/entity/tokenEntity.js +198 -0
- package/dist/nodefony/entity/totpSecretEntity.js +98 -0
- package/dist/nodefony/entity/userTable.js +141 -0
- package/dist/nodefony/entity/webAuthnCredentialEntity.js +114 -0
- package/dist/nodefony/entity/webhookEndpointEntity.js +106 -0
- package/dist/nodefony/interfaces/IDrizzleConfig.js +1 -0
- package/dist/nodefony/interfaces/index.js +1 -0
- package/dist/nodefony/migrations-schema/mysql.js +48 -0
- package/dist/nodefony/migrations-schema/postgres.js +48 -0
- package/dist/nodefony/migrations-schema/sqlite.js +48 -0
- package/dist/nodefony/registerStores.js +218 -0
- package/dist/nodefony/service/DrizzleService.js +282 -0
- package/dist/nodefony/src/DrizzleAuditStore.js +203 -0
- package/dist/nodefony/src/DrizzleIdempotencyStore.js +278 -0
- package/dist/nodefony/src/DrizzleTokenStore.js +244 -0
- package/dist/nodefony/src/DrizzleTotpSecretStore.js +151 -0
- package/dist/nodefony/src/DrizzleUserRepository.js +217 -0
- package/dist/nodefony/src/DrizzleWebAuthnCredentialStore.js +159 -0
- package/dist/nodefony/src/DrizzleWebhookStore.js +169 -0
- package/dist/nodefony/src/SessionStorage.js +259 -0
- package/dist/nodefony/src/connectorTarget.js +59 -0
- package/dist/nodefony/src/likeSql.js +50 -0
- package/dist/nodefony/src/migrator/DrizzleMigrator.js +775 -0
- package/dist/nodefony/src/migrator/adopt.js +553 -0
- package/dist/nodefony/src/migrator/appSchema.js +414 -0
- package/dist/nodefony/src/migrator/catalog.js +76 -0
- package/dist/nodefony/src/migrator/destructive.js +213 -0
- package/dist/nodefony/src/migrator/divergence.js +84 -0
- package/dist/nodefony/src/migrator/drivers/index.js +39 -0
- package/dist/nodefony/src/migrator/drivers/mysqlDriver.js +147 -0
- package/dist/nodefony/src/migrator/drivers/postgresDriver.js +151 -0
- package/dist/nodefony/src/migrator/drivers/sqliteDriver.js +121 -0
- package/dist/nodefony/src/migrator/explain.js +565 -0
- package/dist/nodefony/src/migrator/hash.js +47 -0
- package/dist/nodefony/src/migrator/history.js +219 -0
- package/dist/nodefony/src/migrator/index.js +16 -0
- package/dist/nodefony/src/migrator/kit.js +296 -0
- package/dist/nodefony/src/migrator/name.js +68 -0
- package/dist/nodefony/src/migrator/paths.js +88 -0
- package/dist/nodefony/src/migrator/refusals.js +143 -0
- package/dist/nodefony/src/migrator/resolve.js +281 -0
- package/dist/nodefony/src/migrator/schemaDiff.js +86 -0
- package/dist/nodefony/src/migrator/sources.js +419 -0
- package/dist/nodefony/src/migrator/status.js +231 -0
- package/dist/nodefony/src/migrator/types.js +91 -0
- package/dist/nodefony/src/orm-core/DrizzleOrm.js +1154 -0
- package/dist/nodefony/src/orm-core/DrizzleRepository.js +610 -0
- package/dist/nodefony/src/orm-core/DrizzleTransaction.js +106 -0
- package/dist/nodefony/src/orm-core/index.js +4 -0
- package/dist/nodefony/src/queryKit.js +318 -0
- package/dist/nodefony/src/safeTarget.js +55 -0
- package/dist/types/index.d.ts +76 -0
- package/dist/types/nodefony/command/migrateShared.d.ts +137 -0
- package/dist/types/nodefony/command/orm-generate.d.ts +53 -0
- package/dist/types/nodefony/command/orm-migrate-baseline.d.ts +87 -0
- package/dist/types/nodefony/command/orm-migrate-repair.d.ts +66 -0
- package/dist/types/nodefony/command/orm-migrate-status.d.ts +38 -0
- package/dist/types/nodefony/command/orm-migrate.d.ts +65 -0
- package/dist/types/nodefony/command/orm-reset.d.ts +55 -0
- package/dist/types/nodefony/config/config.d.ts +110 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +24 -0
- package/dist/types/nodefony/entity/auditEventEntity.d.ts +56 -0
- package/dist/types/nodefony/entity/colKit.d.ts +130 -0
- package/dist/types/nodefony/entity/idempotencyEntity.d.ts +52 -0
- package/dist/types/nodefony/entity/sessionEntity.d.ts +50 -0
- package/dist/types/nodefony/entity/tokenEntity.d.ts +53 -0
- package/dist/types/nodefony/entity/totpSecretEntity.d.ts +61 -0
- package/dist/types/nodefony/entity/userTable.d.ts +65 -0
- package/dist/types/nodefony/entity/webAuthnCredentialEntity.d.ts +57 -0
- package/dist/types/nodefony/entity/webhookEndpointEntity.d.ts +58 -0
- package/dist/types/nodefony/interfaces/IDrizzleConfig.d.ts +17 -0
- package/dist/types/nodefony/interfaces/index.d.ts +1 -0
- package/dist/types/nodefony/migrations-schema/mysql.d.ts +9 -0
- package/dist/types/nodefony/migrations-schema/postgres.d.ts +9 -0
- package/dist/types/nodefony/migrations-schema/sqlite.d.ts +9 -0
- package/dist/types/nodefony/registerStores.d.ts +52 -0
- package/dist/types/nodefony/service/DrizzleService.d.ts +27 -0
- package/dist/types/nodefony/src/DrizzleAuditStore.d.ts +65 -0
- package/dist/types/nodefony/src/DrizzleIdempotencyStore.d.ts +129 -0
- package/dist/types/nodefony/src/DrizzleTokenStore.d.ts +146 -0
- package/dist/types/nodefony/src/DrizzleTotpSecretStore.d.ts +60 -0
- package/dist/types/nodefony/src/DrizzleUserRepository.d.ts +67 -0
- package/dist/types/nodefony/src/DrizzleWebAuthnCredentialStore.d.ts +62 -0
- package/dist/types/nodefony/src/DrizzleWebhookStore.d.ts +79 -0
- package/dist/types/nodefony/src/SessionStorage.d.ts +72 -0
- package/dist/types/nodefony/src/connectorTarget.d.ts +48 -0
- package/dist/types/nodefony/src/likeSql.d.ts +29 -0
- package/dist/types/nodefony/src/migrator/DrizzleMigrator.d.ts +94 -0
- package/dist/types/nodefony/src/migrator/adopt.d.ts +280 -0
- package/dist/types/nodefony/src/migrator/appSchema.d.ts +223 -0
- package/dist/types/nodefony/src/migrator/catalog.d.ts +100 -0
- package/dist/types/nodefony/src/migrator/destructive.d.ts +123 -0
- package/dist/types/nodefony/src/migrator/divergence.d.ts +61 -0
- package/dist/types/nodefony/src/migrator/drivers/index.d.ts +26 -0
- package/dist/types/nodefony/src/migrator/drivers/mysqlDriver.d.ts +83 -0
- package/dist/types/nodefony/src/migrator/drivers/postgresDriver.d.ts +81 -0
- package/dist/types/nodefony/src/migrator/drivers/sqliteDriver.d.ts +53 -0
- package/dist/types/nodefony/src/migrator/explain.d.ts +424 -0
- package/dist/types/nodefony/src/migrator/hash.d.ts +39 -0
- package/dist/types/nodefony/src/migrator/history.d.ts +139 -0
- package/dist/types/nodefony/src/migrator/index.d.ts +20 -0
- package/dist/types/nodefony/src/migrator/kit.d.ts +141 -0
- package/dist/types/nodefony/src/migrator/name.d.ts +52 -0
- package/dist/types/nodefony/src/migrator/paths.d.ts +54 -0
- package/dist/types/nodefony/src/migrator/refusals.d.ts +170 -0
- package/dist/types/nodefony/src/migrator/resolve.d.ts +201 -0
- package/dist/types/nodefony/src/migrator/schemaDiff.d.ts +112 -0
- package/dist/types/nodefony/src/migrator/sources.d.ts +119 -0
- package/dist/types/nodefony/src/migrator/status.d.ts +132 -0
- package/dist/types/nodefony/src/migrator/types.d.ts +273 -0
- package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +241 -0
- package/dist/types/nodefony/src/orm-core/DrizzleRepository.d.ts +98 -0
- package/dist/types/nodefony/src/orm-core/DrizzleTransaction.d.ts +71 -0
- package/dist/types/nodefony/src/orm-core/index.d.ts +11 -0
- package/dist/types/nodefony/src/queryKit.d.ts +136 -0
- package/dist/types/nodefony/src/safeTarget.d.ts +42 -0
- package/docs/index.md +954 -0
- package/docs/migrations.md +691 -0
- package/migrations/mysql/0000_framework_init.sql +137 -0
- package/migrations/mysql/meta/_journal.json +13 -0
- package/migrations/postgres/0000_framework_init.sql +128 -0
- package/migrations/postgres/meta/_journal.json +13 -0
- package/migrations/sqlite/0000_framework_init.sql +127 -0
- package/migrations/sqlite/meta/_journal.json +13 -0
- package/package.json +126 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { SqlDialect } from "../../interfaces/IDrizzleConfig.js";
|
|
2
|
+
/** Une table trouvée dans un fichier d'entité de l'application. */
|
|
3
|
+
export interface IDiscoveredTable {
|
|
4
|
+
/** Chemin absolu du fichier qui l'exporte. */
|
|
5
|
+
file: string;
|
|
6
|
+
/** Nom sous lequel le fichier l'exporte (`postTable`, `PostEntity`…). */
|
|
7
|
+
exportName: string;
|
|
8
|
+
/** Nom de la table en base — c'est lui qui compte, pas l'identifiant JS. */
|
|
9
|
+
tableName: string;
|
|
10
|
+
/** Moteur pour lequel elle est écrite — `null` si aucun des trois. */
|
|
11
|
+
dialect: SqlDialect | null;
|
|
12
|
+
}
|
|
13
|
+
/** Ce qu'un fichier d'entité a refusé de livrer. */
|
|
14
|
+
export interface IUnreadableEntityFile {
|
|
15
|
+
/** Chemin absolu du fichier. */
|
|
16
|
+
file: string;
|
|
17
|
+
/** La cause, telle que l'import l'a rendue. */
|
|
18
|
+
cause: string;
|
|
19
|
+
}
|
|
20
|
+
/** Résultat d'une découverte : ce qui a été lu, et ce qui a résisté. */
|
|
21
|
+
export interface IDiscoveredSchema {
|
|
22
|
+
tables: IDiscoveredTable[];
|
|
23
|
+
unreadable: IUnreadableEntityFile[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fichiers d'entités d'une cible (l'application, ou l'un de ses modules).
|
|
27
|
+
*
|
|
28
|
+
* Les `*.schema.ts` sont écartés : c'est la convention du générateur pour les
|
|
29
|
+
* contrats d'entrée (Zod), qui n'ont rien à faire dans un schéma de base.
|
|
30
|
+
*
|
|
31
|
+
* @param targetDir - dossier d'une cible (contient `index.ts` + `nodefony/`).
|
|
32
|
+
* @returns les chemins absolus, triés — l'ordre doit être le même partout.
|
|
33
|
+
*/
|
|
34
|
+
export declare function entityFilesOf(targetDir: string): Promise<string[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Importe des fichiers d'entités et relève les tables qu'ils exportent.
|
|
37
|
+
*
|
|
38
|
+
* L'import est la seule façon HONNÊTE de savoir ce qu'un fichier fournit : lire
|
|
39
|
+
* un nom d'export au motif qu'il finit par `Table` marcherait sur le code que
|
|
40
|
+
* le générateur écrit, et sur rien d'autre — or ces fichiers sont faits pour
|
|
41
|
+
* être modifiés à la main, c'est même écrit dans leur en-tête.
|
|
42
|
+
*
|
|
43
|
+
* Un fichier qui refuse de s'importer n'est PAS ignoré : il est rendu à
|
|
44
|
+
* l'appelant avec sa cause. C'est presque toujours le même défaut — un accès au
|
|
45
|
+
* kernel à l'évaluation du module — et le taire produirait une migration
|
|
46
|
+
* amputée là où il faut une phrase.
|
|
47
|
+
*
|
|
48
|
+
* @param files - chemins absolus des fichiers d'entités.
|
|
49
|
+
* @returns les tables trouvées et les fichiers illisibles.
|
|
50
|
+
*/
|
|
51
|
+
export declare function collectTables(files: readonly string[]): Promise<IDiscoveredSchema>;
|
|
52
|
+
/**
|
|
53
|
+
* Moteur pour lequel une table est écrite.
|
|
54
|
+
*
|
|
55
|
+
* Une entité d'application est du Drizzle **natif** : `sqliteTable`, `pgTable` et
|
|
56
|
+
* `mysqlTable` produisent trois objets différents, et une table écrite pour un
|
|
57
|
+
* moteur est simplement IGNORÉE par l'outil quand il en génère un autre — sans
|
|
58
|
+
* un mot, comme d'habitude. Constaté sur une application témoin : six tables
|
|
59
|
+
* découvertes, quatre écrites, et un message qui annonçait six.
|
|
60
|
+
*
|
|
61
|
+
* @param table - table relevée dans un fichier d'entité.
|
|
62
|
+
* @returns le dialecte, ou `null` si ce n'est aucun des trois.
|
|
63
|
+
*/
|
|
64
|
+
export declare function dialectOf(table: unknown): SqlDialect | null;
|
|
65
|
+
/**
|
|
66
|
+
* Spécificateur d'import de `from` vers `to`, écrit pour VOYAGER.
|
|
67
|
+
*
|
|
68
|
+
* Un spécificateur s'écrit en `/` sur les trois systèmes — c'est du texte que
|
|
69
|
+
* lit un outil, pas un accès disque. L'extension est explicite : le module
|
|
70
|
+
* temporaire est compilé par l'outil, qui doit savoir quoi ouvrir sans deviner.
|
|
71
|
+
*
|
|
72
|
+
* @param fromFile - fichier qui contiendra l'import.
|
|
73
|
+
* @param toFile - fichier visé.
|
|
74
|
+
* @returns un spécificateur relatif, toujours préfixé `./` ou `../`.
|
|
75
|
+
*/
|
|
76
|
+
export declare function importSpecifier(fromFile: string, toFile: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Écrit le module temporaire que `drizzle-kit` lira comme « le schéma ».
|
|
79
|
+
*
|
|
80
|
+
* Chaque table reçoit un alias unique et neutre : deux fichiers peuvent très
|
|
81
|
+
* bien exporter deux tables sous le même identifiant JS, et un ré-export en
|
|
82
|
+
* étoile les rendrait AMBIGUËS — l'ambiguïté n'est pas une erreur en ESM, c'est
|
|
83
|
+
* une absence silencieuse. Le nom de la table en base, lui, n'est pas touché :
|
|
84
|
+
* il vit dans l'appel `…Table("nom")`, pas dans l'identifiant.
|
|
85
|
+
*
|
|
86
|
+
* @param file - chemin du module à écrire.
|
|
87
|
+
* @param tables - tables à ré-exporter, dans l'ordre de découverte.
|
|
88
|
+
* @returns le contenu écrit (rendu pour les bancs).
|
|
89
|
+
*/
|
|
90
|
+
export declare function writeSchemaModule(file: string, tables: readonly IDiscoveredTable[]): Promise<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Schéma PostgreSQL réellement visé par une URL de connexion.
|
|
93
|
+
*
|
|
94
|
+
* 🔴 L'outil d'introspection ne suit PAS le `search_path` porté par l'URL : il
|
|
95
|
+
* lit `public`, quoi qu'on lui donne. Sans cette dérivation, adopter une
|
|
96
|
+
* application logée dans un schéma dédié — le montage habituel d'une base
|
|
97
|
+
* mutualisée — écrivait la référence des tables de `public`, c'est-à-dire
|
|
98
|
+
* celles de quelqu'un d'autre, et déclarait absentes les siennes. Constaté sur
|
|
99
|
+
* un serveur réel : la référence décrivait trois tables étrangères au projet.
|
|
100
|
+
*
|
|
101
|
+
* Deux formes sont reconnues, parce que les deux circulent : le `search_path`
|
|
102
|
+
* passé dans `options`, et le paramètre `schema` que posent certains outils.
|
|
103
|
+
* Une URL sans rien rend `null` — l'appelant laisse alors le défaut de l'outil,
|
|
104
|
+
* qui est le bon.
|
|
105
|
+
*
|
|
106
|
+
* @param url - URL de connexion, telle que le connecteur la porte.
|
|
107
|
+
* @returns le premier schéma du chemin de recherche, ou `null`.
|
|
108
|
+
*/
|
|
109
|
+
export declare function postgresSchemaOf(url: string): string | null;
|
|
110
|
+
/**
|
|
111
|
+
* Écrit la configuration `drizzle-kit` d'une génération d'application.
|
|
112
|
+
*
|
|
113
|
+
* 🔴 **Les chemins y sont RELATIFS, et ce n'est pas un style.** L'outil préfixe
|
|
114
|
+
* son dossier de sortie par `./` : un chemin absolu devient `.//Users/…`, la
|
|
115
|
+
* lecture échoue — et l'échec se présente comme un succès, puisque l'outil rend
|
|
116
|
+
* 0 quand il rate. La configuration est donc écrite pour être lue depuis la
|
|
117
|
+
* racine de l'application, qui est le dossier d'exécution.
|
|
118
|
+
*
|
|
119
|
+
* `tablesFilter` exclut ce que l'application ne possède pas : les tables du
|
|
120
|
+
* framework et la table d'historique. Sans lui, une entité d'application qui
|
|
121
|
+
* référence une table du framework la ferait entrer dans le diff, et la
|
|
122
|
+
* migration porterait un second `CREATE TABLE` de cette table — qui échoue en
|
|
123
|
+
* production, sur toute base déjà migrée.
|
|
124
|
+
*
|
|
125
|
+
* @param options - où écrire, quoi lire, où sortir, quoi exclure.
|
|
126
|
+
* @returns le contenu écrit (rendu pour les bancs).
|
|
127
|
+
*/
|
|
128
|
+
export declare function writeKitConfig({ file, projectRoot, schemaFile, outDir, dialect, excludedTables, dbUrl, }: {
|
|
129
|
+
file: string;
|
|
130
|
+
projectRoot: string;
|
|
131
|
+
schemaFile: string;
|
|
132
|
+
outDir: string;
|
|
133
|
+
dialect: SqlDialect;
|
|
134
|
+
excludedTables: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* Coordonnées de connexion — posées UNIQUEMENT pour l'introspection.
|
|
137
|
+
*
|
|
138
|
+
* La génération, elle, ne touche aucune base : son diff se calcule entre les
|
|
139
|
+
* instantanés du dossier et les entités. Écrire des coordonnées dans sa
|
|
140
|
+
* configuration laisserait croire le contraire à qui relit le fichier, et
|
|
141
|
+
* ferait porter à une commande de lecture pure le risque d'une connexion.
|
|
142
|
+
*/
|
|
143
|
+
dbUrl?: string;
|
|
144
|
+
}): Promise<string>;
|
|
145
|
+
/**
|
|
146
|
+
* Écrit une migration LIBRE et son entrée de journal, sans `drizzle-kit`.
|
|
147
|
+
*
|
|
148
|
+
* C'est la porte de sortie du modèle déclaratif : une vue, un déclencheur, une
|
|
149
|
+
* clé étrangère réelle, un remplissage de données ne se DÉDUISENT d'aucun
|
|
150
|
+
* schéma. Sans elle, on n'aurait le choix qu'entre renoncer et écrire un
|
|
151
|
+
* fichier à la main dans un journal dont le format n'est pas documenté — deux
|
|
152
|
+
* façons de casser l'historique.
|
|
153
|
+
*
|
|
154
|
+
* Le fichier est vide de toute instruction : un squelette qui « propose » du
|
|
155
|
+
* SQL est un squelette qu'on applique sans le lire.
|
|
156
|
+
*
|
|
157
|
+
* @param options - dossier de sortie, dialecte, nom de la migration.
|
|
158
|
+
* @returns le tag attribué et le chemin du fichier écrit.
|
|
159
|
+
* @throws Error si le journal existant est illisible — on n'en réécrit JAMAIS
|
|
160
|
+
* un par-dessus : il porte l'historique déjà appliqué en production.
|
|
161
|
+
*/
|
|
162
|
+
export declare function writeCustomMigration({ outDir, dialect, name, now, }: {
|
|
163
|
+
outDir: string;
|
|
164
|
+
dialect: SqlDialect;
|
|
165
|
+
name: string;
|
|
166
|
+
now?: number;
|
|
167
|
+
}): Promise<{
|
|
168
|
+
tag: string;
|
|
169
|
+
file: string;
|
|
170
|
+
}>;
|
|
171
|
+
/** Une entité que le registre connaît, et la table qu'elle vise. */
|
|
172
|
+
export interface IExpectedEntity {
|
|
173
|
+
/** Nom logique de l'entité, tel qu'il est enregistré. */
|
|
174
|
+
entity: string;
|
|
175
|
+
/** Nom de la table en base. */
|
|
176
|
+
table: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Ce que le registre attend et que les fichiers ne fournissent PAS.
|
|
180
|
+
*
|
|
181
|
+
* C'est le seul contrôle qu'un registre puisse rendre et qu'aucun outil de
|
|
182
|
+
* génération ne rendra jamais : `drizzle-kit` ne sait pas ce qu'une application
|
|
183
|
+
* a déclaré, il ne voit que ce qu'on lui donne à lire. Sans cette confrontation,
|
|
184
|
+
* une entité dont le fichier a été déplacé, renommé, ou rendu illisible
|
|
185
|
+
* disparaît de la migration **sans un mot** — et une migration ne se corrige
|
|
186
|
+
* pas : elle est immuable dès qu'une base l'a reçue.
|
|
187
|
+
*
|
|
188
|
+
* Les tables du framework sont écartées des deux côtés : elles sont fournies par
|
|
189
|
+
* une autre source, appliquée avant.
|
|
190
|
+
*
|
|
191
|
+
* @param expected - entités du registre, sur le connecteur visé.
|
|
192
|
+
* @param providedTables - noms des tables que les fichiers découverts fournissent.
|
|
193
|
+
* @param frameworkTables - noms des tables construites par le framework.
|
|
194
|
+
* @returns les entités sans fournisseur, dans l'ordre reçu.
|
|
195
|
+
*/
|
|
196
|
+
export declare function missingProviders(expected: readonly IExpectedEntity[], providedTables: ReadonlySet<string>, frameworkTables: ReadonlySet<string>): IExpectedEntity[];
|
|
197
|
+
/**
|
|
198
|
+
* Les tables de l'application qui usurpent une table du framework.
|
|
199
|
+
*
|
|
200
|
+
* Deux `CREATE TABLE` pour un même nom : la migration passe sur une base vierge
|
|
201
|
+
* et échoue sur toute base déjà migrée — c'est-à-dire en production, et nulle
|
|
202
|
+
* part ailleurs. C'est le pire endroit pour découvrir la faute, donc on la dit
|
|
203
|
+
* avant d'écrire quoi que ce soit.
|
|
204
|
+
*
|
|
205
|
+
* @param tables - tables fournies par les fichiers de l'application.
|
|
206
|
+
* @param frameworkTables - noms des tables construites par le framework.
|
|
207
|
+
* @returns les tables en conflit, avec le fichier qui les exporte.
|
|
208
|
+
*/
|
|
209
|
+
export declare function usurpedTables(tables: readonly IDiscoveredTable[], frameworkTables: ReadonlySet<string>): IDiscoveredTable[];
|
|
210
|
+
/**
|
|
211
|
+
* Tables attendues sur un connecteur, telles que le REGISTRE les connaît.
|
|
212
|
+
*
|
|
213
|
+
* Le registre est la seule source qui sache ce que l'application DÉCLARE :
|
|
214
|
+
* les fichiers disent ce qu'elle fournit, la base ce qu'elle porte, et c'est
|
|
215
|
+
* le croisement des trois qui fait les verdicts. Deux commandes en dépendent —
|
|
216
|
+
* la génération pour repérer une entité sans fichier, l'adoption pour ne lire
|
|
217
|
+
* QUE les tables de l'application — et une seconde copie divergerait en
|
|
218
|
+
* silence.
|
|
219
|
+
*
|
|
220
|
+
* @param connector - connecteur visé.
|
|
221
|
+
* @returns les entités et leur table, dans l'ordre du registre.
|
|
222
|
+
*/
|
|
223
|
+
export declare function registeredTables(connector: string): IExpectedEntity[];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { SqlDialect } from "../../config/config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Lecture du catalogue du serveur — quelles tables existent, quelles colonnes
|
|
4
|
+
* elles portent —, écrite UNE fois par dialecte et partagée par ses deux
|
|
5
|
+
* porteurs.
|
|
6
|
+
*
|
|
7
|
+
* ## Pourquoi ce fichier existe
|
|
8
|
+
*
|
|
9
|
+
* Deux composants ont besoin de la même réponse, depuis deux connexions
|
|
10
|
+
* différentes : le pilote de l'applicateur de migrations, qui tient sa propre
|
|
11
|
+
* connexion, et l'ORM, qui compare son schéma déclaré à la base **sur la
|
|
12
|
+
* connexion qu'il a déjà**. Recopier les trois requêtes chez le second les
|
|
13
|
+
* ferait diverger en silence — chacune passant ses propres tests — et la
|
|
14
|
+
* divergence ne se verrait qu'à travers un verdict FAUX rendu à un exploitant.
|
|
15
|
+
*
|
|
16
|
+
* ## Pourquoi l'ORM n'ouvre pas simplement un pilote de migration
|
|
17
|
+
*
|
|
18
|
+
* Parce qu'une seconde connexion sur `:memory:` désigne une base **différente
|
|
19
|
+
* et vide**. Le diff y annoncerait toutes les tables manquantes et un
|
|
20
|
+
* rattrapage y écrirait dans une base que personne ne lit. Le lecteur est donc
|
|
21
|
+
* paramétré par l'exécuteur de requêtes de son porteur, jamais par une
|
|
22
|
+
* connexion à lui.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Exécute une requête paramétrée et rend ses lignes.
|
|
26
|
+
*
|
|
27
|
+
* Les paramètres s'écrivent `?` dans tous les dialectes ; c'est au porteur de
|
|
28
|
+
* traduire s'il le faut (PostgreSQL attend `$1`).
|
|
29
|
+
*/
|
|
30
|
+
export type SqlQuery = <T extends Record<string, unknown>>(sql: string, params?: readonly unknown[]) => Promise<T[]>;
|
|
31
|
+
/** Ce dont la comparaison de schéma a besoin, et rien de plus. */
|
|
32
|
+
export interface ISchemaReader {
|
|
33
|
+
/**
|
|
34
|
+
* Deux noms de COLONNE désignent-ils la même, pour ce moteur ?
|
|
35
|
+
*
|
|
36
|
+
* 🔴 **La sémantique est celle du MOTEUR** : si le serveur sait résoudre ce
|
|
37
|
+
* nom dans un `SELECT`, la colonne existe pour l'application ; sinon la
|
|
38
|
+
* requête du code échouerait vraiment, et l'annoncer manquante est JUSTE.
|
|
39
|
+
*
|
|
40
|
+
* - **SQLite** — sans distinction de casse, comme sa résolution de noms.
|
|
41
|
+
* - **MySQL** — sans distinction de casse, sur **toutes** les plateformes :
|
|
42
|
+
* contrairement aux tables, les noms de colonnes ne dépendent pas de
|
|
43
|
+
* `lower_case_table_names`.
|
|
44
|
+
* - **PostgreSQL** — EXACT : les identifiants sont cités (Drizzle cite tout),
|
|
45
|
+
* donc `SELECT "createdAt"` sur une colonne `createdat` échoue pour de bon.
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ **Il n'y a délibérément PAS d'équivalent synchrone pour les TABLES.**
|
|
48
|
+
* Leur sensibilité à la casse dépend de la MACHINE — `lower_case_table_names`
|
|
49
|
+
* vaut `0` sur Linux (sensible, constaté sur MySQL 8.4) et `1` ou `2`
|
|
50
|
+
* ailleurs. Une règle déduite du seul dialecte serait donc fausse une fois
|
|
51
|
+
* sur deux, sans que rien ne le dise. Elle se **CONSTATE** par
|
|
52
|
+
* {@link ISchemaReader.tableExists}, qui interroge le catalogue du serveur et
|
|
53
|
+
* hérite de sa collation — jamais elle ne se déduit.
|
|
54
|
+
*
|
|
55
|
+
* @param declared - nom tel que le code le déclare.
|
|
56
|
+
* @param actual - nom tel que la base le rend.
|
|
57
|
+
* @returns `true` si le moteur les résoudrait vers la même colonne.
|
|
58
|
+
*/
|
|
59
|
+
sameColumnName(declared: string, actual: string): boolean;
|
|
60
|
+
/** La table existe-t-elle dans le schéma courant de la connexion ? */
|
|
61
|
+
tableExists(table: string): Promise<boolean>;
|
|
62
|
+
/** Colonnes de la table, telles que la base les déclare. */
|
|
63
|
+
columnsOf(table: string): Promise<string[]>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Compare deux noms de COLONNE selon la résolution du moteur.
|
|
67
|
+
*
|
|
68
|
+
* Exportée parce qu'elle porte une RÈGLE : la recopier ailleurs la ferait
|
|
69
|
+
* diverger, et une divergence de casse ne se voit que sur une base adoptée,
|
|
70
|
+
* c'est-à-dire chez l'utilisateur.
|
|
71
|
+
*
|
|
72
|
+
* Elle ne vaut PAS pour les tables — voir {@link ISchemaReader.sameColumnName},
|
|
73
|
+
* qui dit pourquoi leur sensibilité se constate au lieu de se déduire.
|
|
74
|
+
*
|
|
75
|
+
* @param dialect - moteur qui résoudrait le nom.
|
|
76
|
+
* @param declared - nom tel que le code le déclare.
|
|
77
|
+
* @param actual - nom tel que la base le rend.
|
|
78
|
+
* @returns `true` si le moteur les résoudrait vers la même colonne.
|
|
79
|
+
*/
|
|
80
|
+
export declare function sameColumnName(dialect: SqlDialect, declared: string, actual: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Compose un lecteur de catalogue au-dessus d'un exécuteur de requêtes.
|
|
83
|
+
*
|
|
84
|
+
* @param dialect - dialecte du serveur interrogé.
|
|
85
|
+
* @param query - exécuteur du porteur (pilote de migration, ou ORM connecté).
|
|
86
|
+
* @returns le lecteur, sans état ni connexion propre.
|
|
87
|
+
*/
|
|
88
|
+
export declare function schemaReader(dialect: SqlDialect, query: SqlQuery): ISchemaReader;
|
|
89
|
+
/**
|
|
90
|
+
* Traduit les paramètres `?` du dialecte commun en `$n` PostgreSQL.
|
|
91
|
+
*
|
|
92
|
+
* L'applicateur écrit ses requêtes une seule fois, avec la forme la plus
|
|
93
|
+
* répandue ; chaque pilote l'adapte. Aucune des requêtes de l'applicateur ne
|
|
94
|
+
* contient de littéral `?` — les valeurs, elles, sont bindées, jamais
|
|
95
|
+
* concaténées.
|
|
96
|
+
*
|
|
97
|
+
* @param sql - requête écrite avec des `?`.
|
|
98
|
+
* @returns la même requête, paramètres numérotés.
|
|
99
|
+
*/
|
|
100
|
+
export declare function toDollarParams(sql: string): string;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { IMigrationFile } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Ce qu'une migration s'apprête à DÉTRUIRE — constaté avant d'appliquer.
|
|
4
|
+
*
|
|
5
|
+
* ## Pourquoi ce garde existe, alors qu'aucun outil de migration n'en a
|
|
6
|
+
*
|
|
7
|
+
* La question posée était : « faut-il sauvegarder la base avant une
|
|
8
|
+
* migration ? ». La réponse honnête est **non, et personne ne le fait** —
|
|
9
|
+
* Flyway, Liquibase, Rails, Django, Alembic, Prisma : aucun ne sauvegarde. Les
|
|
10
|
+
* raisons tiennent :
|
|
11
|
+
*
|
|
12
|
+
* - l'outil n'a **ni les droits, ni la place, ni le temps**. Le compte qui migre
|
|
13
|
+
* a le droit de modifier le schéma, pas d'exporter toutes les données — et
|
|
14
|
+
* c'est exactement ce qu'on veut ;
|
|
15
|
+
* - la sauvegarde est un **métier d'exploitation** (instantané de volume,
|
|
16
|
+
* restauration à un instant donné, réplica), déjà outillé et déjà froid ;
|
|
17
|
+
* - un instantané pris par l'outil donnerait une **fausse assurance**.
|
|
18
|
+
* Restaurer une base de production est une décision et une interruption de
|
|
19
|
+
* service, jamais un drapeau qu'on tape par réflexe. Le pire des scénarios
|
|
20
|
+
* est celui où quelqu'un migre sans précaution *parce que « l'outil
|
|
21
|
+
* sauvegarde »*.
|
|
22
|
+
*
|
|
23
|
+
* Mais il restait un vrai trou, et c'est lui qu'on ferme ici : **appliquer un
|
|
24
|
+
* `DROP COLUMN` en production sans un mot**. L'outil ne sauvegarde pas — il
|
|
25
|
+
* **empêche d'appliquer sans savoir**. C'est ce que fait l'analyse de Atlas, et
|
|
26
|
+
* ce qu'aucun applicateur de l'écosystème Node ne propose.
|
|
27
|
+
*
|
|
28
|
+
* ## Ce que ce scan N'EST PAS
|
|
29
|
+
*
|
|
30
|
+
* Ce n'est pas un analyseur syntaxique SQL, et il ne prétend pas à
|
|
31
|
+
* l'exhaustivité : il reconnaît des formes. Une instruction destructive écrite
|
|
32
|
+
* d'une façon qu'il ne connaît pas passera. **Il ne remplace donc jamais la
|
|
33
|
+
* lecture du SQL** (`--dry-run`), et surtout pas la règle qui protège vraiment :
|
|
34
|
+
* ne jamais faire un changement destructif dans la même version que le code qui
|
|
35
|
+
* s'en sert (étendre, déployer, migrer les données, retirer une version plus
|
|
36
|
+
* tard). Le retour arrière porte alors sur le CODE, jamais sur la base.
|
|
37
|
+
*
|
|
38
|
+
* Un faux positif coûte une lecture et un drapeau ; un faux négatif coûte des
|
|
39
|
+
* données. Le scan penche donc toujours du côté du signalement.
|
|
40
|
+
*/
|
|
41
|
+
/** Gravité d'une trouvaille — elles ne se traitent pas pareil. */
|
|
42
|
+
export type DestructiveSeverity =
|
|
43
|
+
/** Des données existantes disparaissent. Refusé hors développement. */
|
|
44
|
+
"data-loss"
|
|
45
|
+
/**
|
|
46
|
+
* Rien ne disparaît, mais l'instruction peut échouer sur des données
|
|
47
|
+
* existantes, ou casser le code de la version précédente. Signalé, jamais
|
|
48
|
+
* bloquant : c'est le lot normal d'une migration qui fait évoluer un schéma.
|
|
49
|
+
*/
|
|
50
|
+
| "breaking";
|
|
51
|
+
/** Une instruction qui mérite d'être vue avant d'être exécutée. */
|
|
52
|
+
export interface IDestructiveFinding {
|
|
53
|
+
source: string;
|
|
54
|
+
tag: string;
|
|
55
|
+
/** Chemin du fichier — pour aller le lire. */
|
|
56
|
+
path: string;
|
|
57
|
+
severity: DestructiveSeverity;
|
|
58
|
+
/** Étiquette courte et stable, lisible par une machine. */
|
|
59
|
+
kind: string;
|
|
60
|
+
/** Ce qui est perdu ou risqué, en français. */
|
|
61
|
+
what: string;
|
|
62
|
+
/** L'instruction elle-même, bornée pour rester lisible. */
|
|
63
|
+
statement: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Cherche, dans les migrations en attente, ce qui détruit ou casse.
|
|
67
|
+
*
|
|
68
|
+
* @param files - migrations qui vont être appliquées.
|
|
69
|
+
* @returns une trouvaille par instruction reconnue, dans l'ordre d'application.
|
|
70
|
+
*/
|
|
71
|
+
export declare function scanDestructive(files: readonly IMigrationFile[]): IDestructiveFinding[];
|
|
72
|
+
/** Les trouvailles qui font vraiment disparaître des données. */
|
|
73
|
+
export declare function dataLoss(findings: readonly IDestructiveFinding[]): IDestructiveFinding[];
|
|
74
|
+
/**
|
|
75
|
+
* Rend le bilan lisible par un humain — le fait, puis ce qu'il faut faire.
|
|
76
|
+
*
|
|
77
|
+
* @param findings - trouvailles à présenter.
|
|
78
|
+
* @param bloquant - la commande refuse-t-elle d'appliquer ?
|
|
79
|
+
* @returns le texte, sans mise en forme (l'appelant colore s'il le veut).
|
|
80
|
+
*/
|
|
81
|
+
export declare function renderDestructive(findings: readonly IDestructiveFinding[], bloquant: boolean): string;
|
|
82
|
+
/** Les gestes proposés face à un refus destructif. */
|
|
83
|
+
export declare function destructiveActions(connector: string): string[];
|
|
84
|
+
/** Résumé d'une ligne, pour un message ou un journal. */
|
|
85
|
+
export declare function summarizeDestructive(findings: readonly IDestructiveFinding[], connector: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* Ce lot de migrations touche-t-il des lignes qui existaient déjà ?
|
|
88
|
+
*
|
|
89
|
+
* @param files - migrations sur le point d'être appliquées.
|
|
90
|
+
* @returns vrai dès qu'une instruction modifie une table existante.
|
|
91
|
+
*/
|
|
92
|
+
export declare function touchesExistingRows(files: readonly {
|
|
93
|
+
statements: readonly string[];
|
|
94
|
+
}[]): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Comment vérifier qu'une migration a préservé les données — **en UN geste**.
|
|
97
|
+
*
|
|
98
|
+
* Cette phrase existe parce que la sortie de SUCCÈS ne disait rien. Le produit
|
|
99
|
+
* annonçait « ✓ 1 migration appliquée » et s'arrêtait là ; l'agent à qui l'on
|
|
100
|
+
* demandait de prouver que les données avaient suivi n'avait aucun moyen sous
|
|
101
|
+
* les yeux, et celui qu'il inventait — repartir d'une base vide — détruit
|
|
102
|
+
* précisément ce qu'il fallait observer.
|
|
103
|
+
*
|
|
104
|
+
* 🔴 Elle NOMME la base, et c'est ce qui manquait au premier jet. Dire « une
|
|
105
|
+
* copie de la base » sans dire laquelle laisse deviner un chemin : mesuré au
|
|
106
|
+
* banc, l'agent a suivi le conseil, visé le mauvais fichier, vu son `cp`
|
|
107
|
+
* échouer en silence, puis FABRIQUÉ une base au client SQL — avec une table
|
|
108
|
+
* d'historique inventée. La migration a été refusée sur cette base bancale, et
|
|
109
|
+
* c'est ce refus qui l'a renvoyé détruire la vraie. Le produit connaissait
|
|
110
|
+
* pourtant l'emplacement : il le publie dans sa propre sortie.
|
|
111
|
+
*
|
|
112
|
+
* Elle nomme aussi les deux interpréteurs : « VAR=x commande » est de la
|
|
113
|
+
* syntaxe POSIX, que celui de Windows refuse.
|
|
114
|
+
*
|
|
115
|
+
* @param connector - connecteur visé, pour composer la commande.
|
|
116
|
+
* @param target - la base telle que le rapport la publie (`driver.target`),
|
|
117
|
+
* et son dialecte : on copie un FICHIER en sqlite, on exporte ailleurs.
|
|
118
|
+
* @returns la phrase à rendre après une application réussie.
|
|
119
|
+
*/
|
|
120
|
+
export declare function checkDataAdvice(connector: string, target?: {
|
|
121
|
+
dialect: string;
|
|
122
|
+
target?: string;
|
|
123
|
+
}): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { IMigrationPlan } from "./types.js";
|
|
2
|
+
import { type ISchemaComparison } from "./schemaDiff.js";
|
|
3
|
+
/**
|
|
4
|
+
* Ce que la base porte face à ce que le code DÉCLARE, sans condition de verdict.
|
|
5
|
+
*
|
|
6
|
+
* Séparé de {@link describeDivergence} parce que les deux répondent à des
|
|
7
|
+
* questions différentes, et que la seconde REFUSE de répondre avant que le plan
|
|
8
|
+
* soit à jour — ce qui est juste pour un rapport d'état (un écart n'a de sens
|
|
9
|
+
* qu'une fois tout appliqué) et faux pour qui doit décider AVANT d'agir.
|
|
10
|
+
*
|
|
11
|
+
* Le cas qui a exigé cette séparation : l'adoption d'une base existante. Elle
|
|
12
|
+
* doit constater l'état RÉEL avant d'écrire quoi que ce soit dans l'historique
|
|
13
|
+
* — après, il est trop tard, l'affirmation est déjà gravée.
|
|
14
|
+
*
|
|
15
|
+
* Ne modifie jamais rien, et ne jette jamais : une base muette n'est pas une
|
|
16
|
+
* divergence, c'est une panne, qui a sa propre voie de signalement.
|
|
17
|
+
*
|
|
18
|
+
* @param connector - nom du connecteur à interroger.
|
|
19
|
+
* @returns les écarts nommés, ou `null` (rien à dire, ou rien d'interrogeable).
|
|
20
|
+
*/
|
|
21
|
+
export declare function gapAgainstDeclared(connector: string): Promise<ISchemaComparison | null>;
|
|
22
|
+
/**
|
|
23
|
+
* La comparaison BRUTE — ce que la base porte face au code, écart ou non.
|
|
24
|
+
*
|
|
25
|
+
* Séparée de {@link gapAgainstDeclared}, qui ne rend que les écarts : il existe
|
|
26
|
+
* une question à laquelle « aucun écart » est une réponse pleine, et non un
|
|
27
|
+
* silence. Celle-ci : *la base porte-t-elle DÉJÀ les tables que je m'apprête à
|
|
28
|
+
* créer ?* Une base parfaitement conforme y répond « oui », et c'est justement
|
|
29
|
+
* le cas où il ne faut pas écrire un `CREATE TABLE`.
|
|
30
|
+
*
|
|
31
|
+
* Ne modifie jamais rien, et ne jette jamais : une base muette n'est pas une
|
|
32
|
+
* conformité, c'est une absence de réponse — rendue `null` pour que personne
|
|
33
|
+
* ne conclue à sa place.
|
|
34
|
+
*
|
|
35
|
+
* @param connector - nom du connecteur à interroger.
|
|
36
|
+
* @returns la comparaison, ou `null` si rien n'était interrogeable.
|
|
37
|
+
*/
|
|
38
|
+
export declare function comparisonAgainstDeclared(connector: string): Promise<ISchemaComparison | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Ce qui diverge, NOMMÉ — ou `null` quand il n'y a rien à dire.
|
|
41
|
+
*
|
|
42
|
+
* Producteur UNIQUE de la troisième source : le verdict, la phrase française,
|
|
43
|
+
* la charge utile `--json` et la sonde de disponibilité lisent tous ce même
|
|
44
|
+
* retour. Rendre un booléen ici et recalculer le détail ailleurs ferait deux
|
|
45
|
+
* lectures de la base pour une seule question, et deux réponses qui finiraient
|
|
46
|
+
* par se contredire.
|
|
47
|
+
*
|
|
48
|
+
* Répond `null` sans rien interroger dans tous les cas où la réponse ne
|
|
49
|
+
* changerait rien : plan déjà porteur d'un verdict, connecteur absent du
|
|
50
|
+
* registre, connecteur non connecté, ORM d'une autre nature (mongoose n'a pas
|
|
51
|
+
* de schéma déclaré à comparer). Répond `null` aussi quand la comparaison a eu
|
|
52
|
+
* lieu et n'a rien trouvé — l'absence d'écart ne garde pas d'objet vide en
|
|
53
|
+
* mémoire, et l'appelant n'a qu'un test à écrire.
|
|
54
|
+
*
|
|
55
|
+
* **Ne modifie jamais rien** — le rattrapage additif est le travail du mode de
|
|
56
|
+
* schéma dérivé, au démarrage, et de lui seul.
|
|
57
|
+
*
|
|
58
|
+
* @param plan - plan calculé par l'applicateur, en lecture seule.
|
|
59
|
+
* @returns les écarts nommés, ou `null` s'il n'y en a pas à publier.
|
|
60
|
+
*/
|
|
61
|
+
export declare function describeDivergence(plan: IMigrationPlan): Promise<ISchemaComparison | null>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SqlDialect } from "../../../config/config.js";
|
|
2
|
+
import type { IMigrationDriver } from "../types.js";
|
|
3
|
+
export { SqliteMigrationDriver } from "./sqliteDriver.js";
|
|
4
|
+
export { PostgresMigrationDriver, PG_LOCK_KEY } from "./postgresDriver.js";
|
|
5
|
+
export { MysqlMigrationDriver, MYSQL_LOCK_NAME_SQL, MYSQL_LOCK_PREFIX, } from "./mysqlDriver.js";
|
|
6
|
+
/** Cible de connexion de l'applicateur. */
|
|
7
|
+
export interface IMigrationTarget {
|
|
8
|
+
/** Dialecte du connecteur. */
|
|
9
|
+
dialect: SqlDialect;
|
|
10
|
+
/** Fichier SQLite (dialecte `sqlite`). */
|
|
11
|
+
filename?: string;
|
|
12
|
+
/** URL de connexion DIRECTE (dialectes `postgres` et `mysql`). */
|
|
13
|
+
url?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Ouvre le pilote à connexion unique du dialecte demandé.
|
|
17
|
+
*
|
|
18
|
+
* L'URL doit être une connexion **directe** au serveur : un répartiteur de
|
|
19
|
+
* connexions en mode transaction casse les verrous consultatifs de session, et
|
|
20
|
+
* le verrou de l'applicateur en est un.
|
|
21
|
+
*
|
|
22
|
+
* @param target - dialecte et coordonnées de la base.
|
|
23
|
+
* @returns le pilote, déjà connecté.
|
|
24
|
+
* @throws Error si les coordonnées manquent pour ce dialecte.
|
|
25
|
+
*/
|
|
26
|
+
export declare function openMigrationDriver(target: IMigrationTarget): Promise<IMigrationDriver>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { IMigrationDriver } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Préfixe de l'identité du verrou MySQL — **contrat inter-versions**.
|
|
4
|
+
*
|
|
5
|
+
* Au même titre que le nom de la table d'historique : deux versions du
|
|
6
|
+
* framework qui ne s'excluent plus, c'est pendant un déploiement que ça se
|
|
7
|
+
* paie.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MYSQL_LOCK_PREFIX = "nodefony:migrations:";
|
|
10
|
+
/**
|
|
11
|
+
* Expression SQL de l'identité du verrou — évaluée par le SERVEUR.
|
|
12
|
+
*
|
|
13
|
+
* **Pourquoi qualifier par `DATABASE()`** : `GET_LOCK` est global au SERVEUR,
|
|
14
|
+
* pas à la base. Sans cette qualification, deux applications sans aucun rapport
|
|
15
|
+
* hébergées sur la même instance se sérialiseraient — en silence, ce qui est le
|
|
16
|
+
* pire des symptômes.
|
|
17
|
+
*
|
|
18
|
+
* **Pourquoi un repli haché** : MySQL borne le nom d'un verrou à 64 caractères
|
|
19
|
+
* (MariaDB est plus permissif, mais c'est la contrainte la plus stricte qui
|
|
20
|
+
* fait règle). Le préfixe en consomme 20 ; au-delà de 44 caractères de nom de
|
|
21
|
+
* base, l'appel échouerait — et l'échec d'un verrou est un blocage total, avec
|
|
22
|
+
* un message que rien ne rattache au nom de la base. Le repli reste
|
|
23
|
+
* DÉTERMINISTE (fonction du seul nom de base), donc deux versions du framework
|
|
24
|
+
* calculent toujours la même identité : le contrat tient.
|
|
25
|
+
*/
|
|
26
|
+
export declare const MYSQL_LOCK_NAME_SQL: string;
|
|
27
|
+
/**
|
|
28
|
+
* Pilote MySQL / MariaDB de l'applicateur — **une seule connexion**.
|
|
29
|
+
*
|
|
30
|
+
* `GET_LOCK` est un verrou de SESSION : un pool le rendrait inopérant. Il
|
|
31
|
+
* s'auto-libère à la mort de la connexion — aucun verrou zombie.
|
|
32
|
+
*
|
|
33
|
+
* 🔴 **Le DDL n'est PAS transactionnel ici** : un `CREATE TABLE` valide
|
|
34
|
+
* implicitement la transaction en cours. Un échec à mi-course laisse donc la
|
|
35
|
+
* base dans un état partiel, avec un marqueur d'échec persistant — et c'est
|
|
36
|
+
* exactement pourquoi il ne doit JAMAIS y avoir de reprise aveugle : c'est la
|
|
37
|
+
* réparation, après inspection humaine, qui tranche.
|
|
38
|
+
*/
|
|
39
|
+
export declare class MysqlMigrationDriver implements IMigrationDriver {
|
|
40
|
+
#private;
|
|
41
|
+
readonly dialect: "mysql";
|
|
42
|
+
readonly transactionalDdl = false;
|
|
43
|
+
/**
|
|
44
|
+
* @param url - URL de connexion DIRECTE au serveur.
|
|
45
|
+
*/
|
|
46
|
+
constructor(url: string);
|
|
47
|
+
/**
|
|
48
|
+
* Ouvre la connexion dédiée.
|
|
49
|
+
*
|
|
50
|
+
* @throws Error si le pilote `mysql2` n'est pas installé.
|
|
51
|
+
*/
|
|
52
|
+
connect(): Promise<void>;
|
|
53
|
+
/** Connexion ouverte, ou une erreur qui dit quoi faire. */
|
|
54
|
+
/** Motif de la perte de connexion, si elle a été constatée. */
|
|
55
|
+
get lostReason(): string | null;
|
|
56
|
+
/** @inheritdoc */
|
|
57
|
+
exec(sql: string): Promise<void>;
|
|
58
|
+
/** @inheritdoc */
|
|
59
|
+
query<T extends Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<T[]>;
|
|
60
|
+
/** @inheritdoc */
|
|
61
|
+
sameColumnName(declared: string, actual: string): boolean;
|
|
62
|
+
/** @inheritdoc */
|
|
63
|
+
tableExists(table: string): Promise<boolean>;
|
|
64
|
+
/** @inheritdoc */
|
|
65
|
+
columnsOf(table: string): Promise<string[]>;
|
|
66
|
+
/** @inheritdoc */
|
|
67
|
+
begin(): Promise<void>;
|
|
68
|
+
/** @inheritdoc */
|
|
69
|
+
commit(): Promise<void>;
|
|
70
|
+
/** @inheritdoc */
|
|
71
|
+
rollback(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Prend le verrou nommé, avec le délai natif de `GET_LOCK`.
|
|
74
|
+
*
|
|
75
|
+
* @param timeoutMs - délai maximal d'attente.
|
|
76
|
+
* @throws Error si le verrou n'est pas obtenu dans le délai.
|
|
77
|
+
*/
|
|
78
|
+
lock(timeoutMs: number): Promise<void>;
|
|
79
|
+
/** @inheritdoc */
|
|
80
|
+
unlock(): Promise<void>;
|
|
81
|
+
/** @inheritdoc */
|
|
82
|
+
close(): Promise<void>;
|
|
83
|
+
}
|