@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,81 @@
1
+ import type { IMigrationDriver } from "../types.js";
2
+ import { toDollarParams } from "../catalog.js";
3
+ export { toDollarParams };
4
+ /**
5
+ * Clé du verrou consultatif PostgreSQL — **figée à vie**.
6
+ *
7
+ * Elle est dérivée par une RÈGLE, pas choisie : les huit premiers octets de
8
+ * `sha256("nodefony:migrations")`, lus en entier signé big-endian. Une identité
9
+ * « gravée » dont la valeur serait tirée au hasard d'un commit ne serait pas
10
+ * gravée du tout — on ne pourrait plus la recalculer pour la vérifier.
11
+ *
12
+ * La changer ferait que deux versions du framework ne s'excluent plus
13
+ * mutuellement : exactement pendant un déploiement, le seul moment qui compte.
14
+ */
15
+ export declare const PG_LOCK_KEY: bigint;
16
+ /**
17
+ * Pilote PostgreSQL de l'applicateur — **une seule connexion**, pas un pool.
18
+ *
19
+ * `pg_advisory_lock` est un verrou de SESSION : avec un pool, il serait pris
20
+ * sur une connexion, le DDL exécuté sur une autre et la libération faite sur
21
+ * une troisième — le verrou ne protégerait rien. D'où une connexion tenue du
22
+ * verrou à sa libération.
23
+ *
24
+ * Le verrou s'auto-libère à la mort de la connexion : un process tué en plein
25
+ * vol ne laisse **aucun verrou zombie** à lever à la main. C'est l'argument qui
26
+ * a fait écarter une table de verrou.
27
+ */
28
+ export declare class PostgresMigrationDriver implements IMigrationDriver {
29
+ #private;
30
+ readonly dialect: "postgres";
31
+ readonly transactionalDdl = true;
32
+ /**
33
+ * @param url - URL de connexion DIRECTE au serveur.
34
+ */
35
+ constructor(url: string);
36
+ /**
37
+ * Ouvre la connexion dédiée.
38
+ *
39
+ * @throws Error si le pilote `pg` n'est pas installé.
40
+ */
41
+ connect(): Promise<void>;
42
+ /** Motif de la perte de connexion, si elle a été constatée. */
43
+ get lostReason(): string | null;
44
+ /** @inheritdoc */
45
+ exec(sql: string): Promise<void>;
46
+ /** @inheritdoc */
47
+ query<T extends Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<T[]>;
48
+ /** @inheritdoc */
49
+ sameColumnName(declared: string, actual: string): boolean;
50
+ /** @inheritdoc */
51
+ tableExists(table: string): Promise<boolean>;
52
+ /** @inheritdoc */
53
+ columnsOf(table: string): Promise<string[]>;
54
+ /** @inheritdoc */
55
+ begin(): Promise<void>;
56
+ /** @inheritdoc */
57
+ commit(): Promise<void>;
58
+ /** @inheritdoc */
59
+ rollback(): Promise<void>;
60
+ /**
61
+ * Prend le verrou consultatif, en attente BORNÉE.
62
+ *
63
+ * `pg_advisory_lock` attend sans limite : un pod bloqué derrière un job mort
64
+ * attendrait pour toujours, sans rien dire. On sonde donc avec
65
+ * `pg_try_advisory_lock` jusqu'au délai imparti, ce qui rend l'attente
66
+ * observable et l'échec explicite.
67
+ *
68
+ * `lock_timeout` est posé pour son VRAI rôle, distinct : borner l'attente des
69
+ * verrous de TABLE que prennent les `ALTER` — sans lui, un `ALTER` coincé
70
+ * derrière une transaction longue bloque toute la table en file d'attente.
71
+ *
72
+ * @param timeoutMs - délai maximal d'attente du verrou.
73
+ * @returns une promesse résolue une fois le verrou tenu.
74
+ * @throws Error si le verrou n'est pas obtenu dans le délai.
75
+ */
76
+ lock(timeoutMs: number): Promise<void>;
77
+ /** @inheritdoc */
78
+ unlock(): Promise<void>;
79
+ /** @inheritdoc */
80
+ close(): Promise<void>;
81
+ }
@@ -0,0 +1,53 @@
1
+ import { type IMigrationDriver } from "../types.js";
2
+ /**
3
+ * Pilote SQLite de l'applicateur — connexion `better-sqlite3` dédiée.
4
+ *
5
+ * **Pas de verrou explicite** : SQLite est à écrivain unique par nature. Ce
6
+ * n'est pas un manque à combler, c'est la description exacte de la situation —
7
+ * la sérialisation est faite par le moteur, et une table de verrou maison ne
8
+ * ferait qu'ajouter une pièce à déverrouiller à la main le jour où un process
9
+ * meurt.
10
+ */
11
+ export declare class SqliteMigrationDriver implements IMigrationDriver {
12
+ #private;
13
+ readonly dialect: "sqlite";
14
+ readonly transactionalDdl = true;
15
+ /**
16
+ * @param filename - fichier de la base, ou `:memory:`.
17
+ */
18
+ constructor(filename: string);
19
+ /**
20
+ * Ouvre la base, en chargeant le pilote à la demande.
21
+ *
22
+ * Même patron que `PostgresMigrationDriver.connect()` : `openMigrationDriver`
23
+ * enchaîne `new` puis `await connect()` pour les trois dialectes, et aucune
24
+ * application n'embarque un pilote qu'elle n'ouvre pas.
25
+ *
26
+ * @throws Error si le pilote `better-sqlite3` n'est pas installé.
27
+ */
28
+ connect(): Promise<void>;
29
+ /** @inheritdoc */
30
+ exec(sql: string): Promise<void>;
31
+ /** @inheritdoc */
32
+ query<T extends Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<T[]>;
33
+ /** @inheritdoc */
34
+ sameColumnName(declared: string, actual: string): boolean;
35
+ /** @inheritdoc */
36
+ tableExists(table: string): Promise<boolean>;
37
+ /** @inheritdoc */
38
+ columnsOf(table: string): Promise<string[]>;
39
+ /** @inheritdoc */
40
+ begin(): Promise<void>;
41
+ /** @inheritdoc */
42
+ commit(): Promise<void>;
43
+ /** @inheritdoc */
44
+ rollback(): Promise<void>;
45
+ /** @inheritdoc */
46
+ lock(): Promise<void>;
47
+ /** @inheritdoc */
48
+ unlock(): Promise<void>;
49
+ /** @inheritdoc */
50
+ close(): Promise<void>;
51
+ /** Table d'historique servie par ce pilote — pour les diagnostics. */
52
+ get historyTable(): string;
53
+ }
@@ -0,0 +1,424 @@
1
+ import type { DivergenceMode, SqlDialect } from "../../config/config.js";
2
+ import type { IMigrationAction, IMigrationPlan, IMigrationVerdict } from "./types.js";
3
+ import type { ISchemaComparison } from "./schemaDiff.js";
4
+ import type { IDestructiveFinding } from "./destructive.js";
5
+ import type { IDiscoveryFacts } from "./refusals.js";
6
+ /**
7
+ * Le RENDU des migrations : un seul producteur, quatre destinataires.
8
+ *
9
+ * La ligne de commande, la sortie `--json`, le plan d'administration et
10
+ * l'assistance affichée dans un corps d'erreur disent tous la même chose. Écrire
11
+ * la phrase pour l'humain d'un côté et l'objet pour la machine de l'autre
12
+ * ferait deux implémentations d'une même règle — et elles divergeraient, comme
13
+ * toutes les copies.
14
+ *
15
+ * ## Ce que ce fichier garantit, et qui n'est pas cosmétique
16
+ *
17
+ * **Aucune sortie ne laisse l'utilisateur sans geste suivant.** Chaque situation
18
+ * — succès, attente, refus, panne — produit trois choses :
19
+ *
20
+ * 1. **le FAIT**, en français, sans terme d'art (« le fichier `0002_x.sql` a
21
+ * changé après avoir été appliqué ») ;
22
+ * 2. **ce que ça veut dire**, c'est-à-dire la cause la plus probable ;
23
+ * 3. **la commande exacte à copier**, jamais une allusion à une option qu'il
24
+ * faudrait deviner.
25
+ *
26
+ * Un agent lit `nextActions[0].command` et sait quoi faire sans comprendre le
27
+ * français ; un humain lit les mêmes mots sous une forme lisible. C'est la même
28
+ * donnée.
29
+ */
30
+ /**
31
+ * Version de la charge utile `--json` **et** du plan d'administration.
32
+ *
33
+ * Contrat public : ajouter un champ est une évolution mineure, en retirer ou en
34
+ * renommer un est interdit sur toute la série majeure. Un `jq` écrit par un
35
+ * utilisateur ne doit jamais casser sur une mise à jour de correctif.
36
+ */
37
+ export declare const MIGRATION_FORMAT_VERSION = 1;
38
+ /**
39
+ * Verdicts possibles — **énumération GELÉE** avec {@link MIGRATION_FORMAT_VERSION}.
40
+ *
41
+ * Y ajouter une valeur après publication casserait tout consommateur qui les
42
+ * traite exhaustivement (`case` sans `default`, `match` d'un agent). Une
43
+ * situation nouvelle se range donc dans la famille existante qui lui convient,
44
+ * et se détaille dans les champs — jamais dans un huitième mot.
45
+ */
46
+ export type MigrationVerdictName =
47
+ /** Rien à faire : l'historique est complet et les fichiers concordent. */
48
+ "up-to-date"
49
+ /** Des migrations restent à appliquer. */
50
+ | "pending"
51
+ /**
52
+ * Les fichiers ne correspondent plus à ce que l'historique enregistre :
53
+ * empreinte changée après application, ou fichier disparu. Le cas du fichier
54
+ * disparu se range ici — l'énumération est gelée, et c'est bien la même
55
+ * famille : ce qui est écrit dans l'historique ne se retrouve plus sur le
56
+ * disque. Le détail reste lisible dans `sources[].drifted` et `[].missing`.
57
+ */
58
+ | "drift"
59
+ /** Une migration a échoué, ou n'a jamais fini : réparation requise. */
60
+ | "failed"
61
+ /** La base a déjà les tables mais aucun historique : adoption requise. */
62
+ | "adopt"
63
+ /**
64
+ * L'historique est complet, rien n'est en attente — et la base ne correspond
65
+ * pourtant pas au schéma déclaré dans le code.
66
+ */
67
+ | "divergent";
68
+ /** Ce qu'une source de migrations donne à voir. */
69
+ export interface IMigrationSourceReport {
70
+ /** Nom logique de la source (`framework`, `app`, un module tiers). */
71
+ name: string;
72
+ /** Migrations appliquées avec succès. */
73
+ applied: number;
74
+ /** Migrations restant à appliquer. */
75
+ pending: number;
76
+ /** Marqueurs d'échec à lever. */
77
+ failed: number;
78
+ /** Identités en attente, dans l'ordre d'application. */
79
+ pendingTags: string[];
80
+ /** Fichiers modifiés après avoir été appliqués. */
81
+ drifted: {
82
+ tag: string;
83
+ expected: string;
84
+ actual: string;
85
+ }[];
86
+ /** Identités enregistrées dont le fichier a disparu. */
87
+ missing: string[];
88
+ /**
89
+ * UNE LIGNE PAR MIGRATION, dans l'ordre d'application — ce que les compteurs
90
+ * ci-dessus agrègent.
91
+ *
92
+ * Ajoutée pour l'écran d'administration, qui doit montrer QUAND chaque
93
+ * migration est passée, en combien de temps et sous quel déploiement.
94
+ * L'applicateur le savait déjà : le plan porte `startedAt`, `executionMs`,
95
+ * `appliedBy` et `runId` depuis toujours, et le rapport les jetait à un pas
96
+ * de la sortie — l'exploitant devait alors ouvrir un client SQL sur
97
+ * l'historique pour une réponse que le produit avait calculée.
98
+ *
99
+ * Ajout ADDITIF : les lecteurs du format 1 qui ne la connaissent pas
100
+ * l'ignorent, aucun champ existant ne change de sens.
101
+ */
102
+ entries: IMigrationEntryReport[];
103
+ }
104
+ /** Une migration, telle que l'historique et les fichiers la décrivent. */
105
+ export interface IMigrationEntryReport {
106
+ /** Identité immuable une fois publiée. */
107
+ tag: string;
108
+ /** Où en est cette migration pour CE connecteur. */
109
+ status: "applied" | "pending" | "failed" | "drifted" | "missing";
110
+ /** Début de l'application, en millisecondes — absent si jamais appliquée. */
111
+ appliedAt?: number;
112
+ /** Durée de l'application, en millisecondes. */
113
+ durationMs?: number;
114
+ /** Ce qui l'a appliquée, tel que l'historique l'a retenu. */
115
+ appliedBy?: string;
116
+ /** Déploiement qui l'a portée — groupe les migrations d'un même passage. */
117
+ runId?: string;
118
+ /** Motif de l'échec, quand elle a échoué. */
119
+ error?: string;
120
+ }
121
+ /**
122
+ * La charge utile figée — cœur NEUTRE au premier niveau, spécifique du pilote
123
+ * sous `driver`.
124
+ *
125
+ * Ce découpage est la seule chose qui permettra à un second ORM de remplir la
126
+ * même structure sans casser un `jq` d'utilisateur. Si `dialect` vivait au
127
+ * premier niveau, chaque script le graverait — et un ORM sans dialecte n'aurait
128
+ * plus de place dans la structure.
129
+ */
130
+ export interface IMigrationReport {
131
+ formatVersion: typeof MIGRATION_FORMAT_VERSION;
132
+ /** Connecteur observé. */
133
+ connector: string;
134
+ /** Situation d'ensemble — énumération gelée. */
135
+ verdict: MigrationVerdictName;
136
+ /** Code de sortie que cette situation produit sur la ligne de commande. */
137
+ exitCode: 0 | 1 | 2;
138
+ /** Le fait constaté, en une phrase française. */
139
+ summary: string;
140
+ /** Ce qu'il faut faire, du plus direct au plus assumé. Peut être vide. */
141
+ nextActions: IMigrationAction[];
142
+ /** Une source par entrée, dans l'ordre d'application. */
143
+ sources: IMigrationSourceReport[];
144
+ /**
145
+ * CE QUI diverge, nommé — présent au seul verdict `divergent`, absent
146
+ * partout ailleurs.
147
+ *
148
+ * Le verdict dit qu'il y a un écart ; cette clé dit LEQUEL. Sans elle,
149
+ * l'exploitant ouvre un client SQL et compare table par table, sur une base
150
+ * de production, au pire moment — alors que le produit connaissait déjà la
151
+ * réponse. Elle vit au premier niveau, dans le cœur NEUTRE : un second ORM
152
+ * remplira la même structure, et un `jq` d'utilisateur ne doit pas avoir
153
+ * gravé un chemin qui passe par le nom d'un pilote.
154
+ *
155
+ * Son ABSENCE est un fait, pas un oubli : sur une base conforme, il n'y a
156
+ * rien à nommer.
157
+ */
158
+ divergence?: ISchemaComparison;
159
+ /**
160
+ * Ce que le passage a DÉTRUIT, nommé — présent au seul run réel qui a
161
+ * appliqué une migration destructive, absent partout ailleurs.
162
+ *
163
+ * L'essai à blanc publiait déjà cette liste ; le run réel ne la publiait pas,
164
+ * et l'avertissement n'existait qu'en mode humain. Un agent — le mode que le
165
+ * produit lui prescrit — appliquait donc une perte de données sans qu'un seul
166
+ * octet de ce qu'il relit ne la mentionne. Son absence est un fait : rien
167
+ * n'a été détruit.
168
+ */
169
+ destructive?: IDestructiveFinding[];
170
+ /** Tout ce qui est propre au pilote SQL vit ici, et nulle part ailleurs. */
171
+ driver: {
172
+ kind: "sql";
173
+ dialect: SqlDialect;
174
+ ddl: string;
175
+ historyTable: string;
176
+ /** La base visée, sans identifiant ni mot de passe. */
177
+ target?: string;
178
+ /** `true` quand la cible vient de `NF_MIGRATE_DATABASE_URL`. */
179
+ fromMigrateUrl?: boolean;
180
+ };
181
+ }
182
+ /** Grille des codes de sortie — **figée, jamais réaffectée**. */
183
+ export declare const EXIT: {
184
+ /** À jour, ou appliqué avec succès. */
185
+ readonly ok: 0;
186
+ /** Une action humaine est requise sur la base ou sur les fichiers. */
187
+ readonly actionRequired: 1;
188
+ /** La commande n'a pas pu faire son travail (verrou, connexion, usage). */
189
+ readonly error: 2;
190
+ };
191
+ /** Fabrique une action affichable et exécutable. */
192
+ export declare function action(command: string): IMigrationAction;
193
+ /**
194
+ * Situation d'ensemble d'un plan, dans l'ordre de gravité.
195
+ *
196
+ * L'ordre n'est pas esthétique : il dit quel geste vient EN PREMIER. Une
197
+ * migration en échec doit être réparée avant qu'on parle de ce qui reste à
198
+ * appliquer, sinon l'utilisateur lance une commande qui va refuser.
199
+ *
200
+ * @param plan - plan calculé en lecture seule.
201
+ * @param divergent - la base a-t-elle divergé du schéma déclaré ?
202
+ * @returns le verdict, énumération gelée.
203
+ */
204
+ export declare function verdictOf(plan: IMigrationPlan, divergent?: boolean): MigrationVerdictName;
205
+ /**
206
+ * L'historique porte-t-il des migrations que CE code ne connaît pas, et rien
207
+ * d'autre ne cloche-t-il ?
208
+ *
209
+ * C'est l'état NORMAL de deux moments qu'on ne peut pas éviter : une mise à jour
210
+ * progressive, où les anciens exemplaires servent encore pendant que le travail
211
+ * de migration a déjà appliqué la suite ; et un retour arrière, où le code
212
+ * revient en arrière et la base reste en avance. Dans les deux cas, l'exemplaire
213
+ * n'a rien à appliquer, et tout ce qu'il connaît concorde.
214
+ *
215
+ * **Le verdict, lui, reste `drift` — et c'est juste** : ce qui est écrit dans
216
+ * l'historique ne se retrouve pas sur le disque. L'énumération des verdicts est
217
+ * GELÉE avec {@link MIGRATION_FORMAT_VERSION}, et un huitième mot casserait tout
218
+ * consommateur qui les traite exhaustivement. Ce qui était faux n'était pas le
219
+ * constat, c'était ce que la sonde de disponibilité en DÉDUISAIT : retenir le
220
+ * trafic sortait du service tous les anciens exemplaires dès la fin du travail
221
+ * de migration, avant que le premier nouveau soit prêt — une coupure totale sur
222
+ * un déploiement nominal, et l'impossibilité de revenir en arrière.
223
+ *
224
+ * Ce que cette fonction ne couvre PAS, et qui doit continuer de retenir : une
225
+ * empreinte qui a changé (`drifted`), une migration en attente ou en échec, une
226
+ * adoption requise. Une base en avance n'a rien de commun avec un fichier
227
+ * réécrit après coup.
228
+ *
229
+ * @param plan - plan calculé par le migrateur.
230
+ * @returns vrai si le seul écart est un historique en avance sur ce code.
231
+ */
232
+ export declare function isAheadOnly(plan: IMigrationPlan): boolean;
233
+ /**
234
+ * Une divergence RETIENT-elle un déploiement ?
235
+ *
236
+ * ## Pourquoi ce n'est pas un simple « le mode vaut-il `fail` »
237
+ *
238
+ * Le défaut est l'observation, et pour une bonne raison : une application qui
239
+ * écrit des migrations libres — vues, déclencheurs, colonnes ajoutées à la main
240
+ * — a une base légitimement différente du schéma déclaré, en permanence. Faire
241
+ * tomber ses déploiements là-dessus rendrait le constat inutilisable, et la
242
+ * première chose qu'on ferait serait de l'éteindre.
243
+ *
244
+ * **Mais ce raisonnement ne couvre pas une TABLE d'entité absente.** Aucune
245
+ * migration libre ne fait disparaître une table que le code déclare comme
246
+ * entité ; quand elle manque, c'est que le schéma applicatif n'a jamais été
247
+ * posé — la migration n'a pas été générée, pas commitée, ou pas appliquée. Ce
248
+ * n'est pas une base « différente », c'est une base sur laquelle l'application
249
+ * ne peut RIEN faire : chacune de ses routes rendra 500, et le pod se serait
250
+ * déclaré prêt.
251
+ *
252
+ * C'est exactement le constat qui a ouvert ce chantier : les onze tables du
253
+ * framework posées, zéro table applicative, et toutes les routes d'entités en
254
+ * erreur — sans qu'aucun verdict ne le dise assez fort pour arrêter quoi que
255
+ * ce soit.
256
+ *
257
+ * La graduation tient donc en trois lignes, et chaque lecteur y trouve son
258
+ * seuil : `off` ne retient jamais, `fail` retient tout écart, `report` — le
259
+ * défaut — retient ce qu'aucune main légitime ne produit.
260
+ *
261
+ * @param divergence - les écarts nommés, ou `null` quand il n'y en a pas.
262
+ * @param mode - `migrations.divergence`, tel que la configuration le déclare.
263
+ * @returns `true` si la situation doit retenir la mise en service.
264
+ */
265
+ export declare function divergenceIsBlocking(divergence: ISchemaComparison | null | undefined, mode?: DivergenceMode): boolean;
266
+ /**
267
+ * Le code de sortie d'un verdict.
268
+ *
269
+ * **`divergent` ne fait pas tomber un déploiement** quand la configuration le
270
+ * laisse en observation : superviser n'est pas bloquer. C'est ce qui rend le
271
+ * constat utilisable — une application qui écrit des migrations libres a une
272
+ * base légitimement différente du schéma déclaré, en permanence.
273
+ *
274
+ * @param verdict - situation d'ensemble.
275
+ * @param divergenceBlocks - `true` quand la divergence doit retenir la mise en
276
+ * service, tel que {@link divergenceIsBlocking} en décide.
277
+ * @returns `0`, `1` ou `2`.
278
+ */
279
+ export declare function exitCodeOf(verdict: MigrationVerdictName, divergenceBlocks?: boolean): 0 | 1 | 2;
280
+ /** Ce que le rendu doit savoir en plus du plan lui-même. */
281
+ export interface IReportContext {
282
+ /** Mode de schéma effectif du connecteur. */
283
+ ddl: string;
284
+ /**
285
+ * Les écarts NOMMÉS entre la base et le schéma déclaré, tels que
286
+ * `describeDivergence` les rend — `null` ou absent quand il n'y en a pas.
287
+ *
288
+ * C'est le détail qui décide du verdict, pas un booléen posé à côté : deux
289
+ * champs pour un même fait finissent par se contredire.
290
+ */
291
+ divergence?: ISchemaComparison | null;
292
+ /**
293
+ * `migrations.divergence`, tel que la configuration le déclare.
294
+ *
295
+ * Le MODE, jamais un booléen calculé par l'appelant : le seuil qui décide
296
+ * qu'un écart retient un déploiement est une règle du produit
297
+ * ({@link divergenceIsBlocking}), pas une décision que chaque appelant
298
+ * reprendrait à son compte — deux copies finiraient par ne plus retenir les
299
+ * mêmes situations.
300
+ */
301
+ divergenceMode?: DivergenceMode;
302
+ /**
303
+ * `orm:reset` est-elle acceptée dans cet environnement ?
304
+ *
305
+ * Elle efface : la règle est une liste blanche que porte `resetAllowed`, et
306
+ * elle est lue ici pour ne JAMAIS proposer un geste qui va refuser. Une
307
+ * action rendue puis rejetée détruit la confiance dans toutes les autres.
308
+ * Défaut prudent : `false` — on ne suppose pas le droit d'effacer.
309
+ */
310
+ canReset?: boolean;
311
+ /**
312
+ * La base RÉELLEMENT visée, telle que {@link describeTargetSafely} la
313
+ * désigne — sans identifiant ni mot de passe.
314
+ *
315
+ * Absente quand l'appelant ne la connaît pas ; jamais devinée ici.
316
+ */
317
+ target?: string;
318
+ /**
319
+ * La cible vient-elle de `NF_MIGRATE_DATABASE_URL` plutôt que de la
320
+ * configuration du connecteur ?
321
+ *
322
+ * C'est le fait qui manquait, et son absence coûtait cher : une variable
323
+ * oubliée dans un terminal détourne SILENCIEUSEMENT chaque commande de
324
+ * migration vers une autre base, qui rend « appliqué » et le code du succès
325
+ * pendant que la vraie ne reçoit rien. Le seul symptôme arrivait plus tard,
326
+ * au démarrage d'une application sur un schéma qui n'avait pas bougé.
327
+ *
328
+ * Publié à côté de la cible, et pas à sa place : savoir QUELLE base ne dit
329
+ * pas POURQUOI c'est celle-là.
330
+ */
331
+ fromMigrateUrl?: boolean;
332
+ }
333
+ /**
334
+ * Compose la charge utile complète d'un état de migration.
335
+ *
336
+ * @param plan - plan calculé en lecture seule.
337
+ * @param ctx - mode de schéma et conduite face à la divergence.
338
+ * @returns la charge utile, identique pour la ligne de commande, `--json`, le
339
+ * plan d'administration et la sonde.
340
+ */
341
+ export declare function buildReport(plan: IMigrationPlan, ctx: IReportContext): IMigrationReport;
342
+ /**
343
+ * Ce que le verdict veut dire — la CAUSE, entre le fait et le geste.
344
+ *
345
+ * C'est le bloc qu'on omet d'habitude, et c'est celui qui évite l'appel au
346
+ * collègue : l'utilisateur sait ce qui s'est passé, donc il sait si le geste
347
+ * proposé lui convient.
348
+ *
349
+ * @param verdict - situation d'ensemble.
350
+ * @returns une ou deux phrases, ou une chaîne vide si le fait se suffit.
351
+ */
352
+ export declare function meaningOf(verdict: MigrationVerdictName): string;
353
+ /** Codes ANSI, neutralisés hors terminal (un `| jq` ne doit rien recevoir). */
354
+ export interface IStyle {
355
+ bold: (s: string) => string;
356
+ dim: (s: string) => string;
357
+ green: (s: string) => string;
358
+ yellow: (s: string) => string;
359
+ red: (s: string) => string;
360
+ }
361
+ /**
362
+ * Construit le styliste : couleurs en terminal, texte nu ailleurs.
363
+ *
364
+ * @param tty - la sortie est-elle un terminal ?
365
+ * @returns les fonctions de mise en forme.
366
+ */
367
+ export declare function styleFor(tty: boolean): IStyle;
368
+ /**
369
+ * Rendu humain d'un état de migration — l'écran que voit celui qui tape
370
+ * `orm:migrate:status`.
371
+ *
372
+ * @param report - charge utile complète.
373
+ * @param style - mise en forme (couleurs ou texte nu).
374
+ * @returns le texte prêt à écrire sur la sortie standard.
375
+ */
376
+ export declare function renderStatus(report: IMigrationReport, style: IStyle): string;
377
+ /**
378
+ * Rendu humain de ce que la DÉCOUVERTE des entités a vu.
379
+ *
380
+ * Bloc court, posé sous les refus dont la cause peut être un schéma déclaré
381
+ * amputé : un fichier illisible, une table écrite pour un autre moteur, un
382
+ * dossier d'entités qui ne rend rien. L'outil de diff ne distingue pas une
383
+ * table absente d'une table SUPPRIMÉE — sans ces trois nombres, la correction
384
+ * naturelle porte sur la base, qui n'y est pour rien.
385
+ *
386
+ * @param facts - ce que la découverte a relevé.
387
+ * @param style - mise en forme.
388
+ * @returns le bloc, prêt à concaténer ; vide si rien n'appelle l'attention.
389
+ */
390
+ export declare function describeDiscovery(facts: IDiscoveryFacts, style: IStyle): string;
391
+ /**
392
+ * Rendu humain d'un REFUS de l'applicateur.
393
+ *
394
+ * Un refus est un contrat, pas un message : il énonce le fait, ce qu'il
395
+ * signifie, et donne la commande exacte à copier. L'utilisateur ne doit jamais
396
+ * avoir eu connaissance d'une option à l'avance.
397
+ *
398
+ * @param verdict - verdict structuré porté par le refus.
399
+ * @param message - phrase française déjà composée par l'applicateur.
400
+ * @param style - mise en forme.
401
+ * @param ddl - mode de schéma effectif, quand il change la cause (cf {@link refusalInMode}).
402
+ * @returns le texte prêt à écrire sur la sortie d'erreur.
403
+ */
404
+ export declare function renderRefusal(verdict: IMigrationVerdict, message: string, style: IStyle, ddl?: string): string;
405
+ /**
406
+ * Ce qu'un refus veut dire QUAND ON CONNAÎT LE MODE DE SCHÉMA.
407
+ *
408
+ * Le même fait mécanique n'a pas la même cause selon le mode, et le geste
409
+ * change avec la cause. Constaté en exécutant la commande pour de vrai : sur une
410
+ * base parfaitement NEUVE, en développement, `orm:migrate` refuse en disant que
411
+ * la base « porte déjà les tables ». C'est exact — et c'est le DÉMARRAGE
412
+ * lui-même qui vient de les créer, quelques millisecondes plus tôt, parce que le
413
+ * mode `auto` dérive le schéma du code. Sans cette précision, l'utilisateur
414
+ * cherche une base ancienne qui n'existe pas.
415
+ *
416
+ * @param code - code du refus.
417
+ * @param ddl - mode de schéma effectif du connecteur.
418
+ * @param connector - connecteur concerné, pour composer les gestes.
419
+ * @returns l'explication et les gestes, ou `null` si le refus se suffit.
420
+ */
421
+ export declare function refusalInMode(code: IMigrationVerdict["code"], ddl: string, connector: string): {
422
+ meaning: string;
423
+ actions: IMigrationAction[];
424
+ } | null;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Empreinte d'un fichier de migration : **normalisée** et **auto-descriptive**.
3
+ *
4
+ * Deux gestes, deux raisons distinctes.
5
+ *
6
+ * **Normalisée** (CRLF → LF) : Windows est un impératif produit, et un checkout
7
+ * sous `core.autocrlf` réécrit les `.sql`. Hacher les octets bruts ferait
8
+ * diverger toutes les empreintes de celles posées par l'image Linux qui a migré
9
+ * la base d'équipe — arrêt sur dérive permanent, pour un non-changement. Le
10
+ * garde-fou reste entier : toute modification RÉELLE du SQL déclenche l'arrêt,
11
+ * seule la représentation des fins de ligne cesse de compter.
12
+ *
13
+ * **Préfixée de son algorithme** (`sha256:<hex>`) : c'est la seule porte de
14
+ * sortie pour introduire un jour un autre algorithme en RECONNAISSANT les
15
+ * lignes anciennes, sans réécrire une seule base de production.
16
+ *
17
+ * @param content - contenu du fichier `.sql`, tel que lu sur le disque.
18
+ * @returns l'empreinte préfixée, telle qu'elle est stockée en base.
19
+ */
20
+ export declare function migrationHash(content: string): string;
21
+ /**
22
+ * Normalise un contenu SQL : marque d'ordre des octets retirée, fins de ligne en LF.
23
+ *
24
+ * Les deux gestes répondent au même fait — **le fichier a voyagé** — et doivent
25
+ * donc vivre au même endroit, sous peine de ne corriger qu'une moitié du
26
+ * problème.
27
+ *
28
+ * **La marque d'ordre des octets** (`U+FEFF`) est posée en tête par les
29
+ * éditeurs Windows et par PowerShell (`>` et `Out-File` l'écrivent par défaut).
30
+ * `fs.readFile(…, "utf8")` ne la retire pas : elle reste le premier caractère du
31
+ * contenu. Sans ce nettoyage, la première ligne cesse d'être reconnue comme le
32
+ * marqueur de format, et le refus affiche deux chaînes **visuellement
33
+ * identiques** — « attendu ceci, lu cela », avec ceci et cela à l'œil pareils.
34
+ * C'est le pire message d'erreur possible : celui qui n'apprend rien.
35
+ *
36
+ * @param content - contenu brut, tel que lu sur le disque.
37
+ * @returns le même contenu, sans marque d'ordre des octets et en LF.
38
+ */
39
+ export declare function normalizeSql(content: string): string;