@zerotal/orm 1.8.1 → 1.9.0

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.
@@ -85,6 +85,8 @@ function _ctxPath(ctx: object): string {
85
85
  /**
86
86
  * Subscribe the ORM's events to every installed observer. Returns a disposer that
87
87
  * removes every subscription; call it from the ORM provider's `onStopping()`.
88
+ *
89
+ * @internal
88
90
  */
89
91
  export function installOrmObservability(app: Application): () => void {
90
92
  const unsubs: Array<() => void> = [];
@@ -1,6 +1,6 @@
1
1
  import type { SQLInstance } from "../db/sql-types.ts";
2
2
  import { ServiceProvider, registerErrorDiagnoser, isProdLike, deployEnv } from "@zerotal/core";
3
- import type { AppEnvironment } from "@zerotal/core";
3
+ import type { AppEnvironment, DoctorCheck } from "@zerotal/core";
4
4
  import type { ConfigManager } from "@zerotal/core/config";
5
5
  import { SQL } from "bun";
6
6
  import { DB, _getConnection } from "../db/DB.ts";
@@ -18,6 +18,7 @@ import { autoMigrateConcern } from "../schema/autoMigrate.ts";
18
18
  import { registerImplicitBinding } from "../implicitBinding.ts";
19
19
  import { installOrmObservability } from "../observability.ts";
20
20
  import { diagnoseMissingRelation } from "../diagnostics/missingRelation.ts";
21
+ import { pendingMigrationsCheck } from "../diagnostics/pendingMigrationsCheck.ts";
21
22
  import {
22
23
  registerRunMigrationsEndpoint,
23
24
  RUN_MIGRATIONS_PATH,
@@ -207,6 +208,15 @@ export class DatabaseProvider extends ServiceProvider {
207
208
  }
208
209
  }
209
210
 
211
+ /**
212
+ * The overlay answers "why did this fail" after something already has. This asks
213
+ * the same question before anything breaks, which is the cheaper moment to hear
214
+ * it — `doctor` already boots the app and already holds the connection.
215
+ */
216
+ override doctorChecks(): DoctorCheck[] {
217
+ return [pendingMigrationsCheck];
218
+ }
219
+
210
220
  override async onBooted(): Promise<void> {
211
221
  // Forward the ORM's framework events to whatever observers are installed.
212
222
  this._disposeObservability = installOrmObservability(this.app);
@@ -256,6 +266,12 @@ export class DatabaseProvider extends ServiceProvider {
256
266
  runner.registerLazy("db:seed", () =>
257
267
  import("../commands/DbSeedCommand.ts").then((m) => m.DbSeedCommand),
258
268
  );
269
+ // The counterpart to every command above it. `migrate`, `migrate:fresh` and
270
+ // `db:seed` all assume the file will still be there; this is the one that
271
+ // makes that assumption survivable.
272
+ runner.registerLazy("db:backup", () =>
273
+ import("../commands/DbBackupCommand.ts").then((m) => m.DbBackupCommand),
274
+ );
259
275
  runner.registerLazy("make:seeder", () =>
260
276
  import("../commands/MakeSeederCommand.ts").then((m) => m.MakeSeederCommand),
261
277
  );
@@ -111,6 +111,8 @@ function alterTableBlocks(newColumns: NewColumn[]): string {
111
111
  * - Creates new tables with `Schema.create()`
112
112
  * - Adds new columns with `Schema.table()` (ALTER TABLE ADD COLUMN)
113
113
  * - Drops created tables in `down()` (column additions are left for manual rollback)
114
+ *
115
+ * @internal
114
116
  */
115
117
  export function generateMigrationContent(className: string, diff: DiffResult): string {
116
118
  const upParts: string[] = [];
@@ -7,6 +7,7 @@ import type { ClassRef } from "../support/classRef.ts";
7
7
 
8
8
  // ── Model schema descriptor ───────────────────────────────────────────────────
9
9
 
10
+ /** @internal */
10
11
  export interface ModelColumn {
11
12
  name: string;
12
13
  type: ColumnOptions["type"]; // 'string' | 'text' | 'number' | 'boolean' | 'datetime' | 'json'
@@ -19,6 +20,7 @@ export interface ModelColumn {
19
20
  index?: boolean;
20
21
  }
21
22
 
23
+ /** @internal */
22
24
  export interface ModelSchema {
23
25
  table: string;
24
26
  primaryKey: string;
@@ -109,6 +111,8 @@ function encryptedColumns(
109
111
  * by child classes, matching normal TypeScript class semantics.
110
112
  *
111
113
  * Used by `migrate:generate` to compare model intent against the live DB.
114
+ *
115
+ * @internal
112
116
  */
113
117
  export const ModelInspector = {
114
118
  /**
@@ -3,20 +3,24 @@ import { columnDbName, type ModelSchema, type ModelColumn } from "./ModelInspect
3
3
 
4
4
  // -- Diff result types ---------------------------------------------------------
5
5
 
6
+ /** @internal */
6
7
  export interface NewTable {
7
8
  schema: ModelSchema;
8
9
  }
9
10
 
11
+ /** @internal */
10
12
  export interface NewColumn {
11
13
  table: string;
12
14
  column: ModelColumn;
13
15
  }
14
16
 
17
+ /** @internal */
15
18
  export interface DroppedColumn {
16
19
  table: string;
17
20
  column: string;
18
21
  }
19
22
 
23
+ /** @internal */
20
24
  export interface DiffResult {
21
25
  newTables: NewTable[];
22
26
  newColumns: NewColumn[];
@@ -36,6 +40,8 @@ export interface DiffResult {
36
40
  *
37
41
  * Additive deltas (newTables, newColumns) are always safe to apply. Dropped
38
42
  * columns are reported separately and only applied by a disruptive synchronize.
43
+ *
44
+ * @internal
39
45
  */
40
46
  export const SchemaDiffer = {
41
47
  async diff(schemas: ModelSchema[]): Promise<DiffResult> {
@@ -3,6 +3,7 @@ import { _getDialect } from "../model/BaseModel.ts";
3
3
 
4
4
  // ── DB column descriptor (normalised across dialects) ─────────────────────────
5
5
 
6
+ /** @internal */
6
7
  export interface LiveColumn {
7
8
  name: string;
8
9
  rawType: string; // original SQL type string from the DB (uppercase)
@@ -10,6 +11,7 @@ export interface LiveColumn {
10
11
  primary: boolean;
11
12
  }
12
13
 
14
+ /** @internal */
13
15
  export interface LiveTable {
14
16
  name: string;
15
17
  columns: LiveColumn[];
@@ -38,6 +40,8 @@ async function queryParam<T>(sql: string, value: unknown): Promise<T[]> {
38
40
  /**
39
41
  * Queries the live database to enumerate tables and their column definitions.
40
42
  * Used by `migrate:generate` to compute what has changed since the last migration.
43
+ *
44
+ * @internal
41
45
  */
42
46
  export const SchemaInspector = {
43
47
  /** Return all user-defined table names in the current DB. */
@@ -41,7 +41,11 @@ function applyColumn(table: TableBuilder, col: ModelColumn): void {
41
41
  else if (col.index) table.index(dbName);
42
42
  }
43
43
 
44
- /** Options for {@link synchronizeSchema}. */
44
+ /**
45
+ * Options for {@link synchronizeSchema}.
46
+ *
47
+ * @internal
48
+ */
45
49
  export interface SynchronizeOptions {
46
50
  /**
47
51
  * When true, also DROP columns that exist in the database but are no longer
@@ -61,6 +65,8 @@ export interface SynchronizeOptions {
61
65
  *
62
66
  * Returns the diff that was applied (additive deltas are empty when already in sync;
63
67
  * `droppedColumns` is populated but only acted on when `disruptive` is set).
68
+ *
69
+ * @internal
64
70
  */
65
71
  export async function synchronizeSchema(options: SynchronizeOptions = {}): Promise<DiffResult> {
66
72
  const diff = await SchemaDiffer.diff(ModelInspector.all());
@@ -112,7 +118,11 @@ export async function synchronizeSchema(options: SynchronizeOptions = {}): Promi
112
118
  return diff;
113
119
  }
114
120
 
115
- /** Resolved, normalised form of the `database.synchronize` config value. */
121
+ /**
122
+ * Resolved, normalised form of the `database.synchronize` config value.
123
+ *
124
+ * @internal
125
+ */
116
126
  export interface ResolvedSyncOptions {
117
127
  enabled: boolean;
118
128
  disruptive: boolean;
@@ -124,6 +134,8 @@ export interface ResolvedSyncOptions {
124
134
  * false / undefined -> { enabled: false, disruptive: false }
125
135
  * true -> { enabled: true, disruptive: false } (additive)
126
136
  * { enabled, disruptive? } -> as given (enabled defaults true when the object is present)
137
+ *
138
+ * @internal
127
139
  */
128
140
  export function resolveSyncOptions(raw: unknown): ResolvedSyncOptions {
129
141
  if (raw === true) return { enabled: true, disruptive: false };