@c9up/atlas 0.2.0 → 0.2.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/README.md +8 -0
- package/db.darwin-arm64.node +0 -0
- package/db.darwin-x64.node +0 -0
- package/db.linux-arm64-gnu.node +0 -0
- package/db.linux-x64-gnu.node +0 -0
- package/db.win32-x64-msvc.node +0 -0
- package/dist/AtlasProvider.d.ts +60 -7
- package/dist/AtlasProvider.d.ts.map +1 -1
- package/dist/AtlasProvider.js +204 -38
- package/dist/AtlasProvider.js.map +1 -1
- package/dist/BaseEntity.d.ts +17 -0
- package/dist/BaseEntity.d.ts.map +1 -1
- package/dist/BaseEntity.js.map +1 -1
- package/dist/BaseModel.d.ts +63 -27
- package/dist/BaseModel.d.ts.map +1 -1
- package/dist/BaseModel.js +108 -51
- package/dist/BaseModel.js.map +1 -1
- package/dist/BaseRepository.d.ts +11 -1
- package/dist/BaseRepository.d.ts.map +1 -1
- package/dist/BaseRepository.js +95 -20
- package/dist/BaseRepository.js.map +1 -1
- package/dist/ConnectionManager.d.ts +106 -0
- package/dist/ConnectionManager.d.ts.map +1 -0
- package/dist/ConnectionManager.js +228 -0
- package/dist/ConnectionManager.js.map +1 -0
- package/dist/ModelQuery.d.ts +256 -43
- package/dist/ModelQuery.d.ts.map +1 -1
- package/dist/ModelQuery.js +899 -231
- package/dist/ModelQuery.js.map +1 -1
- package/dist/Transaction.d.ts +54 -1
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +121 -59
- package/dist/Transaction.js.map +1 -1
- package/dist/adapters/NapiDbAdapter.d.ts +11 -0
- package/dist/adapters/NapiDbAdapter.d.ts.map +1 -1
- package/dist/adapters/NapiDbAdapter.js +35 -7
- package/dist/adapters/NapiDbAdapter.js.map +1 -1
- package/dist/console/contract.d.ts +62 -0
- package/dist/console/contract.d.ts.map +1 -0
- package/dist/console/contract.js +38 -0
- package/dist/console/contract.js.map +1 -0
- package/dist/console/factoryCommands.d.ts +23 -0
- package/dist/console/factoryCommands.d.ts.map +1 -0
- package/dist/console/factoryCommands.js +62 -0
- package/dist/console/factoryCommands.js.map +1 -0
- package/dist/console/migrationCommands.d.ts +38 -8
- package/dist/console/migrationCommands.d.ts.map +1 -1
- package/dist/console/migrationCommands.js +157 -50
- package/dist/console/migrationCommands.js.map +1 -1
- package/dist/console/schemaCheckCommand.d.ts +11 -17
- package/dist/console/schemaCheckCommand.d.ts.map +1 -1
- package/dist/console/schemaCheckCommand.js +21 -15
- package/dist/console/schemaCheckCommand.js.map +1 -1
- package/dist/console/schemaDumpCommand.d.ts +30 -0
- package/dist/console/schemaDumpCommand.d.ts.map +1 -0
- package/dist/console/schemaDumpCommand.js +69 -0
- package/dist/console/schemaDumpCommand.js.map +1 -0
- package/dist/console/schemaGenerateCommand.d.ts +100 -0
- package/dist/console/schemaGenerateCommand.d.ts.map +1 -0
- package/dist/console/schemaGenerateCommand.js +246 -0
- package/dist/console/schemaGenerateCommand.js.map +1 -0
- package/dist/console/seederCommands.d.ts +46 -0
- package/dist/console/seederCommands.d.ts.map +1 -0
- package/dist/console/seederCommands.js +136 -0
- package/dist/console/seederCommands.js.map +1 -0
- package/dist/decorators/entity.d.ts +13 -2
- package/dist/decorators/entity.d.ts.map +1 -1
- package/dist/decorators/entity.js +10 -0
- package/dist/decorators/entity.js.map +1 -1
- package/dist/decorators/hooks.d.ts +3 -3
- package/dist/decorators/hooks.d.ts.map +1 -1
- package/dist/decorators/hooks.js.map +1 -1
- package/dist/events.d.ts +5 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/factories.d.ts +8 -0
- package/dist/factories.d.ts.map +1 -0
- package/dist/factories.js +8 -0
- package/dist/factories.js.map +1 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/lucid-schema.d.ts +15 -0
- package/dist/lucid-schema.d.ts.map +1 -0
- package/dist/lucid-schema.js +16 -0
- package/dist/lucid-schema.js.map +1 -0
- package/dist/orm.d.ts +14 -0
- package/dist/orm.d.ts.map +1 -0
- package/dist/orm.js +14 -0
- package/dist/orm.js.map +1 -0
- package/dist/query/DatabaseQueryBuilder.d.ts +557 -0
- package/dist/query/DatabaseQueryBuilder.d.ts.map +1 -0
- package/dist/query/DatabaseQueryBuilder.js +1798 -0
- package/dist/query/DatabaseQueryBuilder.js.map +1 -0
- package/dist/query/DmlBuilder.d.ts +62 -0
- package/dist/query/DmlBuilder.d.ts.map +1 -0
- package/dist/query/DmlBuilder.js +84 -0
- package/dist/query/DmlBuilder.js.map +1 -0
- package/dist/query/QueryBuilder.d.ts +5 -0
- package/dist/query/QueryBuilder.d.ts.map +1 -1
- package/dist/query/QueryBuilder.js +7 -0
- package/dist/query/QueryBuilder.js.map +1 -1
- package/dist/query/RawQueryBuilder.d.ts +47 -0
- package/dist/query/RawQueryBuilder.d.ts.map +1 -0
- package/dist/query/RawQueryBuilder.js +138 -0
- package/dist/query/RawQueryBuilder.js.map +1 -0
- package/dist/query/interpolate.d.ts +45 -0
- package/dist/query/interpolate.d.ts.map +1 -0
- package/dist/query/interpolate.js +51 -0
- package/dist/query/interpolate.js.map +1 -0
- package/dist/query/native.d.ts +8 -0
- package/dist/query/native.d.ts.map +1 -1
- package/dist/query/native.js +14 -0
- package/dist/query/native.js.map +1 -1
- package/dist/query/operators.d.ts +4 -0
- package/dist/query/operators.d.ts.map +1 -0
- package/dist/query/operators.js +27 -0
- package/dist/query/operators.js.map +1 -0
- package/dist/schema/Migration.d.ts +31 -7
- package/dist/schema/Migration.d.ts.map +1 -1
- package/dist/schema/Migration.js +34 -7
- package/dist/schema/Migration.js.map +1 -1
- package/dist/schema/MigrationRunner.d.ts +56 -6
- package/dist/schema/MigrationRunner.d.ts.map +1 -1
- package/dist/schema/MigrationRunner.js +382 -112
- package/dist/schema/MigrationRunner.js.map +1 -1
- package/dist/schema/Schema.d.ts +59 -2
- package/dist/schema/Schema.d.ts.map +1 -1
- package/dist/schema/Schema.js +143 -9
- package/dist/schema/Schema.js.map +1 -1
- package/dist/schema/SchemaDumper.d.ts +121 -0
- package/dist/schema/SchemaDumper.d.ts.map +1 -0
- package/dist/schema/SchemaDumper.js +365 -0
- package/dist/schema/SchemaDumper.js.map +1 -0
- package/dist/schema/Seeder.d.ts +13 -0
- package/dist/schema/Seeder.d.ts.map +1 -1
- package/dist/schema/Seeder.js +28 -5
- package/dist/schema/Seeder.js.map +1 -1
- package/dist/schema/TableBuilder.d.ts +27 -8
- package/dist/schema/TableBuilder.d.ts.map +1 -1
- package/dist/schema/TableBuilder.js +50 -12
- package/dist/schema/TableBuilder.js.map +1 -1
- package/dist/schema/catalog.d.ts +25 -1
- package/dist/schema/catalog.d.ts.map +1 -1
- package/dist/schema/catalog.js +69 -22
- package/dist/schema/catalog.js.map +1 -1
- package/dist/schema/introspect.d.ts +1 -1
- package/dist/schema/introspect.d.ts.map +1 -1
- package/dist/schema/introspect.js +26 -11
- package/dist/schema/introspect.js.map +1 -1
- package/dist/seeders.d.ts +7 -0
- package/dist/seeders.d.ts.map +1 -0
- package/dist/seeders.js +7 -0
- package/dist/seeders.js.map +1 -0
- package/dist/services/db.d.ts +120 -9
- package/dist/services/db.d.ts.map +1 -1
- package/dist/services/db.js +182 -26
- package/dist/services/db.js.map +1 -1
- package/dist/testing/DatabaseCleanup.d.ts +29 -6
- package/dist/testing/DatabaseCleanup.d.ts.map +1 -1
- package/dist/testing/DatabaseCleanup.js +44 -17
- package/dist/testing/DatabaseCleanup.js.map +1 -1
- package/dist/testing/DbAssertions.d.ts +48 -0
- package/dist/testing/DbAssertions.d.ts.map +1 -0
- package/dist/testing/DbAssertions.js +80 -0
- package/dist/testing/DbAssertions.js.map +1 -0
- package/dist/testing/Factory.d.ts +111 -25
- package/dist/testing/Factory.d.ts.map +1 -1
- package/dist/testing/Factory.js +331 -56
- package/dist/testing/Factory.js.map +1 -1
- package/dist/testing/TestUtils.d.ts +49 -0
- package/dist/testing/TestUtils.d.ts.map +1 -0
- package/dist/testing/TestUtils.js +79 -0
- package/dist/testing/TestUtils.js.map +1 -0
- package/dist/testing/index.d.ts +3 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +3 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/types/relations.d.ts +31 -0
- package/dist/types/relations.d.ts.map +1 -0
- package/dist/types/relations.js +20 -0
- package/dist/types/relations.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +30 -2
- package/src/AtlasProvider.ts +297 -51
- package/src/BaseEntity.ts +29 -0
- package/src/BaseModel.ts +174 -37
- package/src/BaseRepository.ts +136 -17
- package/src/ConnectionManager.ts +298 -0
- package/src/ModelQuery.ts +1309 -260
- package/src/Transaction.ts +194 -60
- package/src/adapters/NapiDbAdapter.ts +72 -14
- package/src/console/contract.ts +96 -0
- package/src/console/factoryCommands.ts +73 -0
- package/src/console/migrationCommands.ts +253 -63
- package/src/console/schemaCheckCommand.ts +26 -22
- package/src/console/schemaDumpCommand.ts +96 -0
- package/src/console/schemaGenerateCommand.ts +380 -0
- package/src/console/seederCommands.ts +174 -0
- package/src/decorators/entity.ts +18 -2
- package/src/decorators/hooks.ts +5 -3
- package/src/events.ts +5 -0
- package/src/factories.ts +10 -0
- package/src/index.ts +50 -3
- package/src/lucid-schema.ts +22 -0
- package/src/orm.ts +13 -0
- package/src/query/DatabaseQueryBuilder.ts +2724 -0
- package/src/query/DmlBuilder.ts +131 -0
- package/src/query/QueryBuilder.ts +8 -0
- package/src/query/RawQueryBuilder.ts +173 -0
- package/src/query/interpolate.ts +73 -0
- package/src/query/native.ts +29 -0
- package/src/query/operators.ts +27 -0
- package/src/schema/Migration.ts +36 -7
- package/src/schema/MigrationRunner.ts +483 -119
- package/src/schema/Schema.ts +168 -19
- package/src/schema/SchemaDumper.ts +518 -0
- package/src/schema/Seeder.ts +47 -10
- package/src/schema/TableBuilder.ts +48 -11
- package/src/schema/catalog.ts +94 -24
- package/src/schema/introspect.ts +26 -9
- package/src/seeders.ts +16 -0
- package/src/services/db.ts +343 -31
- package/src/testing/DatabaseCleanup.ts +73 -21
- package/src/testing/DbAssertions.ts +127 -0
- package/src/testing/Factory.ts +559 -79
- package/src/testing/TestUtils.ts +123 -0
- package/src/testing/index.ts +12 -1
- package/src/types/relations.ts +43 -0
|
@@ -27,11 +27,40 @@ import {
|
|
|
27
27
|
} from "../schema/MigrationRunner.js";
|
|
28
28
|
import { getDb } from "../services/db.js";
|
|
29
29
|
import { assertSafeName } from "../utils/safePath.js";
|
|
30
|
-
import type
|
|
30
|
+
import { type AtlasCommandClass, argument, flag } from "./contract.js";
|
|
31
|
+
import {
|
|
32
|
+
generateSchemaFile,
|
|
33
|
+
type SchemaGenerateOptions,
|
|
34
|
+
} from "./schemaGenerateCommand.js";
|
|
31
35
|
|
|
32
36
|
export interface MigrationCommandOptions {
|
|
33
37
|
/** Directory holding the numbered migration files. */
|
|
34
38
|
migrationsDir: string;
|
|
39
|
+
/**
|
|
40
|
+
* Sort migration files numerically (`2_x` before `10_x`). Adonis Lucid
|
|
41
|
+
* `migrations.naturalSort`. Defaults to `false`.
|
|
42
|
+
*/
|
|
43
|
+
naturalSort?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Run every migration outside a transaction. Adonis Lucid
|
|
46
|
+
* `migrations.disableTransactions`. A migration can also opt out with
|
|
47
|
+
* `static disableTransactions = true`. Defaults to `false`.
|
|
48
|
+
*/
|
|
49
|
+
disableTransactions?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Regenerate the schema file after a mutating migration command
|
|
52
|
+
* (run/rollback/reset/refresh/fresh) — Adonis Lucid's post-migration
|
|
53
|
+
* `schema:generate`. Off unless an `outputPath` is given; on by default once
|
|
54
|
+
* it is, unless `enabled: false`; suppress per-run with `--no-schema-generate`.
|
|
55
|
+
*/
|
|
56
|
+
schemaGeneration?: SchemaGenerateOptions;
|
|
57
|
+
/**
|
|
58
|
+
* Default schema-dump path auto-loaded by `migration:run` / `migration:fresh`
|
|
59
|
+
* when the target DB has no applied migrations (Adonis Lucid bootstraps from
|
|
60
|
+
* the dump by default). Overridden per-run by `--schema-path`; a run never
|
|
61
|
+
* loads it once migrations exist.
|
|
62
|
+
*/
|
|
63
|
+
schemaPath?: string;
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
/**
|
|
@@ -46,6 +75,9 @@ function toAdapter(conn: AsyncDatabaseConnection): DatabaseAdapter {
|
|
|
46
75
|
},
|
|
47
76
|
query: (sql, params) => conn.query(sql, params),
|
|
48
77
|
runInTransaction: (batch) => conn.runInTransaction(batch),
|
|
78
|
+
// Thread the connection's managed interactive transaction through so the
|
|
79
|
+
// runner can make this.defer() atomic and restore MySQL FK checks.
|
|
80
|
+
transaction: conn.transaction?.bind(conn),
|
|
49
81
|
close: async () => {},
|
|
50
82
|
};
|
|
51
83
|
}
|
|
@@ -65,26 +97,42 @@ function resolveRunner(
|
|
|
65
97
|
process.exitCode = 1;
|
|
66
98
|
return undefined;
|
|
67
99
|
}
|
|
100
|
+
// Pass the connection's ACTUAL dialect — without it the runner defaults to
|
|
101
|
+
// sqlite and emits SQLite SQL (lock table, FK handling, DDL) against a
|
|
102
|
+
// Postgres/MySQL database.
|
|
68
103
|
return new MigrationRunner(toAdapter(db), {
|
|
69
104
|
migrationsDir: options.migrationsDir,
|
|
105
|
+
dialect: db.dialect,
|
|
106
|
+
naturalSort: options.naturalSort,
|
|
107
|
+
disableTransactions: options.disableTransactions,
|
|
70
108
|
});
|
|
71
109
|
}
|
|
72
110
|
|
|
73
111
|
/** `migration:run` — apply every pending migration. */
|
|
74
112
|
export function migrationRunCommand(
|
|
75
113
|
options: MigrationCommandOptions,
|
|
76
|
-
):
|
|
77
|
-
return {
|
|
78
|
-
|
|
79
|
-
description
|
|
80
|
-
|
|
114
|
+
): AtlasCommandClass {
|
|
115
|
+
return class MigrationRun {
|
|
116
|
+
static commandName = "migration:run";
|
|
117
|
+
static description =
|
|
118
|
+
"Run all pending migrations (--schema-path to bootstrap from a dump)";
|
|
119
|
+
static options = { startApp: true };
|
|
120
|
+
static flags = [schemaPathOption(), noSchemaGenerateFlag()];
|
|
121
|
+
|
|
122
|
+
declare schemaPath?: string;
|
|
123
|
+
declare noSchemaGenerate: boolean;
|
|
124
|
+
|
|
125
|
+
async run(): Promise<void> {
|
|
81
126
|
const runner = resolveRunner(options);
|
|
82
127
|
if (!runner) return;
|
|
83
|
-
const ran = await runner.migrate(
|
|
128
|
+
const ran = await runner.migrate({
|
|
129
|
+
schemaPath: this.schemaPath ?? options.schemaPath,
|
|
130
|
+
});
|
|
84
131
|
console.log(
|
|
85
132
|
ran.length ? `Migrated: ${ran.join(", ")}` : "Already up to date",
|
|
86
133
|
);
|
|
87
|
-
|
|
134
|
+
await maybeRegenSchema(options, this);
|
|
135
|
+
}
|
|
88
136
|
};
|
|
89
137
|
}
|
|
90
138
|
|
|
@@ -94,39 +142,57 @@ export function migrationRunCommand(
|
|
|
94
142
|
*/
|
|
95
143
|
export function migrationRollbackCommand(
|
|
96
144
|
options: MigrationCommandOptions,
|
|
97
|
-
):
|
|
98
|
-
return {
|
|
99
|
-
|
|
100
|
-
description
|
|
101
|
-
|
|
145
|
+
): AtlasCommandClass {
|
|
146
|
+
return class MigrationRollback {
|
|
147
|
+
static commandName = "migration:rollback";
|
|
148
|
+
static description =
|
|
149
|
+
"Roll back the latest batch (or --batch=N; --force in prod)";
|
|
150
|
+
static options = { startApp: true };
|
|
151
|
+
static flags = [
|
|
152
|
+
flag("batch", "number", {
|
|
153
|
+
description: "Roll back everything applied after batch N (0 = all)",
|
|
154
|
+
}),
|
|
155
|
+
forceFlag(),
|
|
156
|
+
noSchemaGenerateFlag(),
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
declare batch?: number;
|
|
160
|
+
declare force: boolean;
|
|
161
|
+
declare noSchemaGenerate: boolean;
|
|
162
|
+
|
|
163
|
+
async run(): Promise<void> {
|
|
102
164
|
const runner = resolveRunner(options);
|
|
103
165
|
if (!runner) return;
|
|
104
|
-
const batch = parseBatchFlag(
|
|
166
|
+
const batch = parseBatchFlag(this.batch);
|
|
105
167
|
if (batch === "invalid") {
|
|
106
168
|
console.error("[atlas] --batch must be a non-negative integer");
|
|
107
169
|
process.exitCode = 1;
|
|
108
170
|
return;
|
|
109
171
|
}
|
|
172
|
+
const force = this.force === true;
|
|
110
173
|
const rolled = await runner.rollback(
|
|
111
|
-
batch === undefined ? {} : { batch },
|
|
174
|
+
batch === undefined ? { force } : { batch, force },
|
|
112
175
|
);
|
|
113
176
|
console.log(
|
|
114
177
|
rolled.length
|
|
115
178
|
? `Rolled back: ${rolled.join(", ")}`
|
|
116
179
|
: "Nothing to roll back",
|
|
117
180
|
);
|
|
118
|
-
|
|
181
|
+
await maybeRegenSchema(options, this);
|
|
182
|
+
}
|
|
119
183
|
};
|
|
120
184
|
}
|
|
121
185
|
|
|
122
186
|
/** `migration:status` — list every migration and whether it is applied. */
|
|
123
187
|
export function migrationStatusCommand(
|
|
124
188
|
options: MigrationCommandOptions,
|
|
125
|
-
):
|
|
126
|
-
return {
|
|
127
|
-
|
|
128
|
-
description
|
|
129
|
-
|
|
189
|
+
): AtlasCommandClass {
|
|
190
|
+
return class MigrationStatus {
|
|
191
|
+
static commandName = "migration:status";
|
|
192
|
+
static description = "Show applied and pending migrations";
|
|
193
|
+
static options = { startApp: true };
|
|
194
|
+
|
|
195
|
+
async run(): Promise<void> {
|
|
130
196
|
const runner = resolveRunner(options);
|
|
131
197
|
if (!runner) return;
|
|
132
198
|
const rows = await runner.status();
|
|
@@ -138,57 +204,135 @@ export function migrationStatusCommand(
|
|
|
138
204
|
const batch = row.batch === undefined ? "" : ` (batch ${row.batch})`;
|
|
139
205
|
console.log(`${row.status.padEnd(8)} ${row.name}${batch}`);
|
|
140
206
|
}
|
|
141
|
-
}
|
|
207
|
+
}
|
|
142
208
|
};
|
|
143
209
|
}
|
|
144
210
|
|
|
145
211
|
/** `migration:reset` — roll back every applied migration. */
|
|
146
212
|
export function migrationResetCommand(
|
|
147
213
|
options: MigrationCommandOptions,
|
|
148
|
-
):
|
|
149
|
-
return {
|
|
150
|
-
|
|
151
|
-
description
|
|
152
|
-
|
|
214
|
+
): AtlasCommandClass {
|
|
215
|
+
return class MigrationReset {
|
|
216
|
+
static commandName = "migration:reset";
|
|
217
|
+
static description =
|
|
218
|
+
"Roll back all migrations (--force to override prod guard)";
|
|
219
|
+
static options = { startApp: true };
|
|
220
|
+
static flags = [forceFlag(), noSchemaGenerateFlag()];
|
|
221
|
+
|
|
222
|
+
declare force: boolean;
|
|
223
|
+
declare noSchemaGenerate: boolean;
|
|
224
|
+
|
|
225
|
+
async run(): Promise<void> {
|
|
153
226
|
const runner = resolveRunner(options);
|
|
154
227
|
if (!runner) return;
|
|
155
|
-
const rolled = await runner.reset();
|
|
228
|
+
const rolled = await runner.reset({ force: this.force === true });
|
|
156
229
|
console.log(
|
|
157
230
|
rolled.length
|
|
158
231
|
? `Rolled back: ${rolled.join(", ")}`
|
|
159
232
|
: "Nothing to reset",
|
|
160
233
|
);
|
|
161
|
-
|
|
234
|
+
await maybeRegenSchema(options, this);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** `migration:unlock` — force-clear a stuck migration lock (Lucid `migration:unlock`). */
|
|
240
|
+
export function migrationUnlockCommand(
|
|
241
|
+
options: MigrationCommandOptions,
|
|
242
|
+
): AtlasCommandClass {
|
|
243
|
+
return class MigrationUnlock {
|
|
244
|
+
static commandName = "migration:unlock";
|
|
245
|
+
static description = "Force-clear a stuck migration lock";
|
|
246
|
+
static options = { startApp: true };
|
|
247
|
+
|
|
248
|
+
async run(): Promise<void> {
|
|
249
|
+
const runner = resolveRunner(options);
|
|
250
|
+
if (!runner) return;
|
|
251
|
+
const cleared = await runner.forceUnlock();
|
|
252
|
+
console.log(
|
|
253
|
+
cleared ? "Migration lock cleared" : "No migration lock was held",
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** `migration:fresh` — drop every table, then re-run all migrations (Lucid `migration:fresh`). */
|
|
260
|
+
export function migrationFreshCommand(
|
|
261
|
+
options: MigrationCommandOptions,
|
|
262
|
+
): AtlasCommandClass {
|
|
263
|
+
return class MigrationFresh {
|
|
264
|
+
static commandName = "migration:fresh";
|
|
265
|
+
static description =
|
|
266
|
+
"Drop all tables, then re-run every migration (--force to override prod guard)";
|
|
267
|
+
static options = { startApp: true };
|
|
268
|
+
static flags = [forceFlag(), schemaPathOption(), noSchemaGenerateFlag()];
|
|
269
|
+
|
|
270
|
+
declare force: boolean;
|
|
271
|
+
declare schemaPath?: string;
|
|
272
|
+
declare noSchemaGenerate: boolean;
|
|
273
|
+
|
|
274
|
+
async run(): Promise<void> {
|
|
275
|
+
const runner = resolveRunner(options);
|
|
276
|
+
if (!runner) return;
|
|
277
|
+
const { executed } = await runner.fresh({
|
|
278
|
+
force: this.force === true,
|
|
279
|
+
schemaPath: this.schemaPath ?? options.schemaPath,
|
|
280
|
+
});
|
|
281
|
+
console.log(
|
|
282
|
+
executed.length
|
|
283
|
+
? `Dropped all tables, re-ran: ${executed.join(", ")}`
|
|
284
|
+
: "Dropped all tables (no migrations to run)",
|
|
285
|
+
);
|
|
286
|
+
await maybeRegenSchema(options, this);
|
|
287
|
+
}
|
|
162
288
|
};
|
|
163
289
|
}
|
|
164
290
|
|
|
165
291
|
/** `migration:refresh` — roll everything back, then re-run all migrations. */
|
|
166
292
|
export function migrationRefreshCommand(
|
|
167
293
|
options: MigrationCommandOptions,
|
|
168
|
-
):
|
|
169
|
-
return {
|
|
170
|
-
|
|
171
|
-
description
|
|
172
|
-
|
|
294
|
+
): AtlasCommandClass {
|
|
295
|
+
return class MigrationRefresh {
|
|
296
|
+
static commandName = "migration:refresh";
|
|
297
|
+
static description =
|
|
298
|
+
"Roll back all migrations, then re-run them (--force to override prod guard)";
|
|
299
|
+
static options = { startApp: true };
|
|
300
|
+
static flags = [forceFlag(), noSchemaGenerateFlag()];
|
|
301
|
+
|
|
302
|
+
declare force: boolean;
|
|
303
|
+
declare noSchemaGenerate: boolean;
|
|
304
|
+
|
|
305
|
+
async run(): Promise<void> {
|
|
173
306
|
const runner = resolveRunner(options);
|
|
174
307
|
if (!runner) return;
|
|
175
|
-
const { rolled, executed } = await runner.refresh(
|
|
308
|
+
const { rolled, executed } = await runner.refresh({
|
|
309
|
+
force: this.force === true,
|
|
310
|
+
});
|
|
176
311
|
console.log(`Rolled back: ${rolled.length}, re-ran: ${executed.length}`);
|
|
177
|
-
|
|
312
|
+
await maybeRegenSchema(options, this);
|
|
313
|
+
}
|
|
178
314
|
};
|
|
179
315
|
}
|
|
180
316
|
|
|
181
317
|
/** `db:wipe` — drop every table, including the migrations bookkeeping table. */
|
|
182
|
-
export function dbWipeCommand(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
318
|
+
export function dbWipeCommand(
|
|
319
|
+
options: MigrationCommandOptions,
|
|
320
|
+
): AtlasCommandClass {
|
|
321
|
+
return class DbWipe {
|
|
322
|
+
static commandName = "db:wipe";
|
|
323
|
+
static description =
|
|
324
|
+
"Drop all tables including the migrations table (--force to override prod guard)";
|
|
325
|
+
static options = { startApp: true };
|
|
326
|
+
static flags = [forceFlag()];
|
|
327
|
+
|
|
328
|
+
declare force: boolean;
|
|
329
|
+
|
|
330
|
+
async run(): Promise<void> {
|
|
187
331
|
const runner = resolveRunner(options);
|
|
188
332
|
if (!runner) return;
|
|
189
|
-
await runner.wipe();
|
|
333
|
+
await runner.wipe({ force: this.force === true });
|
|
190
334
|
console.log("Dropped all tables");
|
|
191
|
-
}
|
|
335
|
+
}
|
|
192
336
|
};
|
|
193
337
|
}
|
|
194
338
|
|
|
@@ -217,42 +361,88 @@ export default class extends Migration {
|
|
|
217
361
|
*/
|
|
218
362
|
export function makeMigrationCommand(
|
|
219
363
|
options: MigrationCommandOptions,
|
|
220
|
-
):
|
|
221
|
-
return {
|
|
222
|
-
|
|
223
|
-
description
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
364
|
+
): AtlasCommandClass {
|
|
365
|
+
return class MakeMigration {
|
|
366
|
+
static commandName = "make:migration";
|
|
367
|
+
static description = "Scaffold a new timestamped migration file";
|
|
368
|
+
// Filesystem only — no connection needed.
|
|
369
|
+
static options = { startApp: false };
|
|
370
|
+
static args = [argument("name", { description: "Migration file name" })];
|
|
371
|
+
|
|
372
|
+
declare name: string;
|
|
373
|
+
|
|
374
|
+
async run(): Promise<void> {
|
|
375
|
+
// A missing name never reaches here: the kernel reports the required
|
|
376
|
+
// argument by name before `run()`.
|
|
231
377
|
try {
|
|
232
|
-
assertSafeName(name, "MIGRATION_INVALID", "migration");
|
|
378
|
+
assertSafeName(this.name, "MIGRATION_INVALID", "migration");
|
|
233
379
|
} catch {
|
|
234
|
-
console.error(`[atlas] invalid migration name: ${name}`);
|
|
380
|
+
console.error(`[atlas] invalid migration name: ${this.name}`);
|
|
235
381
|
process.exitCode = 1;
|
|
236
382
|
return;
|
|
237
383
|
}
|
|
238
|
-
const fileName = `${Date.now()}_${name}.ts`;
|
|
384
|
+
const fileName = `${Date.now()}_${this.name}.ts`;
|
|
239
385
|
const filePath = path.join(options.migrationsDir, fileName);
|
|
240
386
|
await fsp.mkdir(options.migrationsDir, { recursive: true });
|
|
241
387
|
await fsp.writeFile(filePath, MIGRATION_STUB, { flag: "wx" });
|
|
242
388
|
console.log(`Created ${filePath}`);
|
|
243
|
-
}
|
|
389
|
+
}
|
|
244
390
|
};
|
|
245
391
|
}
|
|
246
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Flags shared by several migration commands.
|
|
395
|
+
*
|
|
396
|
+
* Declared per command rather than inherited: `migration:status` takes neither
|
|
397
|
+
* `--force` nor `--schema-path`, and listing them anyway would put flags in its
|
|
398
|
+
* help that it ignores.
|
|
399
|
+
*/
|
|
400
|
+
const forceFlag = () =>
|
|
401
|
+
flag("force", "boolean", {
|
|
402
|
+
description: "Override the production guard",
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const schemaPathOption = () =>
|
|
406
|
+
flag("schemaPath", "string", {
|
|
407
|
+
description: "Bootstrap from a schema dump before migrating",
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const noSchemaGenerateFlag = () =>
|
|
411
|
+
flag("noSchemaGenerate", "boolean", {
|
|
412
|
+
description: "Skip regenerating the schema file afterwards",
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Regenerate the schema file after a mutating migration command (Adonis Lucid's
|
|
417
|
+
* post-migration `schema:generate`) — unless it's disabled, has no `outputPath`,
|
|
418
|
+
* or the run passed `--no-schema-generate`.
|
|
419
|
+
*/
|
|
420
|
+
async function maybeRegenSchema(
|
|
421
|
+
options: MigrationCommandOptions,
|
|
422
|
+
command: { noSchemaGenerate?: boolean },
|
|
423
|
+
): Promise<void> {
|
|
424
|
+
const cfg = options.schemaGeneration;
|
|
425
|
+
// Opt-in by configuring an outputPath; then ON unless explicitly disabled —
|
|
426
|
+
// Adonis Lucid presents `enabled: false` as the OFF switch (default on).
|
|
427
|
+
if (!cfg?.outputPath || cfg.enabled === false) return;
|
|
428
|
+
if (command.noSchemaGenerate === true) return;
|
|
429
|
+
const db = getDb();
|
|
430
|
+
if (!db) return;
|
|
431
|
+
const n = await generateSchemaFile(db, cfg);
|
|
432
|
+
console.log(
|
|
433
|
+
`Regenerated ${cfg.outputPath} (${n} table${n === 1 ? "" : "s"})`,
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
247
437
|
/**
|
|
248
438
|
* Parse the `--batch` flag: absent → undefined (default rollback), a
|
|
249
439
|
* non-negative integer string → that number, anything else → `"invalid"`.
|
|
250
440
|
*/
|
|
251
441
|
function parseBatchFlag(
|
|
252
|
-
value:
|
|
442
|
+
value: number | undefined,
|
|
253
443
|
): number | undefined | "invalid" {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return Number.isInteger(
|
|
444
|
+
// The kernel already rejected a non-numeric `--batch`; what is left to check
|
|
445
|
+
// is that the number is a non-negative integer.
|
|
446
|
+
if (value === undefined) return undefined;
|
|
447
|
+
return Number.isInteger(value) && value >= 0 ? value : "invalid";
|
|
258
448
|
}
|
|
@@ -1,33 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `atlas:check` console command — the Ream-idiomatic CLI for schema
|
|
3
|
-
* verification. Ream commands are
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* verification. Ream commands are classes carrying their name, description and
|
|
4
|
+
* inputs as statics; the console kernel discovers them in `commands/` or reads
|
|
5
|
+
* them from `reamrc.commands`.
|
|
6
6
|
*
|
|
7
7
|
* Stays framework-agnostic: resolves the live connection + dialect from atlas's
|
|
8
8
|
* OWN service locators (`getDb` / `getAtlasDialect`), never importing
|
|
9
|
-
* `@c9up/ream`. The {@link
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* `@c9up/ream`. The {@link AtlasCommandClass} shape structurally matches Ream's
|
|
10
|
+
* command contract, so the console kernel accepts it without a type dependency
|
|
11
|
+
* in either direction.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { getAtlasDialect } from "../query/native.js";
|
|
15
15
|
import { runSchemaCheck } from "../schema/SchemaCheck.js";
|
|
16
16
|
import { getDb } from "../services/db.js";
|
|
17
|
+
import { type AtlasCommandClass, flag } from "./contract.js";
|
|
17
18
|
|
|
18
19
|
type Constructor = new (...args: unknown[]) => unknown;
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
export interface AtlasCommand {
|
|
22
|
-
name: string;
|
|
23
|
-
description: string;
|
|
24
|
-
run(args: string[], flags: Record<string, string | boolean>): Promise<void>;
|
|
25
|
-
}
|
|
21
|
+
export type { AtlasCommandClass } from "./contract.js";
|
|
26
22
|
|
|
27
23
|
/**
|
|
28
24
|
* Build the `atlas:check` command for the given models. Register it in
|
|
29
25
|
* `reamrc.commands` (atlas has no global entity registry — list your models,
|
|
30
|
-
* as in Lucid). Run it
|
|
26
|
+
* as in Lucid). Run it as `ream atlas:check`; `--warn` reports drift without a
|
|
31
27
|
* non-zero exit (useful for an advisory CI step).
|
|
32
28
|
*
|
|
33
29
|
* @example
|
|
@@ -36,16 +32,24 @@ export interface AtlasCommand {
|
|
|
36
32
|
* import { User } from '#models/user'
|
|
37
33
|
* export default schemaCheckCommand([User])
|
|
38
34
|
*
|
|
39
|
-
* //
|
|
40
|
-
* // run: <console-entry> atlas:check
|
|
35
|
+
* // run: ream atlas:check --warn
|
|
41
36
|
*/
|
|
42
37
|
export function schemaCheckCommand(
|
|
43
38
|
entities: readonly Constructor[],
|
|
44
|
-
):
|
|
45
|
-
return {
|
|
46
|
-
|
|
47
|
-
description
|
|
48
|
-
|
|
39
|
+
): AtlasCommandClass {
|
|
40
|
+
return class SchemaCheck {
|
|
41
|
+
static commandName = "atlas:check";
|
|
42
|
+
static description = "Verify models match the live database schema";
|
|
43
|
+
static options = { startApp: true };
|
|
44
|
+
static flags = [
|
|
45
|
+
flag("warn", "boolean", {
|
|
46
|
+
description: "Report drift without failing (exit 0)",
|
|
47
|
+
}),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
declare warn: boolean;
|
|
51
|
+
|
|
52
|
+
async run(): Promise<void> {
|
|
49
53
|
const db = getDb();
|
|
50
54
|
if (!db) {
|
|
51
55
|
console.error(
|
|
@@ -56,7 +60,7 @@ export function schemaCheckCommand(
|
|
|
56
60
|
}
|
|
57
61
|
const code = await runSchemaCheck(entities, db, getAtlasDialect());
|
|
58
62
|
// `--warn` downgrades drift to advisory (exit 0); default fails CI.
|
|
59
|
-
if (code !== 0 && !
|
|
60
|
-
}
|
|
63
|
+
if (code !== 0 && !this.warn) process.exitCode = code;
|
|
64
|
+
}
|
|
61
65
|
};
|
|
62
66
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `schema:dump` — serialise the live database's schema to a `.sql` dump + a
|
|
3
|
+
* `.meta.json` manifest (Adonis Lucid `schema:dump` / `SchemaDumper`), so a
|
|
4
|
+
* fresh database can be rebuilt from the dump via `migration:run --schema-path`
|
|
5
|
+
* instead of replaying every migration.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // commands/schema-dump.ts
|
|
9
|
+
* import { schemaDumpCommand } from '@c9up/atlas'
|
|
10
|
+
* export default schemaDumpCommand({ migrationsDir: 'database/migrations' })
|
|
11
|
+
* // run: <console-entry> schema:dump → database/schema/default-schema.sql
|
|
12
|
+
* // schema:dump --prune → also squashes the migration files
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { SchemaDumper } from "../schema/SchemaDumper.js";
|
|
16
|
+
import { getConnection, getDb } from "../services/db.js";
|
|
17
|
+
import { type AtlasCommandClass, flag } from "./contract.js";
|
|
18
|
+
|
|
19
|
+
export interface SchemaDumpCommandOptions {
|
|
20
|
+
/** Directory the dump + manifest are written to. Default `"database/schema"`. */
|
|
21
|
+
outputDir?: string;
|
|
22
|
+
/** Migration directory — required for `--prune` (the files it collapses). */
|
|
23
|
+
migrationsDir?: string;
|
|
24
|
+
/** Migration bookkeeping table name. Default `"ream_migrations"`. */
|
|
25
|
+
schemaTableName?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Timestamp string for the manifest. `Date.now()`/`new Date()` are unavailable
|
|
28
|
+
* in some atlas contexts, so pass one in when you need a deterministic value.
|
|
29
|
+
*/
|
|
30
|
+
generatedAt?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** `schema:dump` — dump the schema; `--prune` squashes migrations, `--connection` targets one. */
|
|
34
|
+
export function schemaDumpCommand(
|
|
35
|
+
options: SchemaDumpCommandOptions = {},
|
|
36
|
+
): AtlasCommandClass {
|
|
37
|
+
return class SchemaDump {
|
|
38
|
+
static commandName = "schema:dump";
|
|
39
|
+
static description =
|
|
40
|
+
"Dump the database schema to a .sql file + manifest (--prune, --connection, --path)";
|
|
41
|
+
static options = { startApp: true };
|
|
42
|
+
static flags = [
|
|
43
|
+
flag("connection", "string", {
|
|
44
|
+
description: "Named connection to dump (defaults to the primary one)",
|
|
45
|
+
}),
|
|
46
|
+
flag("prune", "boolean", {
|
|
47
|
+
description: "Squash the migrations the dump replaces",
|
|
48
|
+
}),
|
|
49
|
+
flag("path", "string", {
|
|
50
|
+
description: "Destination .sql file (not a directory)",
|
|
51
|
+
}),
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
declare connection?: string;
|
|
55
|
+
declare prune: boolean;
|
|
56
|
+
declare path?: string;
|
|
57
|
+
|
|
58
|
+
async run(): Promise<void> {
|
|
59
|
+
const connName = this.connection;
|
|
60
|
+
const db = connName ? getConnection(connName) : getDb();
|
|
61
|
+
if (!db) {
|
|
62
|
+
console.error(
|
|
63
|
+
connName
|
|
64
|
+
? `[atlas] no connection registered under '${connName}'`
|
|
65
|
+
: "[atlas] no database connection — is AtlasProvider registered?",
|
|
66
|
+
);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// Typed by the kernel now — no string/boolean coercion to redo here.
|
|
71
|
+
const prune = this.prune === true;
|
|
72
|
+
// `--path <file>` is the SQL dump FILE path (Adonis Lucid), not a dir.
|
|
73
|
+
const dumpPath = this.path;
|
|
74
|
+
const dumper = new SchemaDumper(db, {
|
|
75
|
+
connectionName: connName ?? "default",
|
|
76
|
+
dumpPath,
|
|
77
|
+
outputDir: options.outputDir,
|
|
78
|
+
migrationsDir: options.migrationsDir,
|
|
79
|
+
schemaTableName: options.schemaTableName,
|
|
80
|
+
prune,
|
|
81
|
+
generatedAt: options.generatedAt,
|
|
82
|
+
});
|
|
83
|
+
await dumper.run();
|
|
84
|
+
if (dumper.error) {
|
|
85
|
+
console.error(`[atlas] schema:dump failed: ${dumper.error.message}`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const r = dumper.result;
|
|
90
|
+
console.log(
|
|
91
|
+
`Dumped ${r?.tableCount ?? 0} table(s) → ${r?.dumpPath}` +
|
|
92
|
+
(prune ? " (migrations squashed)" : ""),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|