@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.
Files changed (143) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +162 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
  5. package/dist/index.js +105 -0
  6. package/dist/nodefony/command/migrateShared.js +247 -0
  7. package/dist/nodefony/command/orm-generate.js +356 -0
  8. package/dist/nodefony/command/orm-migrate-baseline.js +208 -0
  9. package/dist/nodefony/command/orm-migrate-repair.js +114 -0
  10. package/dist/nodefony/command/orm-migrate-status.js +67 -0
  11. package/dist/nodefony/command/orm-migrate.js +141 -0
  12. package/dist/nodefony/command/orm-reset.js +166 -0
  13. package/dist/nodefony/config/config.js +107 -0
  14. package/dist/nodefony/config/defineModuleConfig.js +63 -0
  15. package/dist/nodefony/entity/auditEventEntity.js +93 -0
  16. package/dist/nodefony/entity/colKit.js +260 -0
  17. package/dist/nodefony/entity/idempotencyEntity.js +74 -0
  18. package/dist/nodefony/entity/sessionEntity.js +75 -0
  19. package/dist/nodefony/entity/tokenEntity.js +198 -0
  20. package/dist/nodefony/entity/totpSecretEntity.js +98 -0
  21. package/dist/nodefony/entity/userTable.js +141 -0
  22. package/dist/nodefony/entity/webAuthnCredentialEntity.js +114 -0
  23. package/dist/nodefony/entity/webhookEndpointEntity.js +106 -0
  24. package/dist/nodefony/interfaces/IDrizzleConfig.js +1 -0
  25. package/dist/nodefony/interfaces/index.js +1 -0
  26. package/dist/nodefony/migrations-schema/mysql.js +48 -0
  27. package/dist/nodefony/migrations-schema/postgres.js +48 -0
  28. package/dist/nodefony/migrations-schema/sqlite.js +48 -0
  29. package/dist/nodefony/registerStores.js +218 -0
  30. package/dist/nodefony/service/DrizzleService.js +282 -0
  31. package/dist/nodefony/src/DrizzleAuditStore.js +203 -0
  32. package/dist/nodefony/src/DrizzleIdempotencyStore.js +278 -0
  33. package/dist/nodefony/src/DrizzleTokenStore.js +244 -0
  34. package/dist/nodefony/src/DrizzleTotpSecretStore.js +151 -0
  35. package/dist/nodefony/src/DrizzleUserRepository.js +217 -0
  36. package/dist/nodefony/src/DrizzleWebAuthnCredentialStore.js +159 -0
  37. package/dist/nodefony/src/DrizzleWebhookStore.js +169 -0
  38. package/dist/nodefony/src/SessionStorage.js +259 -0
  39. package/dist/nodefony/src/connectorTarget.js +59 -0
  40. package/dist/nodefony/src/likeSql.js +50 -0
  41. package/dist/nodefony/src/migrator/DrizzleMigrator.js +775 -0
  42. package/dist/nodefony/src/migrator/adopt.js +553 -0
  43. package/dist/nodefony/src/migrator/appSchema.js +414 -0
  44. package/dist/nodefony/src/migrator/catalog.js +76 -0
  45. package/dist/nodefony/src/migrator/destructive.js +213 -0
  46. package/dist/nodefony/src/migrator/divergence.js +84 -0
  47. package/dist/nodefony/src/migrator/drivers/index.js +39 -0
  48. package/dist/nodefony/src/migrator/drivers/mysqlDriver.js +147 -0
  49. package/dist/nodefony/src/migrator/drivers/postgresDriver.js +151 -0
  50. package/dist/nodefony/src/migrator/drivers/sqliteDriver.js +121 -0
  51. package/dist/nodefony/src/migrator/explain.js +565 -0
  52. package/dist/nodefony/src/migrator/hash.js +47 -0
  53. package/dist/nodefony/src/migrator/history.js +219 -0
  54. package/dist/nodefony/src/migrator/index.js +16 -0
  55. package/dist/nodefony/src/migrator/kit.js +296 -0
  56. package/dist/nodefony/src/migrator/name.js +68 -0
  57. package/dist/nodefony/src/migrator/paths.js +88 -0
  58. package/dist/nodefony/src/migrator/refusals.js +143 -0
  59. package/dist/nodefony/src/migrator/resolve.js +281 -0
  60. package/dist/nodefony/src/migrator/schemaDiff.js +86 -0
  61. package/dist/nodefony/src/migrator/sources.js +419 -0
  62. package/dist/nodefony/src/migrator/status.js +231 -0
  63. package/dist/nodefony/src/migrator/types.js +91 -0
  64. package/dist/nodefony/src/orm-core/DrizzleOrm.js +1154 -0
  65. package/dist/nodefony/src/orm-core/DrizzleRepository.js +610 -0
  66. package/dist/nodefony/src/orm-core/DrizzleTransaction.js +106 -0
  67. package/dist/nodefony/src/orm-core/index.js +4 -0
  68. package/dist/nodefony/src/queryKit.js +318 -0
  69. package/dist/nodefony/src/safeTarget.js +55 -0
  70. package/dist/types/index.d.ts +76 -0
  71. package/dist/types/nodefony/command/migrateShared.d.ts +137 -0
  72. package/dist/types/nodefony/command/orm-generate.d.ts +53 -0
  73. package/dist/types/nodefony/command/orm-migrate-baseline.d.ts +87 -0
  74. package/dist/types/nodefony/command/orm-migrate-repair.d.ts +66 -0
  75. package/dist/types/nodefony/command/orm-migrate-status.d.ts +38 -0
  76. package/dist/types/nodefony/command/orm-migrate.d.ts +65 -0
  77. package/dist/types/nodefony/command/orm-reset.d.ts +55 -0
  78. package/dist/types/nodefony/config/config.d.ts +110 -0
  79. package/dist/types/nodefony/config/defineModuleConfig.d.ts +24 -0
  80. package/dist/types/nodefony/entity/auditEventEntity.d.ts +56 -0
  81. package/dist/types/nodefony/entity/colKit.d.ts +130 -0
  82. package/dist/types/nodefony/entity/idempotencyEntity.d.ts +52 -0
  83. package/dist/types/nodefony/entity/sessionEntity.d.ts +50 -0
  84. package/dist/types/nodefony/entity/tokenEntity.d.ts +53 -0
  85. package/dist/types/nodefony/entity/totpSecretEntity.d.ts +61 -0
  86. package/dist/types/nodefony/entity/userTable.d.ts +65 -0
  87. package/dist/types/nodefony/entity/webAuthnCredentialEntity.d.ts +57 -0
  88. package/dist/types/nodefony/entity/webhookEndpointEntity.d.ts +58 -0
  89. package/dist/types/nodefony/interfaces/IDrizzleConfig.d.ts +17 -0
  90. package/dist/types/nodefony/interfaces/index.d.ts +1 -0
  91. package/dist/types/nodefony/migrations-schema/mysql.d.ts +9 -0
  92. package/dist/types/nodefony/migrations-schema/postgres.d.ts +9 -0
  93. package/dist/types/nodefony/migrations-schema/sqlite.d.ts +9 -0
  94. package/dist/types/nodefony/registerStores.d.ts +52 -0
  95. package/dist/types/nodefony/service/DrizzleService.d.ts +27 -0
  96. package/dist/types/nodefony/src/DrizzleAuditStore.d.ts +65 -0
  97. package/dist/types/nodefony/src/DrizzleIdempotencyStore.d.ts +129 -0
  98. package/dist/types/nodefony/src/DrizzleTokenStore.d.ts +146 -0
  99. package/dist/types/nodefony/src/DrizzleTotpSecretStore.d.ts +60 -0
  100. package/dist/types/nodefony/src/DrizzleUserRepository.d.ts +67 -0
  101. package/dist/types/nodefony/src/DrizzleWebAuthnCredentialStore.d.ts +62 -0
  102. package/dist/types/nodefony/src/DrizzleWebhookStore.d.ts +79 -0
  103. package/dist/types/nodefony/src/SessionStorage.d.ts +72 -0
  104. package/dist/types/nodefony/src/connectorTarget.d.ts +48 -0
  105. package/dist/types/nodefony/src/likeSql.d.ts +29 -0
  106. package/dist/types/nodefony/src/migrator/DrizzleMigrator.d.ts +94 -0
  107. package/dist/types/nodefony/src/migrator/adopt.d.ts +280 -0
  108. package/dist/types/nodefony/src/migrator/appSchema.d.ts +223 -0
  109. package/dist/types/nodefony/src/migrator/catalog.d.ts +100 -0
  110. package/dist/types/nodefony/src/migrator/destructive.d.ts +123 -0
  111. package/dist/types/nodefony/src/migrator/divergence.d.ts +61 -0
  112. package/dist/types/nodefony/src/migrator/drivers/index.d.ts +26 -0
  113. package/dist/types/nodefony/src/migrator/drivers/mysqlDriver.d.ts +83 -0
  114. package/dist/types/nodefony/src/migrator/drivers/postgresDriver.d.ts +81 -0
  115. package/dist/types/nodefony/src/migrator/drivers/sqliteDriver.d.ts +53 -0
  116. package/dist/types/nodefony/src/migrator/explain.d.ts +424 -0
  117. package/dist/types/nodefony/src/migrator/hash.d.ts +39 -0
  118. package/dist/types/nodefony/src/migrator/history.d.ts +139 -0
  119. package/dist/types/nodefony/src/migrator/index.d.ts +20 -0
  120. package/dist/types/nodefony/src/migrator/kit.d.ts +141 -0
  121. package/dist/types/nodefony/src/migrator/name.d.ts +52 -0
  122. package/dist/types/nodefony/src/migrator/paths.d.ts +54 -0
  123. package/dist/types/nodefony/src/migrator/refusals.d.ts +170 -0
  124. package/dist/types/nodefony/src/migrator/resolve.d.ts +201 -0
  125. package/dist/types/nodefony/src/migrator/schemaDiff.d.ts +112 -0
  126. package/dist/types/nodefony/src/migrator/sources.d.ts +119 -0
  127. package/dist/types/nodefony/src/migrator/status.d.ts +132 -0
  128. package/dist/types/nodefony/src/migrator/types.d.ts +273 -0
  129. package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +241 -0
  130. package/dist/types/nodefony/src/orm-core/DrizzleRepository.d.ts +98 -0
  131. package/dist/types/nodefony/src/orm-core/DrizzleTransaction.d.ts +71 -0
  132. package/dist/types/nodefony/src/orm-core/index.d.ts +11 -0
  133. package/dist/types/nodefony/src/queryKit.d.ts +136 -0
  134. package/dist/types/nodefony/src/safeTarget.d.ts +42 -0
  135. package/docs/index.md +954 -0
  136. package/docs/migrations.md +691 -0
  137. package/migrations/mysql/0000_framework_init.sql +137 -0
  138. package/migrations/mysql/meta/_journal.json +13 -0
  139. package/migrations/postgres/0000_framework_init.sql +128 -0
  140. package/migrations/postgres/meta/_journal.json +13 -0
  141. package/migrations/sqlite/0000_framework_init.sql +127 -0
  142. package/migrations/sqlite/meta/_journal.json +13 -0
  143. package/package.json +126 -0
@@ -0,0 +1,137 @@
1
+ import { Command, type CliKernel } from "nodefony";
2
+ import type { IDrizzleConfig } from "../interfaces/IDrizzleConfig.js";
3
+ import type { DrizzleMigrator } from "../src/migrator/DrizzleMigrator.js";
4
+ import { MigrationVerdictError } from "../src/migrator/types.js";
5
+ import type { IMigrationAction } from "../src/migrator/types.js";
6
+ import { type IMigrationReport, type IStyle } from "../src/migrator/explain.js";
7
+ import type { IMigrationPlan } from "../src/migrator/types.js";
8
+ import { type IConnectorResolution } from "../src/migrator/resolve.js";
9
+ import { type CommandFailureCode, type IDiscoveryFacts } from "../src/migrator/refusals.js";
10
+ export type { ICommandFailure } from "../src/migrator/refusals.js";
11
+ /** Options communes à toutes les commandes de migration. */
12
+ export interface IMigrateSharedOptions {
13
+ connector?: string;
14
+ json?: boolean;
15
+ }
16
+ /**
17
+ * Classe de base des commandes `orm:*`.
18
+ *
19
+ * Elle n'impose rien du verbe : chaque commande écrit son `generate`. Elle
20
+ * impose la FORME de ce qui sort, parce que c'est cette forme qui fait la
21
+ * différence entre un outil qu'on sait utiliser et un outil qu'on subit.
22
+ */
23
+ export declare abstract class OrmMigrateCommand extends Command {
24
+ /**
25
+ * Faut-il colorer la sortie ?
26
+ *
27
+ * 🔴 La question n'est PAS « est-ce un terminal ? », et la confondre avec ça
28
+ * a deux conséquences que personne ne signale :
29
+ *
30
+ * - **`NO_COLOR` est ignoré.** C'est une convention publique
31
+ * (no-color.org) qu'un utilisateur pose une fois pour toutes ses commandes ;
32
+ * la manquer rend une sortie illisible sur un terminal à palette
33
+ * inhabituelle, et le framework passe pour cassé.
34
+ * - **`FORCE_COLOR` est ignoré.** Sans lui, aucune sortie colorée n'est
35
+ * CAPTURABLE : ni dans un fichier, ni dans une passe d'intégration continue
36
+ * qui sait rendre les couleurs, ni dans un rapport de validation. On ne peut
37
+ * alors pas relire ce que l'exploitant voit vraiment.
38
+ *
39
+ * La règle vit au CŒUR (`resolveColorEnabled`), qui sert déjà les journaux :
40
+ * la réécrire ici en ferait une SECONDE implémentation, et les deux
41
+ * divergeraient — le journal obéirait à `NO_COLOR`, la commande non.
42
+ */
43
+ protected get tty(): boolean;
44
+ /** Mise en forme, neutralisée hors terminal. */
45
+ protected get style(): IStyle;
46
+ /**
47
+ * Configuration validée du module qui porte les connecteurs SQL.
48
+ *
49
+ * @returns la configuration, ou `null` si le module n'est pas chargé.
50
+ */
51
+ protected drizzleConfig(): IDrizzleConfig | null;
52
+ /**
53
+ * Écrit une charge utile sur la sortie standard et pose le code de sortie.
54
+ *
55
+ * `process.exitCode` et jamais `process.exit()` : couper le processus laisse
56
+ * la sortie standard non vidée, et un `| jq` reçoit alors un objet tronqué —
57
+ * un échec qui ressemble à un défaut de la commande.
58
+ *
59
+ * @param payload - l'objet, en mode machine.
60
+ * @param human - le texte, en mode humain.
61
+ * @param exitCode - `0`, `1` ou `2`.
62
+ * @param json - la commande a-t-elle reçu `--json` ?
63
+ */
64
+ protected respond(payload: unknown, human: string, exitCode: number, json: boolean | undefined): void;
65
+ /**
66
+ * Arrête la commande en disant le fait, la cause et le geste.
67
+ *
68
+ * @param connector - connecteur concerné (ou le nom demandé).
69
+ * @param code - code d'arrêt stable, lu par les machines.
70
+ * @param summary - le fait, en français.
71
+ * @param meaning - ce que ça veut dire.
72
+ * @param actions - les commandes exactes à copier.
73
+ * @param json - la commande a-t-elle reçu `--json` ?
74
+ * @param exitCode - `2` par défaut : la commande n'a pas pu travailler.
75
+ * @param discovery - ce que la découverte des entités a vu, quand le refus
76
+ * peut avoir pour cause un schéma déclaré amputé.
77
+ */
78
+ protected fail(connector: string, code: CommandFailureCode | MigrationVerdictError["verdict"]["code"], summary: string, meaning: string, actions: IMigrationAction[], json: boolean | undefined, exitCode?: 1 | 2, discovery?: IDiscoveryFacts): void;
79
+ /**
80
+ * Résout le connecteur demandé, ou ARRÊTE la commande en le disant.
81
+ *
82
+ * Les **trois** réponses sont un contrat public, et elles restent distinctes :
83
+ * un connecteur inconnu, un connecteur porté par un ORM sans migrations, un
84
+ * connecteur prêt. Le jour où un second ORM apporte ses propres migrations,
85
+ * seule la deuxième cesse de sortir pour ses connecteurs — répondre « ne
86
+ * porte pas de migrations » à un connecteur qui en porte serait un message
87
+ * FAUX, et un message faux publié est appris par les scripts qui le lisent.
88
+ *
89
+ * @param opts - options de la commande (connecteur, mode machine).
90
+ * @param allowMigrateUrl - la commande honore-t-elle {@link MIGRATE_URL_ENV} ?
91
+ * @returns le connecteur prêt, ou `null` si la commande est déjà arrêtée.
92
+ */
93
+ protected resolveOrFail(opts: IMigrateSharedOptions, allowMigrateUrl: boolean): {
94
+ resolution: Extract<IConnectorResolution, {
95
+ kind: "ready";
96
+ }>;
97
+ config: IDrizzleConfig;
98
+ } | null;
99
+ /**
100
+ * Construit l'applicateur d'un connecteur résolu.
101
+ *
102
+ * @param resolution - connecteur prêt.
103
+ * @param config - configuration validée.
104
+ * @returns l'applicateur, sources chargées.
105
+ */
106
+ protected migrator(resolution: Extract<IConnectorResolution, {
107
+ kind: "ready";
108
+ }>, config: IDrizzleConfig): Promise<DrizzleMigrator>;
109
+ /**
110
+ * Compose la charge utile d'un état.
111
+ *
112
+ * L'assemblage lui-même vit dans `migrator/status.ts` : le plan
113
+ * d'administration publie le MÊME objet, et deux assemblages divergeaient
114
+ * d'un champ à l'autre sans qu'aucun test ne le voie.
115
+ *
116
+ * @param plan - plan calculé par l'applicateur, en lecture seule.
117
+ * @param resolution - connecteur prêt (porte le mode de schéma effectif).
118
+ * @param config - configuration validée du module.
119
+ * @returns la charge utile, prête pour `--json` comme pour l'écran.
120
+ */
121
+ protected report(plan: IMigrationPlan, resolution: Extract<IConnectorResolution, {
122
+ kind: "ready";
123
+ }>, config: IDrizzleConfig): Promise<IMigrationReport>;
124
+ protected failFrom(e: unknown, connector: string, json: boolean | undefined, ddl?: string): void;
125
+ /**
126
+ * Écrit un état lu et pose son code de sortie.
127
+ *
128
+ * @param report - charge utile complète.
129
+ * @param human - rendu humain déjà composé.
130
+ * @param json - la commande a-t-elle reçu `--json` ?
131
+ */
132
+ protected emitReport(report: IMigrationReport, human: string, json: boolean | undefined): void;
133
+ /** Déclare les deux options que toutes les commandes de migration portent. */
134
+ protected addSharedOptions(): void;
135
+ /** Le CLI courant, typé — utilisé pour les réglages de boot silencieux. */
136
+ protected get cliKernel(): CliKernel;
137
+ }
@@ -0,0 +1,53 @@
1
+ import { type CliKernel } from "nodefony";
2
+ import { OrmMigrateCommand, type IMigrateSharedOptions } from "./migrateShared.js";
3
+ /** Options propres à `orm:generate`. */
4
+ interface IGenerateOptions extends IMigrateSharedOptions {
5
+ name?: string;
6
+ custom?: boolean;
7
+ allowDestructive?: boolean;
8
+ }
9
+ /**
10
+ * `nodefony orm:generate` — écrit la migration qui fera passer la base au
11
+ * schéma que décrivent les entités de l'application.
12
+ *
13
+ * ## Ce que l'utilisateur n'a pas à connaître
14
+ *
15
+ * Rien de `drizzle-kit`. Ni son installation, ni sa configuration par dialecte,
16
+ * ni la notion de « schéma matérialisé », ni son dossier de sortie, ni le format
17
+ * de son journal. Il tape un verbe et un nom. La commande découvre les fichiers
18
+ * d'entités par la convention du générateur, écrit ce qu'il faut dans un dossier
19
+ * de travail qu'elle efface ensuite, et pilote l'outil — dont elle EXIGE la
20
+ * preuve qu'il a travaillé, parce qu'il rend 0 même quand il échoue.
21
+ *
22
+ * ## Ce qu'elle refuse, et pourquoi elle le refuse plutôt que de continuer
23
+ *
24
+ * Une migration est **immuable une fois appliquée**. Une migration amputée n'est
25
+ * pas un désagrément qu'on rattrape à la génération suivante : elle grave dans
26
+ * l'historique une table qui n'existera jamais, et toute base qui l'a reçue
27
+ * restera incomplète. Trois situations valent donc un arrêt qui NOMME :
28
+ *
29
+ * - un fichier d'entité qui ne s'importe pas (le schéma serait incomplet) ;
30
+ * - une entité enregistrée qu'aucun fichier ne fournit (idem, et c'est le
31
+ * contrôle que le registre est le seul à pouvoir faire) ;
32
+ * - un fichier de l'application qui fournit une table du FRAMEWORK — la
33
+ * migration porterait un second `CREATE TABLE` de cette table, qui échoue sur
34
+ * toute base déjà migrée, c'est-à-dire en production et nulle part ailleurs.
35
+ *
36
+ * @example Le cas courant
37
+ * ```bash
38
+ * nodefony orm:generate --name ajout_du_titre
39
+ * nodefony orm:migrate # ou : le travail de déploiement
40
+ * ```
41
+ *
42
+ * @example Ce que le modèle déclaratif ne peut pas déduire
43
+ * ```bash
44
+ * nodefony orm:generate --custom --name vue_des_ventes
45
+ * # → un fichier SQL vide, déjà inscrit au journal : à écrire à la main.
46
+ * ```
47
+ */
48
+ declare class OrmGenerate extends OrmMigrateCommand {
49
+ #private;
50
+ constructor(cli: CliKernel);
51
+ generate(opts?: IGenerateOptions): Promise<this>;
52
+ }
53
+ export default OrmGenerate;
@@ -0,0 +1,87 @@
1
+ import type { CliKernel } from "nodefony";
2
+ import { MIGRATION_FORMAT_VERSION, action } from "../src/migrator/explain.js";
3
+ import { OrmMigrateCommand, type IMigrateSharedOptions } from "./migrateShared.js";
4
+ /** Options propres à l'adoption d'une base existante. */
5
+ interface IBaselineOptions extends IMigrateSharedOptions {
6
+ upTo?: string;
7
+ fromDatabase?: boolean;
8
+ name?: string;
9
+ }
10
+ /**
11
+ * `nodefony orm:migrate:baseline` — déclare une base existante « à niveau »,
12
+ * SANS exécuter une seule instruction de schéma.
13
+ *
14
+ * ## Quand on en a besoin
15
+ *
16
+ * La base contient déjà les tables — parce qu'on branche Nodefony sur une base
17
+ * qui existait avant, ou parce que le schéma a été créé autrement (mode `auto`
18
+ * en développement) — mais aucune migration n'y est enregistrée. Appliquer les
19
+ * migrations dans cet état exécuterait des créations de tables qui existent
20
+ * déjà, et échouerait.
21
+ *
22
+ * ## Pourquoi ce n'est PAS automatique
23
+ *
24
+ * Adopter tout seul retirerait le filet qui protège de la pire erreur : se
25
+ * tromper de base. Une adresse de base héritée d'un autre environnement, et
26
+ * l'outil déclarerait « à niveau » une base qui n'a rien à voir — puis
27
+ * appliquerait dessus les migrations suivantes. La documentation de Flyway
28
+ * elle-même met en garde contre son propre mode automatique pour cette raison.
29
+ *
30
+ * **Vérifie que c'est la bonne base avant de taper cette commande.** Elle ne
31
+ * modifie pas le schéma, mais elle change ce que le framework CROIT du schéma —
32
+ * et tout le reste en découle.
33
+ *
34
+ * ## Ce qu'elle fait exactement
35
+ *
36
+ * Elle inscrit dans l'historique, comme appliquées avec succès et une durée
37
+ * nulle, les migrations qui n'y sont pas encore. Rejouée, elle n'inscrit que ce
38
+ * qui manque : elle est donc sûre à relancer.
39
+ *
40
+ * ## Ce qu'elle REFUSE
41
+ *
42
+ * Adopter, c'est affirmer que la base est à l'état que décrivent ces
43
+ * migrations. Quand la base s'écarte du schéma déclaré, l'affirmation serait
44
+ * fausse — et une affirmation fausse gravée dans l'historique n'est rattrapable
45
+ * par aucune commande. La commande constate donc la base AVANT d'écrire, et
46
+ * refuse (`NF_MIGRATE_BASELINE_AMBIGUOUS`) en nommant ce qui manque.
47
+ *
48
+ * Trois cas y échappent, et pour la même raison — la garde n'a plus rien à
49
+ * deviner : `--up-to`, qui borne l'adoption explicitement ; `--from-database`,
50
+ * dont la référence DÉCRIT la base et rend donc l'affirmation vraie par
51
+ * construction ; et une cible détournée par `NF_MIGRATE_DATABASE_URL`, où la
52
+ * comparaison porterait sur la base de la configuration et non sur celle qu'on
53
+ * migre.
54
+ *
55
+ * ## Une base qui n'a JAMAIS eu de migrations — `--from-database`
56
+ *
57
+ * Adopter suppose des fichiers à inscrire. Une application passée du mode
58
+ * dérivé, où le démarrage fabrique le schéma, au mode de production n'en a
59
+ * aucun : sa base porte tout, son dossier de migrations est vide. Sans
60
+ * référence, la première génération décrirait ce que le CODE déclare — un
61
+ * schéma que cette base n'a peut-être jamais eu, si quelqu'un vient de changer
62
+ * une entité — et l'inscrire graverait une affirmation fausse.
63
+ *
64
+ * `--from-database` LIT le schéma de la base et en écrit la migration de
65
+ * référence, puis l'inscrit. Rien n'est exécuté sur la base. La suite redevient
66
+ * ordinaire : le champ ajouté produit un `ALTER TABLE`.
67
+ *
68
+ * @example Adopter jusqu'à une migration précise
69
+ * ```bash
70
+ * nodefony orm:migrate:baseline --up-to 0003_audit_severity
71
+ * nodefony orm:migrate # applique la suite normalement
72
+ * ```
73
+ *
74
+ * @example Reprendre une base qui existait avant toute migration
75
+ * ```bash
76
+ * nodefony orm:migrate:baseline --from-database # la référence est LUE sur la base
77
+ * nodefony orm:generate --name ajout_du_slug # produit un ALTER, plus un CREATE
78
+ * nodefony orm:migrate # applique
79
+ * ```
80
+ */
81
+ declare class OrmMigrateBaseline extends OrmMigrateCommand {
82
+ #private;
83
+ constructor(cli: CliKernel);
84
+ generate(opts?: IBaselineOptions): Promise<this>;
85
+ }
86
+ export default OrmMigrateBaseline;
87
+ export { MIGRATION_FORMAT_VERSION, action };
@@ -0,0 +1,66 @@
1
+ import type { CliKernel } from "nodefony";
2
+ import { OrmMigrateCommand, type IMigrateSharedOptions } from "./migrateShared.js";
3
+ /** Options propres à la réparation de l'historique. */
4
+ interface IRepairOptions extends IMigrateSharedOptions {
5
+ source?: string;
6
+ updateHashes?: boolean;
7
+ forget?: string[];
8
+ }
9
+ /**
10
+ * `nodefony orm:migrate:repair` — lève les marqueurs d'échec, **après**
11
+ * inspection humaine.
12
+ *
13
+ * ## Ce que « réparer » veut dire ici, et ce que ça ne veut pas dire
14
+ *
15
+ * Cette commande **ne répare pas la base**. Elle efface la trace d'une
16
+ * migration qui n'a pas abouti, pour que l'applicateur accepte de reprendre.
17
+ * C'est une déclaration : « j'ai regardé la base, elle est dans l'état que je
18
+ * crois ».
19
+ *
20
+ * Pourquoi ce n'est pas automatique : quand une migration s'arrête en cours de
21
+ * route, l'état laissé dépend de la base. PostgreSQL et SQLite annulent la
22
+ * migration fautive entière — l'état est net. MySQL valide chaque instruction
23
+ * de schéma au fur et à mesure, sans retour possible : la moitié des
24
+ * changements peut être en place. Aucun programme ne peut décider à la place
25
+ * d'un humain si cette moitié est acceptable.
26
+ *
27
+ * **L'ordre des gestes, et il ne s'inverse pas :**
28
+ *
29
+ * 1. `nodefony orm:migrate:status --json` — voir ce qui a échoué et pourquoi ;
30
+ * 2. regarder la base elle-même, et la remettre d'aplomb si besoin ;
31
+ * 3. `nodefony orm:migrate:repair` — lever le marqueur ;
32
+ * 4. `nodefony orm:migrate` — reprendre.
33
+ *
34
+ * ## `--update-hashes` : à ne taper que sur instruction d'un message
35
+ *
36
+ * Ré-aligner les empreintes déclare que les fichiers modifiés APRÈS avoir été
37
+ * appliqués sont réputés conformes. C'est presque toujours faux : les autres
38
+ * bases ont reçu l'ancienne version et ne recevront jamais la nouvelle. Le
39
+ * geste normal face à un fichier modifié est de le restaurer
40
+ * (`git checkout -- migrations/`) et d'écrire une NOUVELLE migration.
41
+ *
42
+ * L'option existe pour le cas où l'on sait que la modification était sans effet
43
+ * — une reformulation, un commentaire. Elle ne touche jamais la base.
44
+ *
45
+ * ## `--forget <source>/<tag>` : l'issue d'un historique qui MENT
46
+ *
47
+ * Il existait un état dont aucune commande ne sortait : une migration inscrite
48
+ * comme réussie que personne n'a jamais exécutée — une adoption mal bornée, ou
49
+ * une base héritée d'une version antérieure aux gardes. La base ne porte pas
50
+ * les tables, l'historique affirme le contraire, et rien n'est « en attente ».
51
+ * Le générateur disait alors « c'est l'historique qu'il faut reprendre » et
52
+ * renvoyait ici ; mais cette commande ne savait lever que des marqueurs
53
+ * d'ÉCHEC, et répondait « rien à réparer ». Trois messages vrais, aucun geste
54
+ * — et le seul chemin restant était de détruire la base.
55
+ *
56
+ * `--forget` désinscrit UNE entrée nommée, pour qu'elle soit rejouée au
57
+ * passage suivant. Bornée par construction : il faut la nommer
58
+ * (`--forget app/0003_ajout_facture`), il n'y a ni motif ni lot. La base n'est
59
+ * pas touchée — si la migration avait réellement été appliquée, son rejeu
60
+ * échouera, bruyamment, ce qui est le comportement voulu.
61
+ */
62
+ declare class OrmMigrateRepair extends OrmMigrateCommand {
63
+ constructor(cli: CliKernel);
64
+ generate(opts?: IRepairOptions): Promise<this>;
65
+ }
66
+ export default OrmMigrateRepair;
@@ -0,0 +1,38 @@
1
+ import type { CliKernel } from "nodefony";
2
+ import { OrmMigrateCommand, type IMigrateSharedOptions } from "./migrateShared.js";
3
+ /**
4
+ * `nodefony orm:migrate:status` — dit ce que la base a reçu, ce qui reste, et
5
+ * ce qu'il faut taper.
6
+ *
7
+ * **Lecture seule et sans verrou.** Elle n'écrit rien, pas même la table
8
+ * d'historique : un état qui se consulte ne doit pas modifier ce qu'il
9
+ * observe — et une sonde qui écrit dans la base n'est plus une sonde.
10
+ *
11
+ * ## Son autre métier : barrière d'intégration continue
12
+ *
13
+ * Le code de sortie porte le verdict, et il ne changera jamais de sens :
14
+ *
15
+ * | Code | Ce que ça veut dire |
16
+ * | ---- | ------------------------------------------------------------------ |
17
+ * | `0` | à jour — rien à faire |
18
+ * | `1` | une action humaine est requise (migrations en attente, écart, échec) |
19
+ * | `2` | la commande n'a pas pu travailler (base injoignable, verrou, usage) |
20
+ *
21
+ * Une passe de déploiement peut donc s'arrêter dessus sans lire un mot :
22
+ *
23
+ * ```bash
24
+ * nodefony orm:migrate:status --json || exit 1
25
+ * ```
26
+ *
27
+ * @example Ce qu'un agent lit
28
+ * ```bash
29
+ * nodefony orm:migrate:status --json | jq -r '.verdict, .nextActions[0].command'
30
+ * # pending
31
+ * # nodefony orm:migrate
32
+ * ```
33
+ */
34
+ declare class OrmMigrateStatus extends OrmMigrateCommand {
35
+ constructor(cli: CliKernel);
36
+ generate(opts?: IMigrateSharedOptions): Promise<this>;
37
+ }
38
+ export default OrmMigrateStatus;
@@ -0,0 +1,65 @@
1
+ import type { CliKernel } from "nodefony";
2
+ import { EXIT, action } from "../src/migrator/explain.js";
3
+ import { OrmMigrateCommand, type IMigrateSharedOptions } from "./migrateShared.js";
4
+ /**
5
+ * Options propres à l'application des migrations.
6
+ *
7
+ * Pas de `--source` ici, et c'est délibéré : l'applicateur applique l'ordre
8
+ * complet (le framework d'abord, l'application ensuite) et n'a pas de filtre
9
+ * par source. Déclarer l'option pour l'ignorer serait pire qu'un refus — un
10
+ * argument accepté puis jeté fait croire à un comportement qui n'existe pas.
11
+ */
12
+ interface IMigrateOptions extends IMigrateSharedOptions {
13
+ dryRun?: boolean;
14
+ outOfOrder?: boolean;
15
+ ignoreMissing?: boolean;
16
+ allowDestructive?: boolean;
17
+ }
18
+ /**
19
+ * `nodefony orm:migrate` — applique les migrations restantes, sous verrou.
20
+ *
21
+ * ## Ce que la commande fait, dans cet ordre exact
22
+ *
23
+ * 1. **prend le verrou** de la base (verrou natif du serveur : il se libère
24
+ * tout seul si le processus meurt — aucun verrou fantôme à débloquer à la
25
+ * main) ;
26
+ * 2. **crée la table d'historique** si elle n'existe pas ;
27
+ * 3. **valide TOUT** — empreintes, ordre, échecs passés, fichiers manquants ;
28
+ * 4. **applique** les migrations une par une, chacune dans sa transaction là où
29
+ * la base le permet.
30
+ *
31
+ * La validation précède toute écriture : **un refus laisse la base
32
+ * intacte**. C'est ce qui rend sûr de relancer la commande après un refus.
33
+ *
34
+ * ## Elle ne s'exécute jamais toute seule au démarrage — sauf si on le demande
35
+ *
36
+ * Appliquer des migrations au démarrage n'est pas un défaut, et ce n'est pas de
37
+ * la prudence : au démarrage, plusieurs exemplaires partent en même temps. Le
38
+ * mode `ddl: "migrate"` existe pour les déploiements à exemplaire unique et
39
+ * s'écrit à la main. En production orchestrée, c'est un travail séparé qui
40
+ * lance cette commande AVANT que les nouveaux exemplaires ne démarrent.
41
+ *
42
+ * ## Le compte qui migre n'est pas le compte qui sert
43
+ *
44
+ * `NF_MIGRATE_DATABASE_URL` remplace, pour cette commande seulement, la
45
+ * connexion du connecteur. C'est le véhicule du moindre privilège : le secret
46
+ * qui a le droit de modifier le schéma est monté dans le travail de migration,
47
+ * et nulle part ailleurs. Elle doit désigner une connexion **directe** — un
48
+ * répartiteur de connexions en mode transaction casse le verrou.
49
+ *
50
+ * @example Voir sans rien appliquer
51
+ * ```bash
52
+ * nodefony orm:migrate --dry-run
53
+ * ```
54
+ *
55
+ * @example Dans un travail de déploiement
56
+ * ```bash
57
+ * NF_MIGRATE_DATABASE_URL="postgres://migrator:…@db:5432/app" nodefony orm:migrate --json
58
+ * ```
59
+ */
60
+ declare class OrmMigrate extends OrmMigrateCommand {
61
+ constructor(cli: CliKernel);
62
+ generate(opts?: IMigrateOptions): Promise<this>;
63
+ }
64
+ export default OrmMigrate;
65
+ export { EXIT, action };
@@ -0,0 +1,55 @@
1
+ import type { CliKernel } from "nodefony";
2
+ import { OrmMigrateCommand, type IMigrateSharedOptions } from "./migrateShared.js";
3
+ /** Options propres à la remise à zéro d'une base de développement. */
4
+ interface IResetOptions extends IMigrateSharedOptions {
5
+ yes?: boolean;
6
+ }
7
+ /**
8
+ * `nodefony orm:reset` — vide la base de DÉVELOPPEMENT et la laisse prête à
9
+ * être recréée.
10
+ *
11
+ * ## Le geste qu'elle remplace
12
+ *
13
+ * Jusqu'ici, le générateur d'applications disait « supprime ta base de dev, ou
14
+ * passe par une migration » : deux gestes manuels, aucun outil, et chacun s'en
15
+ * tirait comme il pouvait. Une seule commande à retenir pour toute l'équipe,
16
+ * au lieu d'un arbre de décision.
17
+ *
18
+ * ## Ce qu'elle fait exactement
19
+ *
20
+ * Elle supprime **toutes les tables du schéma courant** de la connexion —
21
+ * l'historique des migrations compris. Elle ne supprime NI la base, NI le
22
+ * fichier : supprimer une base demande des droits d'administration que le
23
+ * compte de l'application n'a pas, et supprimer un fichier ouvert échoue sous
24
+ * Windows. Le résultat est le même — une base vide — et il s'obtient partout de
25
+ * la même façon.
26
+ *
27
+ * Ensuite : en mode `auto` (le défaut en développement), le prochain démarrage
28
+ * recrée le schéma depuis le code. Dans les autres modes, la commande dit quoi
29
+ * lancer.
30
+ *
31
+ * ## Le refus est une LISTE BLANCHE, pas une liste noire
32
+ *
33
+ * Elle n'accepte de travailler que si l'environnement est `development`. Pas
34
+ * « refusée en production » : `staging`, `preprod`, `test` et tout
35
+ * environnement inconnu refusent aussi. La différence n'est pas théorique — une
36
+ * garde écrite « si production » laisse passer tout ce qu'on n'a pas pensé à
37
+ * nommer, et c'est exactement là que l'accident arrive.
38
+ *
39
+ * `NF_MIGRATE_DATABASE_URL` n'est **pas** lue par cette commande. Cette
40
+ * variable porte le compte qui a le droit de modifier le schéma en
41
+ * production : lui laisser désigner la cible d'un effacement serait offrir la
42
+ * seule combinaison qu'il ne faut jamais rendre possible.
43
+ *
44
+ * @example
45
+ * ```bash
46
+ * nodefony orm:reset # demande confirmation en terminal
47
+ * nodefony orm:reset --yes # sans question (script)
48
+ * ```
49
+ */
50
+ declare class OrmReset extends OrmMigrateCommand {
51
+ #private;
52
+ constructor(cli: CliKernel);
53
+ generate(opts?: IResetOptions): Promise<this>;
54
+ }
55
+ export default OrmReset;
@@ -0,0 +1,110 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * @nodefony/drizzle — CONFIGURATION DU MODULE (schéma Zod = source unique).
4
+ *
5
+ * ⭐ TL;DR : CE SCHÉMA EST LA CONFIG. Chaque `.default(...)` = la valeur d'usine ;
6
+ * changer un défaut du module = ÉDITER ICI (et nulle part ailleurs). L'app, elle,
7
+ * surcharge via `use("@nodefony/...", { … })` dans SON `nodefony.config.ts`.
8
+ *
9
+ * RÈGLE D'OR (ADR-0006) : ce fichier porte le **schéma Zod commenté** (type +
10
+ * validation + défaut + doc) ET matérialise les défauts via `parse({})`. Aucune
11
+ * valeur n'est re-tapée ailleurs. Le builder (`defineDrizzleConfig`) et les types
12
+ * (`interfaces/IDrizzleConfig.ts`) importent le schéma D'ICI (nœud bas : ce fichier
13
+ * n'importe que `zod` → pas de cycle).
14
+ *
15
+ * Convention figée (cf `feedback_config_validation_zod` + audit config ORM
16
+ * 2026-06), alignée sur `@nodefony/mongoose`/`@nodefony/redis`/`@nodefony/realtime`.
17
+ *
18
+ * ⚠️ Le schéma reste **PUR** : `filename` est **optionnel SANS défaut** — le chemin
19
+ * SQLite par défaut dépend de `kernel.path` (indisponible à l'évaluation du schéma)
20
+ * et est résolu au boot par `DrizzleService`. Aucune lecture `process.env` ici
21
+ * (l'env est appliqué dans `defineDrizzleConfig`).
22
+ *
23
+ * SURCHARGE (précédence croissante — cf ADR-0006) :
24
+ * • App (typé) : `use("@nodefony/drizzle", { connectors: { … } })` ;
25
+ * • Par environnement : infra database `NF_DATABASE_URL`/`DATABASE_URL`
26
+ * (dialecte déduit du scheme, appliqué dans `defineDrizzleConfig`) ;
27
+ * • Déploiement/Docker : `NF__DRIZZLE__<CHEMIN>=valeur` (override env générique).
28
+ */
29
+ /**
30
+ * Dialectes SQL supportés par l'adapter Drizzle. `sqlite` (better-sqlite3) est le
31
+ * défaut bootable ; `postgres` (pg) / `mysql` (mysql2) sont des drivers chargés en
32
+ * LAZY (`optionalDependencies` + `await import` au connect) — un framework doit
33
+ * porter ses entités sur les bases majeures (cf chantier portabilité multi-dialecte).
34
+ */
35
+ export declare const SQL_DIALECTS: readonly ["sqlite", "postgres", "mysql"];
36
+ /** Dialecte SQL d'un connecteur Drizzle. */
37
+ export type SqlDialect = (typeof SQL_DIALECTS)[number];
38
+ /**
39
+ * Stratégies de fabrication du schéma d'un connecteur.
40
+ *
41
+ * Trois valeurs, et pas une de plus : ce qui fait le schéma est soit le
42
+ * démarrage à partir du code (`auto`), soit le démarrage à partir des fichiers
43
+ * de migration (`migrate`), soit personne (`none`). Un quatrième mode serait un
44
+ * mélange, donc un comportement que personne ne saurait décrire dans un
45
+ * incident.
46
+ */
47
+ export declare const DDL_MODES: readonly ["auto", "migrate", "none"];
48
+ /** Stratégie de fabrication du schéma d'un connecteur. */
49
+ export type DdlMode = (typeof DDL_MODES)[number];
50
+ /**
51
+ * Conduites de la sonde de disponibilité quand le schéma est en retard.
52
+ *
53
+ * `fail` retient la mise en service (le processus ne reçoit pas de trafic),
54
+ * `warn` journalise et sert quand même, `off` ne regarde pas.
55
+ */
56
+ export declare const MIGRATION_CHECK_MODES: readonly ["fail", "warn", "off"];
57
+ /** Conduite de la sonde de disponibilité face à un schéma en retard. */
58
+ export type MigrationCheckMode = (typeof MIGRATION_CHECK_MODES)[number];
59
+ /**
60
+ * Conduites face à une base qui ne correspond plus au code alors que
61
+ * l'historique est complet.
62
+ *
63
+ * Le défaut est `report` — et il est structurel, pas prudent : une application
64
+ * qui écrit des migrations libres (vues, déclencheurs, colonnes ajoutées à une
65
+ * table d'entité) a une base légitimement différente du schéma déclaré, en
66
+ * permanence. Faire tomber sa mise en service dessus rendrait le constat
67
+ * inutilisable, donc mort. Superviser ne fait pas tomber un déploiement.
68
+ */
69
+ export declare const DIVERGENCE_MODES: readonly ["report", "fail", "off"];
70
+ /** Conduite face à une base qui a divergé du schéma déclaré. */
71
+ export type DivergenceMode = (typeof DIVERGENCE_MODES)[number];
72
+ export declare const drizzleConfigSchema: z.ZodObject<{
73
+ connectors: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
74
+ dialect: z.ZodDefault<z.ZodEnum<{
75
+ mysql: "mysql";
76
+ postgres: "postgres";
77
+ sqlite: "sqlite";
78
+ }>>;
79
+ filename: z.ZodOptional<z.ZodString>;
80
+ url: z.ZodOptional<z.ZodString>;
81
+ ddl: z.ZodOptional<z.ZodEnum<{
82
+ auto: "auto";
83
+ migrate: "migrate";
84
+ none: "none";
85
+ }>>;
86
+ }, z.core.$strict>>>;
87
+ migrations: z.ZodDefault<z.ZodObject<{
88
+ dir: z.ZodDefault<z.ZodString>;
89
+ check: z.ZodOptional<z.ZodEnum<{
90
+ fail: "fail";
91
+ off: "off";
92
+ warn: "warn";
93
+ }>>;
94
+ lockTimeoutMs: z.ZodDefault<z.ZodNumber>;
95
+ divergence: z.ZodDefault<z.ZodEnum<{
96
+ fail: "fail";
97
+ off: "off";
98
+ report: "report";
99
+ }>>;
100
+ }, z.core.$strict>>;
101
+ frameworkEntities: z.ZodDefault<z.ZodBoolean>;
102
+ }, z.core.$strict>;
103
+ /** Type de sortie (config normalisée + défauts appliqués). */
104
+ export type DrizzleConfig = z.infer<typeof drizzleConfigSchema>;
105
+ /**
106
+ * Défauts du module, matérialisés depuis le schéma (source unique). Toujours
107
+ * valides par construction ; passés au `super(..., config)` du Module class.
108
+ */
109
+ declare const config: DrizzleConfig;
110
+ export default config;
@@ -0,0 +1,24 @@
1
+ import type { IDrizzleConfig, IDrizzleConfigInput } from "../interfaces/IDrizzleConfig.js";
2
+ /**
3
+ * Builder type-safe de la configuration de `@nodefony/drizzle`.
4
+ *
5
+ * ⭐ TL;DR : MACHINERIE DE BOOT — on n'édite (presque) jamais ce fichier. Même
6
+ * pattern que `nodefony.config.ts` ↔ `defineConfig()` du core : `config.ts` PORTE
7
+ * la config (schéma + défauts), `define<X>Config()` la VALIDE au boot (parse +
8
+ * env + freeze) et publie le JSON Schema Studio.
9
+ *
10
+ * Aligné sur `defineMongooseConfig` (l'autre driver ORM) : source unique
11
+ * (`./config.ts`), VALIDE + applique l'ENV + GÈLE. Le **chemin SQLite par défaut**
12
+ * (kernel-dépendant) n'est PAS résolu ici (schéma pur) mais dans `DrizzleService`
13
+ * au boot — cf audit config ORM 2026-06 §3.2.
14
+ *
15
+ * @param config - configuration brute (sections omises = défauts sûrs).
16
+ * @returns config validée, surchargée par l'env, et gelée.
17
+ * @throws ZodError si la config est invalide.
18
+ */
19
+ export declare function defineDrizzleConfig(config?: IDrizzleConfigInput): IDrizzleConfig;
20
+ /**
21
+ * JSON Schema introspectable de la config Drizzle — destiné au formulaire
22
+ * d'édition Studio (futur) et à la documentation générée.
23
+ */
24
+ export declare function drizzleConfigJsonSchema(): unknown;