@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,273 @@
|
|
|
1
|
+
import type { SqlDialect } from "../../config/config.js";
|
|
2
|
+
import type { ISchemaReader } from "./catalog.js";
|
|
3
|
+
/**
|
|
4
|
+
* URL de connexion réservée au **travail de migration**.
|
|
5
|
+
*
|
|
6
|
+
* Elle porte le compte qui a le droit de modifier le schéma — un compte que les
|
|
7
|
+
* exemplaires qui servent le trafic n'ont pas, et ne doivent pas avoir. C'est
|
|
8
|
+
* tout son intérêt : le secret est monté dans le travail de migration
|
|
9
|
+
* seulement.
|
|
10
|
+
*
|
|
11
|
+
* **Qui la lit, exactement** : les commandes `orm:migrate`,
|
|
12
|
+
* `orm:migrate:status`, `orm:migrate:baseline` et `orm:migrate:repair`.
|
|
13
|
+
* Personne d'autre — ni le démarrage de l'application, ni `orm:reset`, ni un
|
|
14
|
+
* store. Une variable de privilège élevé qui fuiterait dans le chemin ordinaire
|
|
15
|
+
* annulerait le moindre privilège qu'elle sert à installer.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ Elle doit désigner une connexion **directe** au serveur. Un répartiteur de
|
|
18
|
+
* connexions en mode transaction (PgBouncer `transaction`) casse les verrous
|
|
19
|
+
* consultatifs de session — et le verrou de l'applicateur en est un.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MIGRATE_URL_ENV = "NF_MIGRATE_DATABASE_URL";
|
|
22
|
+
/**
|
|
23
|
+
* Contrats de l'applicateur de migrations — verdicts, entrées de journal,
|
|
24
|
+
* historique, plan, et le pilote à connexion unique dont il a besoin.
|
|
25
|
+
*
|
|
26
|
+
* **Pourquoi un fichier de types séparé** : ces formes traversent la frontière
|
|
27
|
+
* npm (CLI, data plane d'administration, sonde de disponibilité, porte MCP) —
|
|
28
|
+
* quatre consommateurs pour un seul producteur. Les tenir à part évite qu'un
|
|
29
|
+
* import de l'applicateur tire les pilotes de base de données avec lui.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Nom de la table d'historique — **jamais qualifié d'un schéma**.
|
|
33
|
+
*
|
|
34
|
+
* Elle suit donc le `search_path` de la connexion, exactement comme les tables
|
|
35
|
+
* d'entités. Écrire `public.nodefony_migrations` exclurait à vie le patron
|
|
36
|
+
* PostgreSQL d'isolation par schéma sur une base mutualisée : déplacer ensuite
|
|
37
|
+
* la table d'historique chez un utilisateur serait une migration de données,
|
|
38
|
+
* pas un correctif.
|
|
39
|
+
*/
|
|
40
|
+
export declare const HISTORY_TABLE = "nodefony_migrations";
|
|
41
|
+
/**
|
|
42
|
+
* Marqueur de format que porte la première ligne de chaque fichier `.sql`.
|
|
43
|
+
*
|
|
44
|
+
* Le format de découpe est celui de drizzle-kit, adopté à vie ; un défaut
|
|
45
|
+
* découvert après publication ne pourrait plus être corrigé sans changer le
|
|
46
|
+
* SENS de fichiers déjà livrés. Le marqueur donne la porte de sortie : un
|
|
47
|
+
* fichier d'un autre format est **refusé en le nommant**, jamais lu au mieux.
|
|
48
|
+
*/
|
|
49
|
+
export declare const FORMAT_MARKER = "-- nodefony:migration format=1";
|
|
50
|
+
/** Séparateur de statements produit par drizzle-kit. */
|
|
51
|
+
export declare const STATEMENT_BREAKPOINT = "--> statement-breakpoint";
|
|
52
|
+
/**
|
|
53
|
+
* Codes de refus stables de l'applicateur.
|
|
54
|
+
*
|
|
55
|
+
* Ils sont le contrat lu par une machine — un agent lit `code`, jamais une
|
|
56
|
+
* phrase française. Ajouter un code est additif ; en changer un est une
|
|
57
|
+
* rupture, au même titre qu'un code de sortie.
|
|
58
|
+
*/
|
|
59
|
+
export type MigrationVerdictCode =
|
|
60
|
+
/** Historique vide alors que les tables du schéma existent déjà. */
|
|
61
|
+
"NF_MIGRATE_BASELINE_REQUIRED"
|
|
62
|
+
/** Une migration a échoué (ou n'a jamais fini) : réparer avant de reprendre. */
|
|
63
|
+
| "NF_MIGRATE_FAILED_MARKER"
|
|
64
|
+
/** Le fichier d'une migration appliquée a changé depuis son application. */
|
|
65
|
+
| "NF_MIGRATE_HASH_MISMATCH"
|
|
66
|
+
/** Une migration en attente se range AVANT la dernière appliquée de sa source. */
|
|
67
|
+
| "NF_MIGRATE_OUT_OF_ORDER"
|
|
68
|
+
/** Une migration appliquée n'a plus de fichier dans une source pourtant présente. */
|
|
69
|
+
| "NF_MIGRATE_MISSING_FILE"
|
|
70
|
+
/** Un fichier ne porte pas le format que cet applicateur sait lire. */
|
|
71
|
+
| "NF_MIGRATE_UNKNOWN_FORMAT"
|
|
72
|
+
/** Le verrou n'a pas pu être obtenu dans le délai imparti. */
|
|
73
|
+
| "NF_MIGRATE_LOCK_TIMEOUT"
|
|
74
|
+
/** Le tag demandé (`--up-to`) ne désigne aucune migration connue. */
|
|
75
|
+
| "NF_MIGRATE_UNKNOWN_TAG"
|
|
76
|
+
/** La source demandée (`--source`) n'est pas déclarée par cette application. */
|
|
77
|
+
| "NF_MIGRATE_UNKNOWN_SOURCE"
|
|
78
|
+
/** Le journal d'une source annonce un fichier que le dossier ne contient pas. */
|
|
79
|
+
| "NF_MIGRATE_JOURNAL_MISMATCH";
|
|
80
|
+
/** Commande à exécuter pour lever un refus — l'agent lit `nextActions[0]`. */
|
|
81
|
+
export interface IMigrationAction {
|
|
82
|
+
/** Commande complète, prête à copier. */
|
|
83
|
+
command: string;
|
|
84
|
+
/** Arguments, séparés pour un appel programmatique. */
|
|
85
|
+
args: string[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Verdict structuré : **la source**, dont la prose n'est qu'un rendu.
|
|
89
|
+
*
|
|
90
|
+
* Un producteur, quatre rendus (phrase de la CLI, `--json`, data plane,
|
|
91
|
+
* indication dans un corps d'erreur). Écrire le message pour l'humain ET un
|
|
92
|
+
* JSON pour la machine ferait deux implémentations d'une même règle, qui
|
|
93
|
+
* divergeraient.
|
|
94
|
+
*/
|
|
95
|
+
export interface IMigrationVerdict {
|
|
96
|
+
/** Code stable du refus. */
|
|
97
|
+
code: MigrationVerdictCode;
|
|
98
|
+
/** Connecteur concerné. */
|
|
99
|
+
connector: string;
|
|
100
|
+
/** Source de migrations concernée, quand le refus en désigne une. */
|
|
101
|
+
source?: string;
|
|
102
|
+
/** Tag concerné, quand le refus en désigne un. */
|
|
103
|
+
tag?: string;
|
|
104
|
+
/** Faits constatés, sans mise en forme — de quoi rendre la phrase. */
|
|
105
|
+
facts: Record<string, string | number | boolean | readonly string[]>;
|
|
106
|
+
/** Ce qu'il faut faire ensuite, du plus direct au plus assumé. */
|
|
107
|
+
nextActions: IMigrationAction[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Une source de migrations : un espace de noms OUVERT.
|
|
111
|
+
*
|
|
112
|
+
* `framework` et `app` sont deux valeurs **réservées**, pas une énumération :
|
|
113
|
+
* le registre d'entités est déjà ouvert à N modules, et un modèle à deux
|
|
114
|
+
* valeurs casserait APRÈS publication (un module tiers n'aurait d'autre choix
|
|
115
|
+
* que de se déverser dans `app`, et désinstaller un module bloquerait toute
|
|
116
|
+
* migration ultérieure, pour toujours).
|
|
117
|
+
*/
|
|
118
|
+
export interface IMigrationSource {
|
|
119
|
+
/** Nom logique, découplé du paquet qui livre le dossier. */
|
|
120
|
+
name: string;
|
|
121
|
+
/** Dossier RACINE des migrations ; le sous-dossier de dialecte est dérivé. */
|
|
122
|
+
dir: string;
|
|
123
|
+
/** Rang d'application : `framework` = 0, modules ensuite, `app` en dernier. */
|
|
124
|
+
rank: number;
|
|
125
|
+
}
|
|
126
|
+
/** Un fichier de migration chargé depuis une source. */
|
|
127
|
+
export interface IMigrationFile {
|
|
128
|
+
/** Source qui le livre. */
|
|
129
|
+
source: string;
|
|
130
|
+
/** Identité immuable du fichier (`0000_framework_init`). */
|
|
131
|
+
tag: string;
|
|
132
|
+
/** Rang dans le journal de SA source. */
|
|
133
|
+
idx: number;
|
|
134
|
+
/** Empreinte auto-descriptive `sha256:<hex>` du contenu normalisé. */
|
|
135
|
+
hash: string;
|
|
136
|
+
/** Statements découpés, dans l'ordre, sans les vides. */
|
|
137
|
+
statements: readonly string[];
|
|
138
|
+
/** Chemin du fichier — pour nommer un refus. */
|
|
139
|
+
path: string;
|
|
140
|
+
}
|
|
141
|
+
/** Une ligne de la table d'historique, colonnes lues NOMMÉMENT. */
|
|
142
|
+
export interface IAppliedMigration {
|
|
143
|
+
source: string;
|
|
144
|
+
tag: string;
|
|
145
|
+
hash: string;
|
|
146
|
+
runId: string;
|
|
147
|
+
startedAt: number;
|
|
148
|
+
finishedAt: number | null;
|
|
149
|
+
executionMs: number | null;
|
|
150
|
+
success: boolean;
|
|
151
|
+
error: string | null;
|
|
152
|
+
appliedBy: string | null;
|
|
153
|
+
}
|
|
154
|
+
/** Une dérive constatée : le fichier a changé après avoir été appliqué. */
|
|
155
|
+
export interface IMigrationDrift {
|
|
156
|
+
source: string;
|
|
157
|
+
tag: string;
|
|
158
|
+
/** Empreinte enregistrée au moment de l'application. */
|
|
159
|
+
expected: string;
|
|
160
|
+
/** Empreinte du fichier tel qu'il est aujourd'hui. */
|
|
161
|
+
actual: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* État complet, calculé en LECTURE SEULE.
|
|
165
|
+
*
|
|
166
|
+
* Même producteur pour la CLI, le data plane, la porte d'agent et la sonde de
|
|
167
|
+
* disponibilité — quatre consommateurs, une seule vérité.
|
|
168
|
+
*/
|
|
169
|
+
export interface IMigrationPlan {
|
|
170
|
+
connector: string;
|
|
171
|
+
dialect: SqlDialect;
|
|
172
|
+
/** Migrations appliquées avec succès, ordre d'application. */
|
|
173
|
+
applied: readonly IAppliedMigration[];
|
|
174
|
+
/** Migrations à appliquer, dans l'ordre (rang de source, puis journal). */
|
|
175
|
+
pending: readonly IMigrationFile[];
|
|
176
|
+
/** Fichiers modifiés après application. */
|
|
177
|
+
drifted: readonly IMigrationDrift[];
|
|
178
|
+
/** Marqueurs d'échec, ou migrations jamais terminées. */
|
|
179
|
+
failed: readonly IAppliedMigration[];
|
|
180
|
+
/** Appliquées en base mais sans fichier, dans une source PRÉSENTE. */
|
|
181
|
+
missing: readonly {
|
|
182
|
+
source: string;
|
|
183
|
+
tag: string;
|
|
184
|
+
}[];
|
|
185
|
+
/** Sources vues en base mais absentes du registre — ignorées, jamais bloquantes. */
|
|
186
|
+
ignoredSources: readonly string[];
|
|
187
|
+
/** Historique vide alors que les tables du schéma existent déjà. */
|
|
188
|
+
baselineRequired: boolean;
|
|
189
|
+
}
|
|
190
|
+
/** Résultat de l'application d'une migration. */
|
|
191
|
+
export interface IMigrationApplied {
|
|
192
|
+
source: string;
|
|
193
|
+
tag: string;
|
|
194
|
+
executionMs: number;
|
|
195
|
+
}
|
|
196
|
+
/** Résultat d'un `migrate()`. */
|
|
197
|
+
export interface IMigrationRun {
|
|
198
|
+
/** Identifiant du run — groupe les migrations d'un même déploiement. */
|
|
199
|
+
runId: string;
|
|
200
|
+
/** Migrations effectivement appliquées, dans l'ordre. */
|
|
201
|
+
applied: readonly IMigrationApplied[];
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Pilote de base de données de l'applicateur — **une connexion, pas un pool**.
|
|
205
|
+
*
|
|
206
|
+
* `pg_advisory_lock` et `GET_LOCK` sont des verrous de SESSION : un pool les
|
|
207
|
+
* rend inopérants (verrou pris sur une connexion, DDL exécuté sur une autre,
|
|
208
|
+
* libération sur une troisième). L'applicateur tient donc sa propre connexion,
|
|
209
|
+
* du verrou jusqu'à sa libération, avec les mêmes pilotes chargés en lazy que
|
|
210
|
+
* l'adapter — aucune dépendance nouvelle.
|
|
211
|
+
*/
|
|
212
|
+
export interface IMigrationDriver extends ISchemaReader {
|
|
213
|
+
/** Dialecte servi. */
|
|
214
|
+
readonly dialect: SqlDialect;
|
|
215
|
+
/**
|
|
216
|
+
* Le DDL de ce dialecte est-il transactionnel ?
|
|
217
|
+
*
|
|
218
|
+
* MySQL répond `false` : un `CREATE TABLE` y valide implicitement. C'est ce
|
|
219
|
+
* qui interdit toute reprise aveugle après un échec — la réparation tranche.
|
|
220
|
+
*/
|
|
221
|
+
readonly transactionalDdl: boolean;
|
|
222
|
+
/** Exécute un statement sans résultat. */
|
|
223
|
+
exec(sql: string): Promise<void>;
|
|
224
|
+
/** Exécute une requête paramétrée ; les paramètres s'écrivent `?`. */
|
|
225
|
+
query<T extends Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<T[]>;
|
|
226
|
+
/** Ouvre une transaction (`BEGIN IMMEDIATE` en sqlite). */
|
|
227
|
+
begin(): Promise<void>;
|
|
228
|
+
commit(): Promise<void>;
|
|
229
|
+
rollback(): Promise<void>;
|
|
230
|
+
/**
|
|
231
|
+
* Prend le verrou d'applicateur, ou lève au bout de `timeoutMs`.
|
|
232
|
+
*
|
|
233
|
+
* L'identité du verrou est un **contrat inter-versions** : deux versions du
|
|
234
|
+
* framework qui ne s'excluent plus, c'est pendant un déploiement que ça se
|
|
235
|
+
* paie — le seul moment qui compte.
|
|
236
|
+
*/
|
|
237
|
+
lock(timeoutMs: number): Promise<void>;
|
|
238
|
+
/** Libère le verrou. Sans effet s'il n'était pas tenu. */
|
|
239
|
+
unlock(): Promise<void>;
|
|
240
|
+
/** Ferme la connexion. */
|
|
241
|
+
close(): Promise<void>;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Le verrou d'applicateur n'a pas été obtenu dans le délai imparti.
|
|
245
|
+
*
|
|
246
|
+
* Une classe, et pas une `Error` nue, pour une raison précise : c'est ce qui
|
|
247
|
+
* permet à l'applicateur de la reconnaître et de la rendre sous le code
|
|
248
|
+
* `NF_MIGRATE_LOCK_TIMEOUT`, publié dans le contrat et lu par les
|
|
249
|
+
* orchestrateurs pour décider d'ATTENDRE puis de reprendre. Levée nue, elle
|
|
250
|
+
* tombait dans le fourre-tout des pannes : le code promis n'était jamais émis,
|
|
251
|
+
* et les branches qui l'attendaient — jusqu'au code de sortie — étaient
|
|
252
|
+
* inatteignables.
|
|
253
|
+
*
|
|
254
|
+
* Un verrou tenu n'est pas une panne : c'est le déploiement d'à côté qui
|
|
255
|
+
* travaille, et la réponse est d'attendre.
|
|
256
|
+
*/
|
|
257
|
+
export declare class MigrationLockTimeoutError extends Error {
|
|
258
|
+
readonly timeoutMs: number;
|
|
259
|
+
/**
|
|
260
|
+
* @param timeoutMs - délai d'attente écoulé.
|
|
261
|
+
* @param message - phrase française destinée à un humain.
|
|
262
|
+
*/
|
|
263
|
+
constructor(timeoutMs: number, message: string);
|
|
264
|
+
}
|
|
265
|
+
/** Erreur portant un {@link IMigrationVerdict} — le message n'est qu'un rendu. */
|
|
266
|
+
export declare class MigrationVerdictError extends Error {
|
|
267
|
+
readonly verdict: IMigrationVerdict;
|
|
268
|
+
/**
|
|
269
|
+
* @param verdict - verdict structuré, seule source de la décision.
|
|
270
|
+
* @param message - phrase française destinée à un humain.
|
|
271
|
+
*/
|
|
272
|
+
constructor(verdict: IMigrationVerdict, message: string);
|
|
273
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { Orm } from "@nodefony/orm-core";
|
|
2
|
+
import type { IOrmMigrationApplyReply, IOrmMigrationPlanReply, IOrmMigrationReply } from "@nodefony/orm-core";
|
|
3
|
+
import { type ISchemaComparison } from "../migrator/schemaDiff.js";
|
|
4
|
+
import type { IColumnInfo, IConnectionInfo, IOrmProbe, IRepository, ITransaction } from "@nodefony/orm-core";
|
|
5
|
+
import type { SqlDialect } from "../../interfaces/IDrizzleConfig.js";
|
|
6
|
+
/** Options de connexion de l'adapter Drizzle (driver selon `dialect`). */
|
|
7
|
+
export interface DrizzleOrmOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Dialecte SQL : `sqlite` (défaut, driver better-sqlite3) · `postgres` (driver
|
|
10
|
+
* `pg`, lazy) · `mysql` (driver `mysql2`, lazy). Sélectionne le client ET la
|
|
11
|
+
* variante de table que les entités enregistrent.
|
|
12
|
+
*/
|
|
13
|
+
dialect?: SqlDialect;
|
|
14
|
+
/** Fichier SQLite (`":memory:"` par défaut) — dialecte `sqlite` uniquement. */
|
|
15
|
+
filename?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Chaîne de connexion (`postgres://…`, `mysql://…`) — dialectes `postgres`/
|
|
18
|
+
* `mysql`. Requise pour ces dialectes.
|
|
19
|
+
*/
|
|
20
|
+
url?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Le schéma doit-il être DÉRIVÉ du code à la connexion ?
|
|
23
|
+
*
|
|
24
|
+
* `true` (défaut) : `CREATE TABLE IF NOT EXISTS` et les index déclarés sont
|
|
25
|
+
* émis à chaque connexion — c'est le confort du développement et l'usage
|
|
26
|
+
* direct en banc de test.
|
|
27
|
+
*
|
|
28
|
+
* `false` : la connexion ne touche PAS au schéma. C'est ce que veulent les
|
|
29
|
+
* modes `migrate` et `none` : le schéma y appartient aux migrations, et une
|
|
30
|
+
* création dérivée qui passerait par-dessus créerait précisément la
|
|
31
|
+
* divergence que les migrations existent pour empêcher — une table posée par
|
|
32
|
+
* le démarrage n'a aucune trace dans l'historique, donc plus personne ne sait
|
|
33
|
+
* d'où elle vient.
|
|
34
|
+
*/
|
|
35
|
+
deriveSchema?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Qui sait lire l'état des migrations de CE connecteur.
|
|
38
|
+
*
|
|
39
|
+
* Injecté par le service du module, seul à détenir la configuration (fichiers
|
|
40
|
+
* de migration, mode `ddl`, coordonnées) — l'ORM, lui, ne connaît que sa
|
|
41
|
+
* connexion. Sans lui, {@link DrizzleOrm.migrationStatus} répond que le
|
|
42
|
+
* connecteur n'est pas déclaré dans la configuration, ce qui est le cas d'un
|
|
43
|
+
* ORM construit à la main dans un banc.
|
|
44
|
+
*/
|
|
45
|
+
migrationStatus?: () => Promise<IOrmMigrationReply>;
|
|
46
|
+
/** Qui sait dire ce qui S'APPLIQUERAIT — même origine que `migrationStatus`. */
|
|
47
|
+
migrationPlan?: () => Promise<IOrmMigrationPlanReply>;
|
|
48
|
+
/** Qui sait APPLIQUER — refuse hors développement, en le disant. */
|
|
49
|
+
applyMigrations?: () => Promise<IOrmMigrationApplyReply>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Adapter Drizzle (driver `better-sqlite3`) **branché sur `@nodefony/orm-core`**
|
|
53
|
+
* — 3ᵉ adapter du banc multi-ORM (P7.4), choix SQL #1 moderne.
|
|
54
|
+
*
|
|
55
|
+
* Particularité vs les autres ORM : Drizzle est **schema-as-code** — il n'y
|
|
56
|
+
* a pas de « compilation » de modèle. `entity.schema` *est* déjà une table
|
|
57
|
+
* Drizzle (`sqliteTable(...)`). L'adapter :
|
|
58
|
+
* - dérive le DDL de chaque table via `getTableConfig()` et le crée (dev/test ;
|
|
59
|
+
* la prod passera par `drizzle-kit`) — pas de `sync()` natif côté Drizzle ;
|
|
60
|
+
* - résout les relations déclaratives ({@link IEntityRelation}) en métadonnées
|
|
61
|
+
* d'**eager-load manuel** (cf {@link DrizzleRepository}), pour rester générique
|
|
62
|
+
* sans imposer la couche `relations()` de Drizzle ;
|
|
63
|
+
* - pilote les transactions à la main (`BEGIN`/`COMMIT`/`ROLLBACK`) car
|
|
64
|
+
* `better-sqlite3` est synchrone (cf {@link DrizzleTransaction}).
|
|
65
|
+
*
|
|
66
|
+
* Trappe SQL brut : {@link DrizzleOrm.getNativeConnection} expose le db Drizzle
|
|
67
|
+
* (tag `sql`) pour les jointures arbitraires (ADR-0003 risque #1).
|
|
68
|
+
*/
|
|
69
|
+
export declare class DrizzleOrm extends Orm {
|
|
70
|
+
#private;
|
|
71
|
+
/**
|
|
72
|
+
* Ce que cet adapter sait de l'état de sa connexion, **par dialecte**.
|
|
73
|
+
*
|
|
74
|
+
* `postgres`/`mysql` traduisent les signaux de leur pool → `"events"`.
|
|
75
|
+
* `sqlite` est une base EMBARQUÉE : il n'y a ni serveur à perdre ni socket
|
|
76
|
+
* à surveiller, donc rien à constater — dire `"assumed"` n'est pas un aveu
|
|
77
|
+
* de faiblesse, c'est la description exacte de la situation.
|
|
78
|
+
*/
|
|
79
|
+
get liveness(): "events" | "assumed";
|
|
80
|
+
/**
|
|
81
|
+
* @param name - clé unique de l'ORM dans le `ormRegistry` (ex. `"db_test"`).
|
|
82
|
+
* @param options - options de connexion (`dialect`, `filename` sqlite, `url` pg).
|
|
83
|
+
*/
|
|
84
|
+
constructor(name: string, options?: DrizzleOrmOptions);
|
|
85
|
+
/**
|
|
86
|
+
* Le schéma est-il dérivé du code à la connexion, ou appartient-il aux
|
|
87
|
+
* migrations ?
|
|
88
|
+
*
|
|
89
|
+
* Se lire de l'extérieur a un usage précis : une brique qui s'apprête à
|
|
90
|
+
* interroger une table doit pouvoir dire si quelqu'un la crée. Quand la
|
|
91
|
+
* réponse est « non » et que la table ne figure dans aucune migration, un refus
|
|
92
|
+
* au démarrage vaut infiniment mieux qu'une erreur de colonne inconnue à la
|
|
93
|
+
* première requête d'un utilisateur.
|
|
94
|
+
*
|
|
95
|
+
* @returns `true` en mode `auto` (développement, test), `false` sinon.
|
|
96
|
+
*/
|
|
97
|
+
get derivesSchema(): boolean;
|
|
98
|
+
/** Dialecte SQL de ce connecteur. */
|
|
99
|
+
get dialect(): SqlDialect;
|
|
100
|
+
/**
|
|
101
|
+
* Écart constaté entre le schéma déclaré par le code et la base — `null`
|
|
102
|
+
* quand la base est conforme.
|
|
103
|
+
*
|
|
104
|
+
* Publié pour que trois surfaces disent la MÊME chose : le corps d'erreur
|
|
105
|
+
* rendu au client, la sonde de disponibilité, et l'écran d'administration.
|
|
106
|
+
* En mode dérivé il est renseigné au démarrage, après rattrapage de ce qui se
|
|
107
|
+
* rattrapait ; ailleurs il ne l'est que sur demande explicite
|
|
108
|
+
* ({@link DrizzleOrm.compareToDeclared}).
|
|
109
|
+
*/
|
|
110
|
+
get schemaDrift(): ISchemaComparison | null;
|
|
111
|
+
/**
|
|
112
|
+
* Compare la base à ce que le code déclare — sans jamais rien modifier.
|
|
113
|
+
*
|
|
114
|
+
* C'est la **troisième source** : les outils de migration croisent les
|
|
115
|
+
* fichiers et l'historique, et concluent « tout est appliqué » sans avoir
|
|
116
|
+
* regardé la base. Croiser le schéma réel rend visible l'incident qu'aucun
|
|
117
|
+
* d'eux ne voit — historique complet, rien en attente, et pourtant la base ne
|
|
118
|
+
* correspond pas au code (un `ALTER` passé à la main, un correctif jamais
|
|
119
|
+
* reporté, deux environnements qui ont divergé).
|
|
120
|
+
*
|
|
121
|
+
* La lecture passe par la connexion DÉJÀ ouverte de ce connecteur : en
|
|
122
|
+
* ouvrir une seconde sur `:memory:` désignerait une base vide et rendrait un
|
|
123
|
+
* verdict faux.
|
|
124
|
+
*
|
|
125
|
+
* @returns les écarts, séparés selon qu'ils se rattrapent ou non.
|
|
126
|
+
*/
|
|
127
|
+
compareToDeclared(): Promise<ISchemaComparison>;
|
|
128
|
+
/**
|
|
129
|
+
* Emplacement PHYSIQUE lisible de la base, pour l'écran Studio « Stores »
|
|
130
|
+
* (« où sont écrites mes données ? »). Fichier SQLite **relativisé** au cwd
|
|
131
|
+
* (anti info-leak, cf {@link DrizzleOrm.#safeTarget}) — `undefined` pour
|
|
132
|
+
* `:memory:` (volatil) et pour un backend RÉSEAU (postgres/mysql), dont
|
|
133
|
+
* l'emplacement EST l'infra déclarée, déjà surfacée à part (le Studio dérive
|
|
134
|
+
* alors « backend réseau — voir l'infra »). Stable dès la construction
|
|
135
|
+
* (`#filename` fixé au ctor, indépendant du connect).
|
|
136
|
+
*/
|
|
137
|
+
get location(): string | undefined;
|
|
138
|
+
protected onConnect(): Promise<void>;
|
|
139
|
+
disconnect(): Promise<void>;
|
|
140
|
+
getRepository<T = unknown>(name: string): IRepository<T>;
|
|
141
|
+
/**
|
|
142
|
+
* Exécute `work` dans une transaction, sur les TROIS dialectes : commit si la
|
|
143
|
+
* closure résout, rollback si elle rejette (cf {@link DrizzleTransaction}).
|
|
144
|
+
*
|
|
145
|
+
* Un repository n'entre dans la transaction que lié par `withTransaction(tx)` :
|
|
146
|
+
* en postgres/mysql, la transaction tient une connexion dédiée du pool, tandis
|
|
147
|
+
* que `getRepository()` écrit via le pool — donc hors transaction.
|
|
148
|
+
*
|
|
149
|
+
* @param work - travail transactionnel ; reçoit la transaction à lier aux repositories.
|
|
150
|
+
* @returns la valeur rendue par `work`.
|
|
151
|
+
* @throws `not connected` hors connexion ; sinon l'erreur de `work`, après rollback.
|
|
152
|
+
*/
|
|
153
|
+
transaction<R>(work: (tx: ITransaction) => Promise<R>): Promise<R>;
|
|
154
|
+
getNativeConnection<C = unknown>(): C;
|
|
155
|
+
/**
|
|
156
|
+
* État des migrations de ce connecteur — la MÊME charge utile que
|
|
157
|
+
* `orm:migrate:status --json`, jamais un second calcul.
|
|
158
|
+
*
|
|
159
|
+
* Le travail est fait par le lecteur injecté à la construction : lui seul
|
|
160
|
+
* détient la configuration (fichiers, mode `ddl`, coordonnées), quand cet
|
|
161
|
+
* objet ne connaît que sa connexion. Sans lecteur — un ORM construit à la
|
|
162
|
+
* main dans un banc —, la réponse dit exactement cela, et surtout PAS « ne
|
|
163
|
+
* porte pas de migrations » : ce serait faux d'un connecteur SQL, et un
|
|
164
|
+
* message faux publié est appris par les scripts qui le lisent.
|
|
165
|
+
*
|
|
166
|
+
* @returns l'état, ou l'empêchement qui explique pourquoi il n'y en a pas.
|
|
167
|
+
*/
|
|
168
|
+
migrationStatus(): Promise<IOrmMigrationReply>;
|
|
169
|
+
/**
|
|
170
|
+
* Ce qui S'APPLIQUERAIT, avec son SQL — lecture seule.
|
|
171
|
+
*
|
|
172
|
+
* @returns le plan, ou l'empêchement.
|
|
173
|
+
*/
|
|
174
|
+
migrationPlan(): Promise<IOrmMigrationPlanReply>;
|
|
175
|
+
/**
|
|
176
|
+
* Applique les migrations en attente — **développement seulement**, le
|
|
177
|
+
* lecteur injecté refuse ailleurs en le disant.
|
|
178
|
+
*
|
|
179
|
+
* @returns ce qui a été appliqué, ou l'empêchement.
|
|
180
|
+
*/
|
|
181
|
+
applyMigrations(): Promise<IOrmMigrationApplyReply>;
|
|
182
|
+
/**
|
|
183
|
+
* Ping bas-coût : `SELECT 1` — round-trip RÉEL vers la base, routé par
|
|
184
|
+
* dialecte (pool pg/mysql, client better-sqlite3 synchrone).
|
|
185
|
+
*
|
|
186
|
+
* @throws si le connecteur n'est pas connecté.
|
|
187
|
+
*/
|
|
188
|
+
ping(): Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* Sonde driver-spécifique, **routée par dialecte** — alimente le data plane
|
|
191
|
+
* admin (panneau Studio ORM) :
|
|
192
|
+
* - **sqlite** : stockage via PRAGMA (synchrone, bon marché) — taille
|
|
193
|
+
* (`page_count × page_size`), mode de journal (WAL ?), pages libres ;
|
|
194
|
+
* - **postgres / mysql** : état du **pool**, la métrique qui compte sur une
|
|
195
|
+
* base serveur (sa saturation est une falaise de débit) — lu sur des
|
|
196
|
+
* compteurs EN MÉMOIRE, donc sans requête réseau.
|
|
197
|
+
*
|
|
198
|
+
* Le stockage d'une base serveur n'est PAS sondé : il coûterait une requête
|
|
199
|
+
* (`pg_database_size`…) à chaque appel, pour une donnée que l'admin du SGBD
|
|
200
|
+
* expose déjà. Mieux vaut ne rien promettre que promettre en silence.
|
|
201
|
+
*
|
|
202
|
+
* Best-effort — `{}` seulement si le connecteur n'est pas connecté.
|
|
203
|
+
*
|
|
204
|
+
* @returns sonde `storage` (sqlite) ou `pool` (serveur), `{}` hors connexion.
|
|
205
|
+
*/
|
|
206
|
+
probe(): Promise<IOrmProbe>;
|
|
207
|
+
/**
|
|
208
|
+
* Colonnes normalisées d'une entité, dérivées du DDL Drizzle
|
|
209
|
+
* (`getTableConfig`) — alimente le graphe canonique / ERD / contexte IA.
|
|
210
|
+
*
|
|
211
|
+
* @param name - nom logique de l'entité.
|
|
212
|
+
* @returns colonnes (`[]` si l'entité n'est pas connue de cet ORM).
|
|
213
|
+
*/
|
|
214
|
+
describeEntity(name: string): IColumnInfo[];
|
|
215
|
+
/**
|
|
216
|
+
* Décrit le schéma ENTIER attendu par le code : une entrée par table.
|
|
217
|
+
*
|
|
218
|
+
* Même calcul que {@link DrizzleOrm.describeEntity}, à l'échelle du
|
|
219
|
+
* connecteur — et c'est le point : le rattrapage de colonnes au démarrage, le
|
|
220
|
+
* constat de divergence et l'écran d'administration comparent tous « ce que
|
|
221
|
+
* le code déclare » à « ce que la base contient ». Trois lecteurs, un seul
|
|
222
|
+
* producteur ; recopier ce parcours ailleurs le ferait diverger du jour où
|
|
223
|
+
* un dialecte s'ajoute.
|
|
224
|
+
*
|
|
225
|
+
* @returns une entrée par table, avec son NOM EN BASE (≠ nom d'entité).
|
|
226
|
+
*/
|
|
227
|
+
describeTables(): {
|
|
228
|
+
entity: string;
|
|
229
|
+
table: string;
|
|
230
|
+
columns: IColumnInfo[];
|
|
231
|
+
}[];
|
|
232
|
+
/**
|
|
233
|
+
* Décrit la connexion : driver `sqlite` (better-sqlite3) + cible (chemin du
|
|
234
|
+
* fichier, `:memory:` pour les tests). Aucun credential (SQLite = fichier local).
|
|
235
|
+
* Le chemin est **relativisé** à la racine du process : on ne fuite jamais
|
|
236
|
+
* l'arborescence absolue du serveur (home, structure FS) dans le data plane.
|
|
237
|
+
*
|
|
238
|
+
* @returns driver + cible (chemin relatif, basename si hors projet, ou `:memory:`).
|
|
239
|
+
*/
|
|
240
|
+
describeConnection(): IConnectionInfo;
|
|
241
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Column } from "drizzle-orm";
|
|
2
|
+
import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
|
|
3
|
+
import type { SQLiteTable } from "drizzle-orm/sqlite-core";
|
|
4
|
+
import type { PgTable } from "drizzle-orm/pg-core";
|
|
5
|
+
import type { MySqlTable } from "drizzle-orm/mysql-core";
|
|
6
|
+
import type { SqlDialect } from "../../interfaces/IDrizzleConfig.js";
|
|
7
|
+
import type { Criteria, IRepository, ITransaction, RepositoryReadOptions, UpdateData } from "@nodefony/orm-core";
|
|
8
|
+
/**
|
|
9
|
+
* Handle Drizzle (instance racine ou transaction) — schéma résolu côté adapter.
|
|
10
|
+
*
|
|
11
|
+
* Typage = **vue d'exécution CANONIQUE** (surface better-sqlite3) quel que soit
|
|
12
|
+
* le dialecte : `NodePgDatabase` expose la même surface builder pour les verbes
|
|
13
|
+
* du repository (`select`/`insert`/`update`/`delete`), et l'exactitude runtime
|
|
14
|
+
* est prouvée par les e2e PG (session/token/user/webauthn/totp/audit/webhook).
|
|
15
|
+
* La surface d'exécution native qui DIVERGE (`db.all` vs `db.execute().rows`)
|
|
16
|
+
* est routée par le `queryKit` — jamais ici.
|
|
17
|
+
*/
|
|
18
|
+
export type DrizzleDb = BetterSQLite3Database<Record<string, never>>;
|
|
19
|
+
/** Table Drizzle multi-dialecte (l'union honnête des variantes colKit). */
|
|
20
|
+
export type DrizzleTable = SQLiteTable | PgTable | MySqlTable;
|
|
21
|
+
/** Colonne Drizzle dialecte-agnostique (classe de base — acceptée par les
|
|
22
|
+
* opérateurs `eq`/`lt`/`asc`… et les fragments `sql`). */
|
|
23
|
+
export type DrizzleColumn = Column;
|
|
24
|
+
/**
|
|
25
|
+
* Vue d'exécution canonique (typage sqlite) d'une table multi-dialecte —
|
|
26
|
+
* **LE point unique** de conversion vers les builders (cf {@link DrizzleDb} :
|
|
27
|
+
* la surface builder est structurellement identique sqlite/pg, prouvée e2e).
|
|
28
|
+
* Consommé aussi par les stores à requêtes builder (`DrizzleAuditStore`).
|
|
29
|
+
*/
|
|
30
|
+
export declare function execTable(table: DrizzleTable): SQLiteTable;
|
|
31
|
+
/**
|
|
32
|
+
* Relation résolue au boot de l'ORM, prête pour l'eager-load manuel (sans la
|
|
33
|
+
* couche `relations()` de Drizzle, pour rester générique cross-entités).
|
|
34
|
+
*/
|
|
35
|
+
export interface DrizzleResolvedRelation {
|
|
36
|
+
/** Cardinalité. */
|
|
37
|
+
type: "one-to-many" | "many-to-one" | "one-to-one";
|
|
38
|
+
/** Table cible Drizzle (variante du dialecte du connecteur). */
|
|
39
|
+
targetTable: DrizzleTable;
|
|
40
|
+
/** Colonne clé étrangère (sur la cible pour 1-N, sur la source pour N-1). */
|
|
41
|
+
foreignKey: string;
|
|
42
|
+
/** Clé primaire de l'entité courante (côté parent du 1-N). */
|
|
43
|
+
localKey: string;
|
|
44
|
+
/** Clé primaire de la cible (côté lookup du N-1). */
|
|
45
|
+
targetKey: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Repository portable (contrat {@link IRepository}) au-dessus d'une table Drizzle
|
|
49
|
+
* + driver `better-sqlite3`.
|
|
50
|
+
*
|
|
51
|
+
* 3ᵉ adapter du banc orm-core (ADR-0003) : valide le contrat sur un ORM
|
|
52
|
+
* **type-safe-first** dont le `WHERE` est un *builder* d'expressions (pas un objet
|
|
53
|
+
* plat). Spécificités traduites ici :
|
|
54
|
+
* - critère portable → expressions Drizzle (`eq`/`and`/`gt`/`inArray`/`like`...),
|
|
55
|
+
* opérateurs riches inclus (résolution ADR-0003 risque #3) ;
|
|
56
|
+
* - **eager-load manuel** (`options.relations`) : une requête `IN (...)` par
|
|
57
|
+
* relation déclarée, puis regroupement en mémoire — portable sans déclarer la
|
|
58
|
+
* couche `relations()` de Drizzle ;
|
|
59
|
+
* - liaison transactionnelle via {@link DrizzleRepository.withTransaction} (le
|
|
60
|
+
* handle de transaction *est* un db Drizzle → réutilisé tel quel).
|
|
61
|
+
*
|
|
62
|
+
* @typeParam T - forme plate de l'entité gérée.
|
|
63
|
+
*/
|
|
64
|
+
export declare class DrizzleRepository<T = unknown> implements IRepository<T> {
|
|
65
|
+
#private;
|
|
66
|
+
/**
|
|
67
|
+
* @param db - handle Drizzle (instance racine ou transaction).
|
|
68
|
+
* @param table - table Drizzle de l'entité (variante du dialecte).
|
|
69
|
+
* @param relations - relations résolues (eager-load), indexées par champ.
|
|
70
|
+
* @param connector - nom de la connexion (clé du registre) — défaut `"default"`.
|
|
71
|
+
* @param dialect - dialecte SQL du connecteur — défaut `"sqlite"`.
|
|
72
|
+
* @param transactional - `true` quand `db` est un handle de transaction :
|
|
73
|
+
* l'instance est jetable (une par `withTransaction`) et, en pg, sa connexion
|
|
74
|
+
* dédiée est rendue au commit — préparer dessus coûterait sans jamais
|
|
75
|
+
* s'amortir. Les transactions gardent le chemin non préparé.
|
|
76
|
+
*/
|
|
77
|
+
constructor(db: DrizzleDb, table: DrizzleTable, relations: Record<string, DrizzleResolvedRelation>, connector?: string, dialect?: SqlDialect, transactional?: boolean);
|
|
78
|
+
find(criteria?: Criteria<T>, options?: RepositoryReadOptions): Promise<T[]>;
|
|
79
|
+
findOne(criteria: Criteria<T>, options?: RepositoryReadOptions): Promise<T | null>;
|
|
80
|
+
create(data: Partial<T>): Promise<T>;
|
|
81
|
+
createMany(data: Partial<T>[]): Promise<T[]>;
|
|
82
|
+
updateOne(criteria: Criteria<T>, data: Partial<T>): Promise<T | null>;
|
|
83
|
+
upsert(criteria: Criteria<T>, update: UpdateData<T>, insertOnly?: Partial<T>): Promise<T>;
|
|
84
|
+
updateMany(criteria: Criteria<T>, data: Partial<T>): Promise<number>;
|
|
85
|
+
increment(criteria: Criteria<T>, changes: Partial<Record<keyof T, number>>): Promise<T | null>;
|
|
86
|
+
delete(criteria: Criteria<T>): Promise<number>;
|
|
87
|
+
deleteOne(criteria: Criteria<T>): Promise<boolean>;
|
|
88
|
+
findOneAndDelete(criteria: Criteria<T>): Promise<T | null>;
|
|
89
|
+
count(criteria?: Criteria<T>): Promise<number>;
|
|
90
|
+
/**
|
|
91
|
+
* `COUNT(DISTINCT col)` natif — la déduplication reste dans le moteur, aucune
|
|
92
|
+
* ligne n'est rapatriée. `COUNT(DISTINCT …)` ignore les `NULL` sur les trois
|
|
93
|
+
* dialectes, ce qui donne au contrat sa sémantique sans clause supplémentaire.
|
|
94
|
+
*/
|
|
95
|
+
countDistinct(field: keyof T & string, criteria?: Criteria<T>): Promise<number>;
|
|
96
|
+
exists(criteria: Criteria<T>): Promise<boolean>;
|
|
97
|
+
withTransaction(tx: ITransaction): IRepository<T>;
|
|
98
|
+
}
|