@nodefony/drizzle 10.0.0-alpha.8 → 10.0.0-beta.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 (75) hide show
  1. package/README.md +19 -19
  2. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
  4. package/dist/index.js +9 -6
  5. package/dist/nodefony/command/migrateShared.js +17 -1
  6. package/dist/nodefony/command/orm-generate.js +14 -10
  7. package/dist/nodefony/command/orm-migrate-baseline.js +5 -2
  8. package/dist/nodefony/command/orm-reset.js +1 -0
  9. package/dist/nodefony/config/defineModuleConfig.js +23 -7
  10. package/dist/nodefony/entity/userTable.js +4 -1
  11. package/dist/nodefony/registerStores.js +4 -28
  12. package/dist/nodefony/service/DrizzleService.js +42 -16
  13. package/dist/nodefony/src/DrizzleAuditStore.js +14 -2
  14. package/dist/nodefony/src/DrizzleIdempotencyStore.js +14 -2
  15. package/dist/nodefony/src/DrizzleTokenStore.js +13 -1
  16. package/dist/nodefony/src/DrizzleTotpSecretStore.js +13 -1
  17. package/dist/nodefony/src/DrizzleWebAuthnCredentialStore.js +13 -1
  18. package/dist/nodefony/src/DrizzleWebhookStore.js +13 -1
  19. package/dist/nodefony/src/SessionStorage.js +8 -0
  20. package/dist/nodefony/src/connectorTarget.js +1 -1
  21. package/dist/nodefony/src/frameworkConnector.js +29 -0
  22. package/dist/nodefony/src/memoryDatabase.js +13 -0
  23. package/dist/nodefony/src/migrator/DrizzleMigrator.js +3 -3
  24. package/dist/nodefony/src/migrator/adopt.js +1 -1
  25. package/dist/nodefony/src/migrator/appSchema.js +44 -1
  26. package/dist/nodefony/src/migrator/destructive.js +2 -3
  27. package/dist/nodefony/src/migrator/drivers/mysqlDriver.js +1 -1
  28. package/dist/nodefony/src/migrator/drivers/postgresDriver.js +1 -1
  29. package/dist/nodefony/src/migrator/drivers/sqliteDriver.js +1 -0
  30. package/dist/nodefony/src/migrator/explain.js +7 -10
  31. package/dist/nodefony/src/migrator/history.js +25 -2
  32. package/dist/nodefony/src/migrator/kit.js +41 -6
  33. package/dist/nodefony/src/migrator/paths.js +55 -1
  34. package/dist/nodefony/src/migrator/rebuildCopy.js +6 -6
  35. package/dist/nodefony/src/migrator/refusals.js +50 -1
  36. package/dist/nodefony/src/migrator/resolve.js +59 -9
  37. package/dist/nodefony/src/migrator/sources.js +13 -8
  38. package/dist/nodefony/src/orm-core/DrizzleOrm.js +51 -30
  39. package/dist/nodefony/src/orm-core/DrizzleRepository.js +38 -26
  40. package/dist/nodefony/src/queryKit.js +5 -5
  41. package/dist/nodefony/src/safeTarget.js +1 -0
  42. package/dist/types/nodefony/command/migrateShared.d.ts +10 -0
  43. package/dist/types/nodefony/config/defineModuleConfig.d.ts +5 -1
  44. package/dist/types/nodefony/entity/userTable.d.ts +46 -3
  45. package/dist/types/nodefony/registerStores.d.ts +4 -3
  46. package/dist/types/nodefony/src/DrizzleAuditStore.d.ts +6 -0
  47. package/dist/types/nodefony/src/DrizzleIdempotencyStore.d.ts +8 -1
  48. package/dist/types/nodefony/src/DrizzleTokenStore.d.ts +6 -0
  49. package/dist/types/nodefony/src/DrizzleTotpSecretStore.d.ts +6 -0
  50. package/dist/types/nodefony/src/DrizzleUserRepository.d.ts +1 -1
  51. package/dist/types/nodefony/src/DrizzleWebAuthnCredentialStore.d.ts +6 -0
  52. package/dist/types/nodefony/src/DrizzleWebhookStore.d.ts +6 -0
  53. package/dist/types/nodefony/src/SessionStorage.d.ts +6 -0
  54. package/dist/types/nodefony/src/connectorTarget.d.ts +1 -1
  55. package/dist/types/nodefony/src/frameworkConnector.d.ts +24 -0
  56. package/dist/types/nodefony/src/memoryDatabase.d.ts +10 -0
  57. package/dist/types/nodefony/src/migrator/DrizzleMigrator.d.ts +4 -4
  58. package/dist/types/nodefony/src/migrator/appSchema.d.ts +25 -0
  59. package/dist/types/nodefony/src/migrator/destructive.d.ts +1 -1
  60. package/dist/types/nodefony/src/migrator/drivers/index.d.ts +2 -2
  61. package/dist/types/nodefony/src/migrator/explain.d.ts +9 -9
  62. package/dist/types/nodefony/src/migrator/kit.d.ts +15 -0
  63. package/dist/types/nodefony/src/migrator/name.d.ts +1 -1
  64. package/dist/types/nodefony/src/migrator/paths.d.ts +35 -0
  65. package/dist/types/nodefony/src/migrator/refusals.d.ts +36 -1
  66. package/dist/types/nodefony/src/migrator/resolve.d.ts +44 -1
  67. package/dist/types/nodefony/src/migrator/types.d.ts +2 -2
  68. package/dist/types/nodefony/src/orm-core/DrizzleOrm.d.ts +13 -2
  69. package/dist/types/nodefony/src/orm-core/DrizzleRepository.d.ts +1 -1
  70. package/dist/types/nodefony/src/orm-core/ddlPlan.d.ts +2 -2
  71. package/dist/types/nodefony/src/queryKit.d.ts +9 -9
  72. package/dist/types/nodefony/src/safeTarget.d.ts +2 -2
  73. package/docs/index.md +31 -28
  74. package/docs/migrations.md +39 -3
  75. package/package.json +19 -20
@@ -1,5 +1,7 @@
1
1
  import { MIGRATE_URL_ENV } from "./types.js";
2
2
  import { action } from "./explain.js";
3
+ import { FRAMEWORK_CONNECTOR } from "../frameworkConnector.js";
4
+ import { RESERVED_MIGRATION_DIRS } from "./paths.js";
3
5
  import { knownConnectors } from "./resolve.js";
4
6
  //#region nodefony/src/migrator/refusals.ts
5
7
  /**
@@ -79,6 +81,30 @@ function generationNeedsAnswer(label, replay) {
79
81
  };
80
82
  }
81
83
  /**
84
+ * Un fichier lu par l'outil de génération porte un `await` de premier niveau.
85
+ *
86
+ * Nodefony est ESM, et l'application démarre très bien avec ce fichier. Mais
87
+ * l'outil qui écrit les migrations (`drizzle-kit`) relit les fichiers de schéma
88
+ * HORS de la chaîne de l'application, en les transpilant en CommonJS — où un
89
+ * `await` de premier niveau n'existe pas. Sans ce refus, l'utilisateur recevait
90
+ * une pile d'appels d'esbuild qui ne nomme ni l'outil ni le remède.
91
+ *
92
+ * @param label - ce qui était généré, tel qu'on le cite à l'utilisateur.
93
+ * @param file - fichier fautif, tel que l'outil l'a nommé.
94
+ * @param line - ligne de l'`await`.
95
+ * @param replay - la commande à relancer une fois le fichier corrigé.
96
+ * @returns le refus, prêt pour la ligne de commande comme pour l'écran.
97
+ */
98
+ function generationTopLevelAwait(label, file, line, replay) {
99
+ return {
100
+ code: "NF_GENERATE_TOP_LEVEL_AWAIT",
101
+ summary: `${file}:${line} — un \`await\` de premier niveau empêche de générer ${label} ; rien n'a été écrit.`,
102
+ meaning: "L'application démarre avec ce fichier : elle est ESM. Mais l'outil qui écrit les migrations (drizzle-kit) relit les fichiers de schéma de son côté, en CommonJS, où un `await` de premier niveau n'existe pas. Un fichier d'entité DÉCLARE un schéma : ce qui se décide à l'exécution (choisir une entité selon l'infrastructure, importer un module à la demande) se place dans l'`index.ts` du module — `@entities([...])` accepte une liste calculée. La base n'est pas en cause : elle n'a pas été interrogée.",
103
+ nextActions: [action(replay)],
104
+ exitCode: 2
105
+ };
106
+ }
107
+ /**
82
108
  * L'outil de génération s'est arrêté, et ce qu'il a dit est remonté TEL QUEL.
83
109
  *
84
110
  * 🔴 Ne JAMAIS remplacer sa sortie par une hypothèse. Le fourre-tout des
@@ -176,6 +202,29 @@ function notConfigured(connector, driver) {
176
202
  };
177
203
  }
178
204
  /**
205
+ * Écrire une migration pour un connecteur secondaire dont le NOM est celui d'un
206
+ * dossier de dialecte — refusé.
207
+ *
208
+ * Un connecteur secondaire range ses migrations sous `migrations/<connecteur>/`,
209
+ * à côté des dossiers de dialecte du connecteur du framework. Nommé `postgres`,
210
+ * son dossier SERAIT celui des migrations PostgreSQL du framework : il les
211
+ * lirait comme les siennes, et les siennes seraient appliquées à la base du
212
+ * framework. On le refuse en nommant la collision, plutôt que de le découvrir
213
+ * au premier déploiement.
214
+ *
215
+ * @param connector - connecteur demandé.
216
+ * @returns le refus, prêt pour la ligne de commande comme pour l'écran.
217
+ */
218
+ function reservedConnectorName(connector) {
219
+ return {
220
+ code: "NF_MIGRATE_SECONDARY_CONNECTOR",
221
+ summary: `Le connecteur « ${connector} » porte le nom d'un dossier réservé (${RESERVED_MIGRATION_DIRS.join(", ")}) : ses migrations ne peuvent pas avoir de dossier propre. Rien n'a été écrit.`,
222
+ meaning: `Les migrations d'un connecteur secondaire vivent sous \`migrations/<connecteur>/<dialecte>\`, à côté des dossiers de dialecte du connecteur « ${FRAMEWORK_CONNECTOR} ». Le dossier \`migrations/${connector}\` est déjà l'un d'eux : les deux connecteurs y liraient les migrations de l'autre, et chacune serait appliquée à la mauvaise base. Renomme le connecteur dans \`connectors\` (par exemple « ${connector}_data ») : son nom ne sert qu'à le désigner.`,
223
+ nextActions: [action("nodefony inspect config --json")],
224
+ exitCode: 2
225
+ };
226
+ }
227
+ /**
179
228
  * Traduit une résolution qui n'est PAS `ready` en refus lisible.
180
229
  *
181
230
  * @param wanted - nom demandé par l'appelant.
@@ -222,4 +271,4 @@ function describeResolutionRefusal(wanted, resolution, config) {
222
271
  };
223
272
  }
224
273
  //#endregion
225
- export { MigrationToolError, describeResolutionRefusal, formatToolOutput, generationFailed, generationNeedsAnswer, generationToolMissing, introspectFailed, moduleAbsent, notConfigured };
274
+ export { MigrationToolError, describeResolutionRefusal, formatToolOutput, generationFailed, generationNeedsAnswer, generationToolMissing, generationTopLevelAwait, introspectFailed, moduleAbsent, notConfigured, reservedConnectorName };
@@ -1,6 +1,7 @@
1
1
  import { MIGRATE_URL_ENV } from "./types.js";
2
2
  import { resolveConnectorTarget } from "../connectorTarget.js";
3
- import { defaultMigrationSources } from "./paths.js";
3
+ import { MEMORY_DATABASE } from "../memoryDatabase.js";
4
+ import { connectorMigrationsDir, defaultMigrationSources } from "./paths.js";
4
5
  import { DrizzleMigrator } from "./DrizzleMigrator.js";
5
6
  import { parseDatabaseUrl, sqliteFilenameFromUrl } from "nodefony";
6
7
  import { ormRegistry } from "@nodefony/orm-core";
@@ -103,11 +104,20 @@ function generateApplyAllowed(env) {
103
104
  *
104
105
  * @param explicit - valeur écrite dans la configuration du connecteur, si elle l'est.
105
106
  * @param env - environnement constaté.
107
+ * 🔴 **Une base VOLATILE (`:memory:`) dérive toujours son schéma**, en
108
+ * production comprise. Rien d'autre ne peut la peupler : elle repart vide à
109
+ * chaque démarrage, et une migration appliquée ne survit pas au processus. En
110
+ * `none`, elle restait vide, le verdict de schéma tombait à `divergent` et la
111
+ * sonde de disponibilité retenait la mise en service (vécu en CI : le connecteur
112
+ * d'un module de banc, ouvert en production sous dérogation).
113
+ *
106
114
  * @param appHasMigrations - l'application versionne au moins une migration.
115
+ * @param volatile - la base est en mémoire (`:memory:`).
107
116
  * @returns le mode effectif.
108
117
  */
109
- function resolveDdlMode(explicit, env, appHasMigrations = false) {
118
+ function resolveDdlMode(explicit, env, appHasMigrations = false, volatile = false) {
110
119
  if (explicit) return explicit;
120
+ if (volatile) return "auto";
111
121
  if (env.runtime === "development" || env.nodeEnv === "test") return appHasMigrations && env.nodeEnv !== "test" ? "migrate" : "auto";
112
122
  return "none";
113
123
  }
@@ -142,6 +152,23 @@ function appVersionsMigrations(dir) {
142
152
  return false;
143
153
  }
144
154
  /**
155
+ * Des migrations versionnées CONCERNENT-elles ce connecteur ?
156
+ *
157
+ * C'est la question que pose la bascule `auto` → `migrate`, et elle n'est pas
158
+ * « l'application versionne-t-elle des migrations ? » : celles de la racine
159
+ * décrivent la base du framework. Posée telle quelle, elle faisait basculer en
160
+ * `migrate` un connecteur secondaire qui n'en recevra jamais une — sa base
161
+ * restait alors sans schéma, ni dérivé ni migré. On regarde donc le dossier
162
+ * DU connecteur ({@link connectorMigrationsDir}).
163
+ *
164
+ * @param connector - nom du connecteur.
165
+ * @param dir - dossier de migrations de l'application ({@link appMigrationsDir}).
166
+ * @returns `true` si le dossier propre au connecteur porte au moins un `.sql`.
167
+ */
168
+ function connectorVersionsMigrations(connector, dir) {
169
+ return appVersionsMigrations(connectorMigrationsDir(dir, connector));
170
+ }
171
+ /**
145
172
  * La conduite de la sonde de disponibilité face à un schéma en retard.
146
173
  *
147
174
  * En production, un exemplaire dont le schéma est en retard ne doit pas
@@ -158,6 +185,26 @@ function resolveCheckMode(explicit, env) {
158
185
  return env.runtime === "production" ? "fail" : "warn";
159
186
  }
160
187
  /**
188
+ * En schéma `auto`, faut-il ÉNONCER au démarrage les migrations enregistrées
189
+ * que personne n'a appliquées ?
190
+ *
191
+ * Oui en développement, sauf conduite `off` — c'est ce conseil qui évite de
192
+ * conclure à une base incohérente devant une colonne inconnue. Jamais sur une
193
+ * base `:memory:` : elle repart VIDE à chaque démarrage, son historique aussi,
194
+ * si bien que tout y paraît « à appliquer » à chaque fois — un avertissement
195
+ * permanent qui n'apprend rien (vécu : le connecteur de banc `mediasoup`,
196
+ * une fois déclaré, réclamait à chaque démarrage les migrations du framework
197
+ * et de l'application, qui ne le concernent pas).
198
+ *
199
+ * @param check - conduite résolue ({@link resolveCheckMode}).
200
+ * @param env - environnement constaté.
201
+ * @param filename - fichier sqlite du connecteur, s'il en a un.
202
+ * @returns `true` si le conseil doit être rendu.
203
+ */
204
+ function adviseMigrations(check, env, filename) {
205
+ return check !== "off" && env.runtime === "development" && filename !== ":memory:";
206
+ }
207
+ /**
161
208
  * Décrit ce qui porte un connecteur, pour le nommer dans un refus.
162
209
  *
163
210
  * Un message qui dit seulement « ce connecteur ne porte pas de migrations »
@@ -177,10 +224,10 @@ function describeOwner(name) {
177
224
  driver: ""
178
225
  };
179
226
  }
180
- const klass = orm?.constructor?.name;
227
+ const klass = orm.constructor?.name;
181
228
  let driver = "";
182
229
  try {
183
- driver = orm?.describeConnection?.().driver ?? "";
230
+ driver = orm.describeConnection?.().driver ?? "";
184
231
  } catch {
185
232
  driver = "";
186
233
  }
@@ -221,7 +268,7 @@ const SQL_DRIVERS = /* @__PURE__ */ new Set([
221
268
  */
222
269
  function knownConnectors(config) {
223
270
  const names = new Set(ormRegistry.list());
224
- for (const name of Object.keys(config.connectors ?? {})) names.add(name);
271
+ for (const name of Object.keys(config.connectors)) names.add(name);
225
272
  return [...names].sort();
226
273
  }
227
274
  /**
@@ -245,7 +292,7 @@ function knownConnectors(config) {
245
292
  * @returns la réponse, discriminée par `kind`.
246
293
  */
247
294
  function resolveConnector(connector, config, env, kernel, options = {}) {
248
- const declared = config.connectors?.[connector];
295
+ const declared = config.connectors[connector];
249
296
  if (!declared) {
250
297
  if (ormRegistry.has(connector)) {
251
298
  const owner = describeOwner(connector);
@@ -287,7 +334,7 @@ function resolveConnector(connector, config, env, kernel, options = {}) {
287
334
  url: fromMigrateUrl && dialect !== "sqlite" ? parsedUrl.url : base.url
288
335
  },
289
336
  fromMigrateUrl,
290
- ddl: resolveDdlMode(declared.ddl, env, appVersionsMigrations(appMigrationsDir(kernel, config.migrations.dir)))
337
+ ddl: resolveDdlMode(declared.ddl, env, connectorVersionsMigrations(connector, appMigrationsDir(kernel, config.migrations.dir)), declared.filename === MEMORY_DATABASE)
291
338
  };
292
339
  }
293
340
  /**
@@ -335,7 +382,10 @@ function appMigrationsDir(kernel, dir) {
335
382
  */
336
383
  async function buildMigrator(resolution, config, kernel) {
337
384
  const appDir = appMigrationsDir(kernel, config.migrations.dir);
338
- const sources = await defaultMigrationSources(appDir, { framework: config.frameworkEntities !== false });
385
+ const sources = await defaultMigrationSources(appDir, {
386
+ framework: config.frameworkEntities !== false,
387
+ connector: resolution.connector
388
+ });
339
389
  return new DrizzleMigrator({
340
390
  connector: resolution.connector,
341
391
  ...resolution.target,
@@ -348,4 +398,4 @@ function resolveDivergenceMode(config) {
348
398
  return config.migrations.divergence;
349
399
  }
350
400
  //#endregion
351
- export { MIGRATE_URL_ENV, appMigrationsDir, appVersionsMigrations, buildMigrator, generateApplyAllowed, knownConnectors, readMigrationEnv, resetAllowed, resolveCheckMode, resolveConnector, resolveDdlMode, resolveDivergenceMode };
401
+ export { MEMORY_DATABASE, MIGRATE_URL_ENV, adviseMigrations, appMigrationsDir, appVersionsMigrations, buildMigrator, connectorVersionsMigrations, generateApplyAllowed, knownConnectors, readMigrationEnv, resetAllowed, resolveCheckMode, resolveConnector, resolveDdlMode, resolveDivergenceMode };
@@ -71,7 +71,8 @@ async function readJournal(dir, source) {
71
71
  }
72
72
  let journal;
73
73
  try {
74
- journal = JSON.parse(raw);
74
+ const parsed = JSON.parse(raw);
75
+ journal = typeof parsed === "object" && parsed !== null ? parsed : {};
75
76
  } catch (e) {
76
77
  throw new MigrationVerdictError({
77
78
  code: "NF_MIGRATE_UNKNOWN_FORMAT",
@@ -91,21 +92,25 @@ async function readJournal(dir, source) {
91
92
  facts: { file },
92
93
  nextActions: []
93
94
  }, `Le journal des migrations « ${file} » ne porte pas de liste « entries » : ce n'est pas un journal que ce format sait lire.`);
94
- if (!SUPPORTED_JOURNAL_VERSIONS.includes(String(journal.version))) throw new MigrationVerdictError({
95
+ const version = String(journal.version);
96
+ if (!SUPPORTED_JOURNAL_VERSIONS.includes(version)) throw new MigrationVerdictError({
95
97
  code: "NF_MIGRATE_UNKNOWN_FORMAT",
96
98
  connector: "",
97
99
  source,
98
100
  facts: {
99
101
  file,
100
- journalVersion: String(journal.version),
102
+ journalVersion: version,
101
103
  supported: SUPPORTED_JOURNAL_VERSIONS
102
104
  },
103
105
  nextActions: [{
104
106
  command: "npm update @nodefony/drizzle",
105
107
  args: ["update", "@nodefony/drizzle"]
106
108
  }]
107
- }, `Le journal de migrations de la source « ${source} » est en version ${String(journal.version)}, que cette version du framework ne sait pas lire (reconnue : ${SUPPORTED_JOURNAL_VERSIONS.join(", ")}).`);
108
- return journal;
109
+ }, `Le journal de migrations de la source « ${source} » est en version ${version}, que cette version du framework ne sait pas lire (reconnue : ${SUPPORTED_JOURNAL_VERSIONS.join(", ")}).`);
110
+ return {
111
+ version,
112
+ entries: journal.entries
113
+ };
109
114
  }
110
115
  /**
111
116
  * Lit un fichier de migration, valide son format et découpe ses statements.
@@ -370,8 +375,8 @@ function scanLine(line, needle, grammar, curseur) {
370
375
  */
371
376
  function delimiteurDollar(line, debut) {
372
377
  let i = debut + 1;
373
- while (i < line.length && /[A-Za-z0-9_]/.test(line[i])) {
374
- if (i === debut + 1 && /[0-9]/.test(line[i])) return null;
378
+ while (i < line.length && /[A-Za-z0-9_]/.test(line.charAt(i))) {
379
+ if (i === debut + 1 && /[0-9]/.test(line.charAt(i))) return null;
375
380
  i += 1;
376
381
  }
377
382
  return line[i] === "$" ? line.slice(debut, i + 1) : null;
@@ -390,7 +395,7 @@ function delimiteurDollar(line, debut) {
390
395
  function createdTables(files) {
391
396
  const found = /* @__PURE__ */ new Set();
392
397
  const pattern = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"']?([A-Za-z0-9_$]+)[`"']?/gi;
393
- for (const file of files) for (const statement of file.statements) for (const match of statement.matchAll(pattern)) found.add(match[1]);
398
+ for (const file of files) for (const statement of file.statements) for (const [, table] of statement.matchAll(pattern)) if (table !== void 0) found.add(table);
394
399
  return [...found];
395
400
  }
396
401
  /**
@@ -1,4 +1,5 @@
1
1
  import { schemaReader, toDollarParams } from "../migrator/catalog.js";
2
+ import "../memoryDatabase.js";
2
3
  import { notConfigured } from "../migrator/refusals.js";
3
4
  import { additiveSql, compareSchema, hasGap } from "../migrator/schemaDiff.js";
4
5
  import { describeTargetSafely } from "../safeTarget.js";
@@ -182,7 +183,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
182
183
  * @param options - options de connexion (`dialect`, `filename` sqlite, `url` pg).
183
184
  */
184
185
  constructor(name, options = {}) {
185
- super(name);
186
+ super(name, options.container);
186
187
  this.#dialect = options.dialect ?? "sqlite";
187
188
  this.#deriveSchema = options.deriveSchema !== false;
188
189
  this.#filename = options.filename ?? ":memory:";
@@ -430,17 +431,18 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
430
431
  }
431
432
  async onConnect() {
432
433
  await this.#releasePrevious();
433
- this.#tables = Object.create(null);
434
+ const tables = Object.create(null);
435
+ this.#tables = tables;
434
436
  this.#relations = Object.create(null);
435
437
  const entities = this.#ownEntities();
436
438
  switch (this.#dialect) {
437
439
  case "sqlite":
438
- await this.#connectSqlite(entities);
440
+ await this.#connectSqlite(entities, tables);
439
441
  break;
440
442
  case "postgres":
441
- await this.#connectPostgres(entities);
443
+ await this.#connectPostgres(entities, tables);
442
444
  break;
443
- case "mysql": await this.#connectMysql(entities);
445
+ case "mysql": await this.#connectMysql(entities, tables);
444
446
  }
445
447
  if (this.#deriveSchema) {
446
448
  await this.#reconcileSchema();
@@ -460,7 +462,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
460
462
  * La méthode devient asynchrone pour cette seule raison ; le pilote, lui,
461
463
  * reste synchrone une fois chargé (`client.exec`, `client.pragma`).
462
464
  */
463
- async #connectSqlite(entities) {
465
+ async #connectSqlite(entities, tables) {
464
466
  let Sqlite;
465
467
  let sqliteDrizzle;
466
468
  try {
@@ -470,7 +472,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
470
472
  Sqlite = resolved;
471
473
  sqliteDrizzle = (await import("drizzle-orm/better-sqlite3")).drizzle;
472
474
  } catch (e) {
473
- throw new Error(`DrizzleOrm "${this.name}": the sqlite dialect needs the optional driver \`better-sqlite3\` (run \`npm i better-sqlite3\`). ${e.message}`, { cause: e });
475
+ throw new Error(`DrizzleOrm "${this.name}": the sqlite dialect needs the optional driver \`better-sqlite3\` (run \`npm i better-sqlite3\`). ` + e.message, { cause: e });
474
476
  }
475
477
  const client = new Sqlite(this.#filename);
476
478
  if (this.#filename !== ":memory:") {
@@ -505,7 +507,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
505
507
  for (const entity of entities) {
506
508
  this.#assertDialectTable(entity, SQLiteTable, "sqlite");
507
509
  const table = entity.schema;
508
- this.#tables[entity.name] = table;
510
+ tables[entity.name] = table;
509
511
  entity.model = table;
510
512
  }
511
513
  if (!this.#deriveSchema) return;
@@ -536,12 +538,12 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
536
538
  #rawQuery = async (sql, params = []) => {
537
539
  switch (this.#dialect) {
538
540
  case "sqlite": {
539
- const statement = this.#client.prepare(sql);
541
+ const statement = this.#sqliteClient().prepare(sql);
540
542
  return statement.reader ? statement.all(...params) : [];
541
543
  }
542
- case "postgres": return (await this.#pgPool.query(toDollarParams(sql), params)).rows;
544
+ case "postgres": return (await this.#pgClient().query(toDollarParams(sql), params)).rows;
543
545
  case "mysql": {
544
- const [rows] = await this.#mysqlPool.query(sql, params);
546
+ const [rows] = await this.#mysqlClient().query(sql, params);
545
547
  return Array.isArray(rows) ? rows : [];
546
548
  }
547
549
  }
@@ -554,13 +556,13 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
554
556
  async #rawExec(sql) {
555
557
  switch (this.#dialect) {
556
558
  case "sqlite":
557
- this.#client.exec(sql);
559
+ this.#sqliteClient().exec(sql);
558
560
  return;
559
561
  case "postgres":
560
- await this.#pgPool.query(sql);
562
+ await this.#pgClient().query(sql);
561
563
  return;
562
564
  case "mysql":
563
- await this.#mysqlPool.query(sql);
565
+ await this.#mysqlClient().query(sql);
564
566
  return;
565
567
  }
566
568
  }
@@ -639,20 +641,20 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
639
641
  case "sqlite": {
640
642
  const t = table;
641
643
  if (empechees.has(getTableConfig(t).name)) continue;
642
- for (const statement of this.#createIndexesSQL(t)) this.#client.exec(statement);
644
+ for (const statement of this.#createIndexesSQL(t)) this.#sqliteClient().exec(statement);
643
645
  break;
644
646
  }
645
647
  case "postgres": {
646
648
  const t = table;
647
649
  if (empechees.has(getTableConfig$1(t).name)) continue;
648
- for (const statement of this.#createIndexesPgSQL(t)) await this.#pgPool.query(statement);
650
+ for (const statement of this.#createIndexesPgSQL(t)) await this.#pgClient().query(statement);
649
651
  break;
650
652
  }
651
653
  case "mysql": {
652
654
  const t = table;
653
655
  if (empechees.has(getTableConfig$2(t).name)) continue;
654
656
  for (const statement of this.#createIndexesMysqlSQL(t)) try {
655
- await this.#mysqlPool.query(statement);
657
+ await this.#mysqlClient().query(statement);
656
658
  } catch (error) {
657
659
  if (error.code !== "ER_DUP_KEYNAME") throw error;
658
660
  }
@@ -676,7 +678,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
676
678
  * déploiement SQLite ne les tire jamais. Pool partagé ; DDL dérivé pour dev/test
677
679
  * (prod = drizzle-kit). Échec d'import → message actionnable (`npm i pg`).
678
680
  */
679
- async #connectPostgres(entities) {
681
+ async #connectPostgres(entities, tables) {
680
682
  if (!this.#url) throw new Error(`DrizzleOrm "${this.name}": dialect "postgres" requires a connection \`url\`.`);
681
683
  let PoolCtor;
682
684
  let pgDrizzle;
@@ -707,7 +709,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
707
709
  this.#pgPool = pool;
708
710
  this.#db = pgDrizzle(pool);
709
711
  try {
710
- await this.#finishPostgres(entities, pool, pgDrizzle);
712
+ await this.#finishPostgres(entities, tables, pool, pgDrizzle);
711
713
  } catch (e) {
712
714
  this.#unwireAll();
713
715
  await pool.end().catch(() => void 0);
@@ -718,11 +720,11 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
718
720
  }
719
721
  }
720
722
  /** Suite de la connexion postgres — isolée pour rendre `connect()` atomique. */
721
- async #finishPostgres(entities, pool, pgDrizzle) {
723
+ async #finishPostgres(entities, tables, pool, pgDrizzle) {
722
724
  this.#beginTx = async () => {
723
725
  const cx = await pool.connect();
724
726
  const puitsTx = (err) => {
725
- this.connectionLost(`pg (transaction) : ${err?.message ?? String(err)}`);
727
+ this.connectionLost(`pg (transaction) : ${err instanceof Error ? err.message : String(err)}`);
726
728
  };
727
729
  cx.on("error", puitsTx);
728
730
  /** Rend la connexion, en retirant d'abord NOTRE puits. */
@@ -747,7 +749,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
747
749
  for (const entity of entities) {
748
750
  this.#assertDialectTable(entity, PgTable, "postgres");
749
751
  const table = entity.schema;
750
- this.#tables[entity.name] = table;
752
+ tables[entity.name] = table;
751
753
  entity.model = table;
752
754
  }
753
755
  if (!this.#deriveSchema) return;
@@ -808,7 +810,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
808
810
  */
809
811
  #wirePgLifecycle(pool) {
810
812
  const onError = (err) => {
811
- this.connectionLost(`pg: ${err?.message ?? String(err)}`);
813
+ this.connectionLost(`pg: ${err instanceof Error ? err.message : String(err)}`);
812
814
  };
813
815
  const onConnect = () => {
814
816
  this.connectionRestored();
@@ -852,7 +854,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
852
854
  this.connectionRestored();
853
855
  cx.on("error", (err) => {
854
856
  if (this.#mysqlPool !== pool) return;
855
- this.connectionLost(`mysql: ${err?.message ?? String(err)}`);
857
+ this.connectionLost(`mysql: ${err instanceof Error ? err.message : String(err)}`);
856
858
  });
857
859
  cx.stream?.once("close", () => {
858
860
  if (this.#mysqlPool !== pool) return;
@@ -872,7 +874,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
872
874
  * `datetime(3)` (kind `dateMs` du colKit) sont écrites/relues en UTC — mêmes
873
875
  * instants que `timestamptz` PG, sans dépendre de la timezone du serveur.
874
876
  */
875
- async #connectMysql(entities) {
877
+ async #connectMysql(entities, tables) {
876
878
  if (!this.#url) throw new Error(`DrizzleOrm "${this.name}": dialect "mysql" requires a connection \`url\`.`);
877
879
  let pool;
878
880
  let mysqlDrizzle;
@@ -902,7 +904,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
902
904
  this.#mysqlPool = pool;
903
905
  this.#db = mysqlDrizzle(pool);
904
906
  try {
905
- await this.#finishMysql(entities, pool, mysqlDrizzle);
907
+ await this.#finishMysql(entities, tables, pool, mysqlDrizzle);
906
908
  } catch (e) {
907
909
  this.#unwireAll();
908
910
  await pool.end().catch(() => void 0);
@@ -913,7 +915,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
913
915
  }
914
916
  }
915
917
  /** Suite de la connexion mysql — isolée pour rendre `connect()` atomique. */
916
- async #finishMysql(entities, pool, mysqlDrizzle) {
918
+ async #finishMysql(entities, tables, pool, mysqlDrizzle) {
917
919
  this.#beginTx = async () => {
918
920
  const cx = await pool.getConnection();
919
921
  try {
@@ -936,7 +938,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
936
938
  for (const entity of entities) {
937
939
  this.#assertDialectTable(entity, MySqlTable, "mysql");
938
940
  const table = entity.schema;
939
- this.#tables[entity.name] = table;
941
+ tables[entity.name] = table;
940
942
  entity.model = table;
941
943
  }
942
944
  if (!this.#deriveSchema) return;
@@ -976,7 +978,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
976
978
  const table = this.#tables?.[name];
977
979
  if (!this.#db) throw new Error(`DrizzleOrm "${this.name}": non connecté — impossible de rendre un dépôt pour "${name}". Les tables ne sont résolues qu'à la connexion. Si ce run est une commande en ligne, elle n'a pas déclaré \`externalServices\` (CONSOLE_DATA_RUN_PROFILE).`);
978
980
  if (!table) throw new Error(`DrizzleOrm "${this.name}": no entity table registered under "${name}".`);
979
- if (this.#repositories === null) this.#repositories = Object.create(null);
981
+ this.#repositories ??= Object.create(null);
980
982
  let repository = this.#repositories[name];
981
983
  if (repository === void 0) {
982
984
  repository = new DrizzleRepository(this.#db, table, this.#relations?.[name] ?? {}, this.name, this.#dialect);
@@ -1009,6 +1011,25 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
1009
1011
  throw error;
1010
1012
  }
1011
1013
  }
1014
+ /**
1015
+ * Connexion SQLite courante — lève si le connecteur n'est pas ouvert. Les
1016
+ * appelants ne passent ici qu'une fois `onConnect` engagé : la levée ne sert
1017
+ * qu'à typer, et à dire la cause si l'ordre était un jour rompu.
1018
+ */
1019
+ #sqliteClient() {
1020
+ if (this.#client === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
1021
+ return this.#client;
1022
+ }
1023
+ /** Pool PostgreSQL courant — même contrat que {@link #sqliteClient}. */
1024
+ #pgClient() {
1025
+ if (this.#pgPool === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
1026
+ return this.#pgPool;
1027
+ }
1028
+ /** Pool MySQL courant — même contrat que {@link #sqliteClient}. */
1029
+ #mysqlClient() {
1030
+ if (this.#mysqlPool === null) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
1031
+ return this.#mysqlPool;
1032
+ }
1012
1033
  getNativeConnection() {
1013
1034
  if (!this.#db) throw new Error(`DrizzleOrm "${this.name}": not connected.`);
1014
1035
  return this.#db;
@@ -1122,7 +1143,7 @@ var DrizzleOrm = class DrizzleOrm extends Orm {
1122
1143
  * @returns sonde `pool` (taille max, idle, empruntées, en attente).
1123
1144
  */
1124
1145
  #probePgPool(pool) {
1125
- const max = pool.options?.max;
1146
+ const max = pool.options.max;
1126
1147
  return { pool: {
1127
1148
  size: typeof max === "number" ? max : 10,
1128
1149
  available: pool.idleCount,