@zerotal/orm 1.8.1 → 1.10.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.
- package/CHANGELOG.md +45 -0
- package/api-surface.md +34 -220
- package/package.json +3 -3
- package/src/commands/DbBackupCommand.ts +143 -0
- package/src/commands/_backup.ts +356 -0
- package/src/config.ts +10 -8
- package/src/db/DB.ts +27 -2
- package/src/db/NPlusOneDetector.ts +2 -2
- package/src/db/ReadWriteRouter.ts +2 -0
- package/src/db/dialects/MysqlDialect.ts +2 -0
- package/src/db/dialects/PostgresDialect.ts +2 -0
- package/src/db/dialects/SqliteDialect.ts +2 -0
- package/src/db/dialects/index.ts +2 -0
- package/src/db/dialects/types.ts +12 -2
- package/src/db/resolver.ts +4 -0
- package/src/db/types.ts +5 -5
- package/src/diagnostics/pendingMigrationsCheck.ts +60 -0
- package/src/implicitBinding.ts +1 -0
- package/src/model/BaseModel.ts +6 -0
- package/src/model/ModelQueryBuilder.ts +1 -0
- package/src/model/OrmContext.ts +14 -2
- package/src/model/decorators/belongsTo.ts +2 -2
- package/src/model/decorators/column.ts +8 -7
- package/src/model/decorators/hasMany.ts +1 -1
- package/src/model/decorators/hasManyThrough.ts +2 -2
- package/src/model/decorators/hasOne.ts +1 -1
- package/src/model/decorators/manyToMany.ts +4 -4
- package/src/model/decorators/morphMany.ts +1 -1
- package/src/model/decorators/morphOne.ts +1 -1
- package/src/model/decorators/morphTo.ts +2 -2
- package/src/model/decorators/morphToMany.ts +3 -3
- package/src/model/decorators/morphedByMany.ts +3 -3
- package/src/model/decorators/table.ts +2 -2
- package/src/model/hooks/HookRegistry.ts +2 -0
- package/src/observability.ts +2 -0
- package/src/provider/DatabaseProvider.ts +17 -1
- package/src/schema/MigrationCodegen.ts +2 -0
- package/src/schema/ModelInspector.ts +4 -0
- package/src/schema/SchemaDiffer.ts +6 -0
- package/src/schema/SchemaInspector.ts +4 -0
- package/src/schema/autoMigrate.ts +15 -3
package/src/model/BaseModel.ts
CHANGED
|
@@ -107,6 +107,8 @@ HookRegistry.onAfterRun = (ModelClass, hook, model): void => {
|
|
|
107
107
|
/**
|
|
108
108
|
* Register a named connection that models can select via `static connection`.
|
|
109
109
|
* Stored on the current OrmContext (execution-scoped), not a global.
|
|
110
|
+
*
|
|
111
|
+
* @internal
|
|
110
112
|
*/
|
|
111
113
|
export function registerModelConnection(name: string, conn: SQLInstance, dialect?: Dialect): void {
|
|
112
114
|
currentOrmContext().namedConnections.set(name, conn);
|
|
@@ -159,6 +161,8 @@ export function _getModelConnection(): SQLInstance {
|
|
|
159
161
|
* multi-database strategy: it returns the active tenant's connection so every model
|
|
160
162
|
* query transparently hits the right database. Returns `null` to defer to the normal
|
|
161
163
|
* resolution. Defaults to a no-op, so it changes nothing until something registers it.
|
|
164
|
+
*
|
|
165
|
+
* @internal
|
|
162
166
|
*/
|
|
163
167
|
export type ContextConnectionResolver = (ModelClass?: typeof BaseModel) => SQLInstance | null;
|
|
164
168
|
let _contextConnectionResolver: ContextConnectionResolver | null = null;
|
|
@@ -167,6 +171,8 @@ let _contextConnectionResolver: ContextConnectionResolver | null = null;
|
|
|
167
171
|
* Register a context-aware connection resolver (pass `null` to clear). Consulted by
|
|
168
172
|
* `_resolveConn` after explicit transactions and `static connection` named bindings,
|
|
169
173
|
* but before the default connection — so transactions and pinned connections still win.
|
|
174
|
+
*
|
|
175
|
+
* @internal
|
|
170
176
|
*/
|
|
171
177
|
export function registerConnectionResolver(fn: ContextConnectionResolver | null): void {
|
|
172
178
|
_contextConnectionResolver = fn;
|
|
@@ -275,6 +275,7 @@ function _createPivotCollection<T extends BaseModel>(
|
|
|
275
275
|
// BaseModel as a type only — no cycle.
|
|
276
276
|
|
|
277
277
|
export type GlobalScopeCallback = (qb: ModelQueryBuilder<BaseModel>) => void;
|
|
278
|
+
/** @internal */
|
|
278
279
|
export function _globalScopeRegistry(): Map<ClassRef, Map<string, GlobalScopeCallback>> {
|
|
279
280
|
return currentOrmContext().globalScopes as unknown as Map<
|
|
280
281
|
ClassRef,
|
package/src/model/OrmContext.ts
CHANGED
|
@@ -18,6 +18,8 @@ import type { SQLInstance } from "../db/sql-types.ts";
|
|
|
18
18
|
* } finally {
|
|
19
19
|
* useOrmContext(prev);
|
|
20
20
|
* }
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
21
23
|
*/
|
|
22
24
|
export class OrmContext {
|
|
23
25
|
/** Explicit connection override that wins over the resolved default (used by tests / `withDatabase`). */
|
|
@@ -34,7 +36,11 @@ export class OrmContext {
|
|
|
34
36
|
|
|
35
37
|
let _ctx = new OrmContext();
|
|
36
38
|
|
|
37
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Return the currently active {@link OrmContext}.
|
|
41
|
+
*
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
38
44
|
export function currentOrmContext(): OrmContext {
|
|
39
45
|
return _ctx;
|
|
40
46
|
}
|
|
@@ -45,6 +51,8 @@ export function currentOrmContext(): OrmContext {
|
|
|
45
51
|
*
|
|
46
52
|
* @param ctx The context to activate (defaults to a new, empty context).
|
|
47
53
|
* @returns The context that was active before this call.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
48
56
|
*/
|
|
49
57
|
export function useOrmContext(ctx: OrmContext = new OrmContext()): OrmContext {
|
|
50
58
|
const prev = _ctx;
|
|
@@ -52,7 +60,11 @@ export function useOrmContext(ctx: OrmContext = new OrmContext()): OrmContext {
|
|
|
52
60
|
return prev;
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Replace the active context with a fresh, empty {@link OrmContext} (test/teardown reset).
|
|
65
|
+
*
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
56
68
|
export function resetOrmContext(): void {
|
|
57
69
|
_ctx = new OrmContext();
|
|
58
70
|
}
|
|
@@ -5,9 +5,9 @@ export interface BelongsToOptions {
|
|
|
5
5
|
/** FK column on this (child) model pointing to the related row (e.g. `user_id`). */
|
|
6
6
|
foreignKey: string;
|
|
7
7
|
/** Key on the related model the FK references. Defaults to `'id'`. */
|
|
8
|
-
localKey?: string;
|
|
8
|
+
localKey?: string | undefined;
|
|
9
9
|
/** Return a default (unsaved) related model instead of null when absent. */
|
|
10
|
-
withDefault?: boolean | Record<string, unknown> | ((model: unknown) => void);
|
|
10
|
+
withDefault?: boolean | Record<string, unknown> | ((model: unknown) => void) | undefined;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -82,11 +82,11 @@ export interface ColumnOptions {
|
|
|
82
82
|
*
|
|
83
83
|
* @default "string"
|
|
84
84
|
*/
|
|
85
|
-
type?: "string" | "text" | "number" | "boolean" | "datetime" | "json";
|
|
85
|
+
type?: "string" | "text" | "number" | "boolean" | "datetime" | "json" | undefined;
|
|
86
86
|
/** Mark this column as the table's primary key. */
|
|
87
|
-
primary?: boolean;
|
|
87
|
+
primary?: boolean | undefined;
|
|
88
88
|
/** Allow SQL `NULL` for this column. */
|
|
89
|
-
nullable?: boolean;
|
|
89
|
+
nullable?: boolean | undefined;
|
|
90
90
|
/** Default value applied when none is provided. */
|
|
91
91
|
default?: unknown;
|
|
92
92
|
/**
|
|
@@ -96,9 +96,9 @@ export interface ColumnOptions {
|
|
|
96
96
|
* usually a correctness property (a webhook idempotency key, an invoice number),
|
|
97
97
|
* and `migrate:generate` can only emit constraints it can see declared.
|
|
98
98
|
*/
|
|
99
|
-
unique?: boolean;
|
|
99
|
+
unique?: boolean | undefined;
|
|
100
100
|
/** Add a plain (non-unique) index on this column. */
|
|
101
|
-
index?: boolean;
|
|
101
|
+
index?: boolean | undefined;
|
|
102
102
|
/**
|
|
103
103
|
* Shorthand cast types automatically serialize/deserialize the column value.
|
|
104
104
|
* Can also be a custom object with `get`/`set` functions for full control.
|
|
@@ -134,9 +134,10 @@ export interface ColumnOptions {
|
|
|
134
134
|
get?: (dbValue: unknown) => unknown;
|
|
135
135
|
set?: (jsValue: unknown) => unknown;
|
|
136
136
|
}
|
|
137
|
-
| CastContract<unknown
|
|
137
|
+
| CastContract<unknown>
|
|
138
|
+
| undefined;
|
|
138
139
|
/** Enum object (e.g. the imported TS enum) used alongside cast: 'enum'. */
|
|
139
|
-
enumValues?: Record<string, string | number
|
|
140
|
+
enumValues?: Record<string, string | number> | undefined;
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
/**
|
|
@@ -5,7 +5,7 @@ export interface HasManyOptions {
|
|
|
5
5
|
/** FK column on the related table pointing back to the parent (e.g. `user_id`). */
|
|
6
6
|
foreignKey: string;
|
|
7
7
|
/** Parent local key the FK references. Defaults to `'id'`. */
|
|
8
|
-
localKey?: string;
|
|
8
|
+
localKey?: string | undefined;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -6,9 +6,9 @@ export interface HasManyThroughOptions {
|
|
|
6
6
|
/** FK on the related table referencing the through model (e.g. 'user_id' on posts). */
|
|
7
7
|
secondKey: string;
|
|
8
8
|
/** Parent local key the firstKey references. Default 'id'. */
|
|
9
|
-
localKey?: string;
|
|
9
|
+
localKey?: string | undefined;
|
|
10
10
|
/** Through local key the secondKey references. Default 'id'. */
|
|
11
|
-
throughLocalKey?: string;
|
|
11
|
+
throughLocalKey?: string | undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -5,7 +5,7 @@ export interface HasOneOptions {
|
|
|
5
5
|
/** FK column on the related table pointing back to the parent (e.g. `user_id`). */
|
|
6
6
|
foreignKey: string;
|
|
7
7
|
/** Parent local key the FK references. Defaults to `'id'`. */
|
|
8
|
-
localKey?: string;
|
|
8
|
+
localKey?: string | undefined;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -9,13 +9,13 @@ export interface ManyToManyOptions {
|
|
|
9
9
|
/** Pivot column referencing the related model (e.g. `role_id`). */
|
|
10
10
|
pivotRelatedKey: string;
|
|
11
11
|
/** Parent local key the pivot FK references. Defaults to `'id'`. */
|
|
12
|
-
localKey?: string;
|
|
12
|
+
localKey?: string | undefined;
|
|
13
13
|
/** Related local key the pivot relatedKey references. Defaults to `'id'`. */
|
|
14
|
-
relatedKey?: string;
|
|
14
|
+
relatedKey?: string | undefined;
|
|
15
15
|
/** Extra pivot columns to hydrate onto each related model's `pivot` bag. */
|
|
16
|
-
withPivot?: string[];
|
|
16
|
+
withPivot?: string[] | undefined;
|
|
17
17
|
/** Maintain created_at / updated_at on the pivot table during attach/sync. */
|
|
18
|
-
withTimestamps?: boolean;
|
|
18
|
+
withTimestamps?: boolean | undefined;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -7,12 +7,12 @@ export interface MorphToOptions {
|
|
|
7
7
|
* Override the _type column name. Defaults to `<propertyName>_type`.
|
|
8
8
|
* e.g. property `commentable` → `commentable_type`.
|
|
9
9
|
*/
|
|
10
|
-
morphTypeColumn?: string;
|
|
10
|
+
morphTypeColumn?: string | undefined;
|
|
11
11
|
/**
|
|
12
12
|
* Override the _id column name. Defaults to `<propertyName>_id`.
|
|
13
13
|
* e.g. property `commentable` → `commentable_id`.
|
|
14
14
|
*/
|
|
15
|
-
morphForeignKey?: string;
|
|
15
|
+
morphForeignKey?: string | undefined;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function _toSnake(s: string): string {
|
|
@@ -6,11 +6,11 @@ export interface MorphToManyOptions {
|
|
|
6
6
|
/** FK on the pivot table referencing the related model (e.g. 'tag_id'). */
|
|
7
7
|
relatedPivotKey: string;
|
|
8
8
|
/** Pivot table name. Defaults to `{morphName}s`. */
|
|
9
|
-
pivotTable?: string;
|
|
9
|
+
pivotTable?: string | undefined;
|
|
10
10
|
/** Extra pivot columns to hydrate. */
|
|
11
|
-
withPivot?: string[];
|
|
11
|
+
withPivot?: string[] | undefined;
|
|
12
12
|
/** Maintain pivot timestamps. */
|
|
13
|
-
withTimestamps?: boolean;
|
|
13
|
+
withTimestamps?: boolean | undefined;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -6,9 +6,9 @@ export interface MorphedByManyOptions {
|
|
|
6
6
|
/** FK on the pivot referencing the parent model (e.g. 'tag_id'). */
|
|
7
7
|
parentPivotKey: string;
|
|
8
8
|
/** Pivot table name. Defaults to `{morphName}s`. */
|
|
9
|
-
pivotTable?: string;
|
|
10
|
-
withPivot?: string[];
|
|
11
|
-
withTimestamps?: boolean;
|
|
9
|
+
pivotTable?: string | undefined;
|
|
10
|
+
withPivot?: string[] | undefined;
|
|
11
|
+
withTimestamps?: boolean | undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -11,9 +11,9 @@ import type { ClassRef } from "../../support/classRef.ts";
|
|
|
11
11
|
*/
|
|
12
12
|
export interface TableOptions {
|
|
13
13
|
/** Enable / disable automatic timestamp columns. @default true */
|
|
14
|
-
timestamps?: boolean;
|
|
14
|
+
timestamps?: boolean | undefined;
|
|
15
15
|
/** Override the primary key column name. @default "id" */
|
|
16
|
-
primaryKey?: string;
|
|
16
|
+
primaryKey?: string | undefined;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -36,6 +36,8 @@ const _suppressCtx = new AsyncLocalStorage<true>();
|
|
|
36
36
|
/**
|
|
37
37
|
* Run `fn` with all model hooks and observers silenced.
|
|
38
38
|
* Used internally by Factory when `dispatchEvents()` has NOT been called.
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
39
41
|
*/
|
|
40
42
|
export function _suppressHooks<T>(fn: () => Promise<T>): Promise<T> {
|
|
41
43
|
return _suppressCtx.run(true, fn);
|
package/src/observability.ts
CHANGED
|
@@ -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,14 +41,18 @@ function applyColumn(table: TableBuilder, col: ModelColumn): void {
|
|
|
41
41
|
else if (col.index) table.index(dbName);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
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
|
|
48
52
|
* declared by any model. Off by default - additive changes are always safe,
|
|
49
53
|
* drops can lose data.
|
|
50
54
|
*/
|
|
51
|
-
disruptive?: boolean;
|
|
55
|
+
disruptive?: boolean | undefined;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/**
|
|
@@ -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
|
-
/**
|
|
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 };
|