uql-orm 0.59.0 → 0.60.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/dist/migrate/acquireQuerierForMigrations.d.ts +5 -6
- package/dist/migrate/acquireQuerierForMigrations.js +12 -8
- package/dist/migrate/builder/types.d.ts +0 -2
- package/dist/migrate/codegen/index.d.ts +1 -1
- package/dist/migrate/codegen/index.js +1 -1
- package/dist/migrate/codegen/migrationFile.d.ts +26 -3
- package/dist/migrate/codegen/migrationFile.js +29 -7
- package/dist/migrate/generator/mongoCommand.d.ts +3 -4
- package/dist/migrate/generator/mongoCommand.js +29 -3
- package/dist/migrate/index.d.ts +1 -0
- package/dist/migrate/index.js +1 -0
- package/dist/migrate/migrator.d.ts +23 -12
- package/dist/migrate/migrator.js +48 -39
- package/dist/migrate/storage/jsonStorage.d.ts +3 -3
- package/dist/migrate/storage/mongoStorage.d.ts +17 -0
- package/dist/migrate/storage/mongoStorage.js +32 -0
- package/dist/type/migration.d.ts +12 -12
- package/dist/type/querier.d.ts +5 -0
- package/dist/type/querier.js +7 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Querier, type QuerierPool, type SqlQuerier } from '../type/index.js';
|
|
1
|
+
import { type MongoQuerier, type Querier, type QuerierPool, type SqlQuerier } from '../type/index.js';
|
|
2
2
|
/**
|
|
3
|
-
* Querier used for schema migrations and the migration journal
|
|
3
|
+
* Querier used for schema migrations and the migration journal.
|
|
4
4
|
*
|
|
5
5
|
* Pools may override {@link QuerierPool.getMigrationQuerier} so DDL runs on a different target than
|
|
6
6
|
* app traffic (e.g. LibSQL embedded replica: local `file:` + remote `syncUrl`).
|
|
@@ -13,8 +13,7 @@ export declare function acquireQuerierForMigrations(pool: QuerierPool): Promise<
|
|
|
13
13
|
* traffic, but the ownership rule is the same one: whoever acquires, releases.
|
|
14
14
|
*/
|
|
15
15
|
export declare function withQuerierForMigrations<T>(pool: QuerierPool, task: (querier: Querier) => Promise<T>): Promise<T>;
|
|
16
|
-
/**
|
|
17
|
-
* Same, for the paths that only work against SQL. `requiredBy` names the caller in the error, which is
|
|
18
|
-
* the only thing the five copies of this acquire-assert-release dance used to differ by.
|
|
19
|
-
*/
|
|
16
|
+
/** Same, for the paths that only work against SQL. `requiredBy` names the caller in the error. */
|
|
20
17
|
export declare function withSqlQuerierForMigrations<T>(pool: QuerierPool, requiredBy: string, task: (querier: SqlQuerier) => Promise<T>): Promise<T>;
|
|
18
|
+
/** Same, for the paths that only work against MongoDB. */
|
|
19
|
+
export declare function withMongoQuerierForMigrations<T>(pool: QuerierPool, requiredBy: string, task: (querier: MongoQuerier) => Promise<T>): Promise<T>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { isSqlQuerier } from '../type/index.js';
|
|
1
|
+
import { isMongoQuerier, isSqlQuerier, } from '../type/index.js';
|
|
2
2
|
/**
|
|
3
|
-
* Querier used for schema migrations and the migration journal
|
|
3
|
+
* Querier used for schema migrations and the migration journal.
|
|
4
4
|
*
|
|
5
5
|
* Pools may override {@link QuerierPool.getMigrationQuerier} so DDL runs on a different target than
|
|
6
6
|
* app traffic (e.g. LibSQL embedded replica: local `file:` + remote `syncUrl`).
|
|
@@ -23,14 +23,18 @@ export async function withQuerierForMigrations(pool, task) {
|
|
|
23
23
|
await querier.release();
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Same, for the paths that only work against SQL. `requiredBy` names the caller in the error, which is
|
|
28
|
-
* the only thing the five copies of this acquire-assert-release dance used to differ by.
|
|
29
|
-
*/
|
|
26
|
+
/** Same, for the paths that only work against SQL. `requiredBy` names the caller in the error. */
|
|
30
27
|
export function withSqlQuerierForMigrations(pool, requiredBy, task) {
|
|
28
|
+
return withQuerierOfKind(pool, isSqlQuerier, `${requiredBy} requires a SQL-based querier`, task);
|
|
29
|
+
}
|
|
30
|
+
/** Same, for the paths that only work against MongoDB. */
|
|
31
|
+
export function withMongoQuerierForMigrations(pool, requiredBy, task) {
|
|
32
|
+
return withQuerierOfKind(pool, isMongoQuerier, `${requiredBy} requires a MongoDB querier`, task);
|
|
33
|
+
}
|
|
34
|
+
function withQuerierOfKind(pool, isKind, error, task) {
|
|
31
35
|
return withQuerierForMigrations(pool, (querier) => {
|
|
32
|
-
if (!
|
|
33
|
-
throw new TypeError(
|
|
36
|
+
if (!isKind(querier)) {
|
|
37
|
+
throw new TypeError(error);
|
|
34
38
|
}
|
|
35
39
|
return task(querier);
|
|
36
40
|
});
|
|
@@ -414,6 +414,4 @@ export interface IMigrationBuilder {
|
|
|
414
414
|
dropForeignKey(tableName: string, constraintName: string): Promise<void>;
|
|
415
415
|
/** Execute raw SQL */
|
|
416
416
|
raw(sql: string): Promise<void>;
|
|
417
|
-
/** Get all recorded operations */
|
|
418
|
-
getOperations(): AnyMigrationOperation[];
|
|
419
417
|
}
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { createEntityCodeGenerator, EntityCodeGenerator, type EntityCodeGeneratorOptions, type GeneratedEntity, } from './entityCodeGenerator.js';
|
|
7
7
|
export { entityTypesSource } from './entityTypes.js';
|
|
8
|
-
export { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCall, emitSqlRunCalls, type SqlMigrationModuleOptions, } from './migrationFile.js';
|
|
8
|
+
export { buildMigrationModule, buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitMongoCommandCalls, emitSqlRunCall, emitSqlRunCalls, type MigrationModuleOptions, type MigrationQuerierType, type SqlMigrationModuleOptions, } from './migrationFile.js';
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
// Entity code generator
|
|
7
7
|
export { createEntityCodeGenerator, EntityCodeGenerator, } from './entityCodeGenerator.js';
|
|
8
8
|
export { entityTypesSource } from './entityTypes.js';
|
|
9
|
-
export { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCall, emitSqlRunCalls, } from './migrationFile.js';
|
|
9
|
+
export { buildMigrationModule, buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitMongoCommandCalls, emitSqlRunCall, emitSqlRunCalls, } from './migrationFile.js';
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Source code generation for default-export
|
|
2
|
+
* Source code generation for default-export migrations (`uql-migrate`), on a `SqlQuerier` or a `MongoQuerier`.
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
/** The querier a migration module is written against. */
|
|
5
|
+
export type MigrationQuerierType = 'SqlQuerier' | 'MongoQuerier';
|
|
6
|
+
export type MigrationModuleOptions = {
|
|
5
7
|
migrationName: string;
|
|
6
8
|
createdAt: Date;
|
|
9
|
+
/** Defaults to `SqlQuerier`. */
|
|
10
|
+
querier?: MigrationQuerierType;
|
|
7
11
|
/** Extra lines in the file header comment (without leading ` * `). */
|
|
8
12
|
docExtraLines?: string[];
|
|
9
13
|
/** Indented body inside `async up` (including newlines). */
|
|
@@ -11,6 +15,8 @@ export type SqlMigrationModuleOptions = {
|
|
|
11
15
|
/** Indented body inside `async down` (including newlines). */
|
|
12
16
|
downInner: string;
|
|
13
17
|
};
|
|
18
|
+
/** @deprecated Use {@link MigrationModuleOptions}. */
|
|
19
|
+
export type SqlMigrationModuleOptions = MigrationModuleOptions;
|
|
14
20
|
/**
|
|
15
21
|
* Emit one `await querier.run(...)` line for entity-generated migrations.
|
|
16
22
|
* Uses `JSON.stringify` so SQL with backticks (SQLite/LibSQL), quotes, `${`, etc. stays valid TS source.
|
|
@@ -18,11 +24,28 @@ export type SqlMigrationModuleOptions = {
|
|
|
18
24
|
export declare function emitSqlRunCall(sql: string): string;
|
|
19
25
|
/** Indented `up`/`down` body: one `await querier.run(...)` per SQL string (entity-generated migrations, #87). */
|
|
20
26
|
export declare function emitSqlRunCalls(statements: string[]): string;
|
|
27
|
+
/** Indented `up`/`down` body: one awaited driver call on `querier.db` per MongoDB command. */
|
|
28
|
+
export declare function emitMongoCommandCalls(statements: string[]): string;
|
|
21
29
|
/** Body for `up` in a manual (empty) migration scaffold. */
|
|
22
30
|
export declare const EMPTY_MANUAL_MIGRATION_UP_INNER = " // Add your migration logic here.\n // Use one await querier.run(\"...\") per SQL statement when possible (same style as generate:entities).\n // Example (Postgres):\n // await querier.run(\"CREATE TABLE \\\"users\\\" (\\\"id\\\" SERIAL PRIMARY KEY);\");\n";
|
|
23
31
|
/** Body for `down` in a manual (empty) migration scaffold. */
|
|
24
32
|
export declare const EMPTY_MANUAL_MIGRATION_DOWN_INNER = " // Add your rollback logic here.\n // await querier.run(\"DROP TABLE IF EXISTS \\\"users\\\";\");\n";
|
|
33
|
+
/** How a migration on each querier is scaffolded empty, and how a generated statement is spelled in it. */
|
|
34
|
+
export declare const migrationSource: {
|
|
35
|
+
SqlQuerier: {
|
|
36
|
+
emptyUp: string;
|
|
37
|
+
emptyDown: string;
|
|
38
|
+
emit: typeof emitSqlRunCalls;
|
|
39
|
+
};
|
|
40
|
+
MongoQuerier: {
|
|
41
|
+
emptyUp: string;
|
|
42
|
+
emptyDown: string;
|
|
43
|
+
emit: typeof emitMongoCommandCalls;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
25
46
|
/**
|
|
26
47
|
* Full contents of a `export default { async up/down(querier) { ... } }` migration module.
|
|
27
48
|
*/
|
|
28
|
-
export declare function
|
|
49
|
+
export declare function buildMigrationModule(options: MigrationModuleOptions): string;
|
|
50
|
+
/** @deprecated Use {@link buildMigrationModule}. */
|
|
51
|
+
export declare const buildSqlQuerierMigrationModule: typeof buildMigrationModule;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Source code generation for default-export SQL migrations (`SqlQuerier` / `uql-migrate`).
|
|
3
|
-
*/
|
|
1
|
+
import { mongoCommandSource } from '../generator/mongoCommand.js';
|
|
4
2
|
/**
|
|
5
3
|
* Emit one `await querier.run(...)` line for entity-generated migrations.
|
|
6
4
|
* Uses `JSON.stringify` so SQL with backticks (SQLite/LibSQL), quotes, `${`, etc. stays valid TS source.
|
|
@@ -12,6 +10,10 @@ export function emitSqlRunCall(sql) {
|
|
|
12
10
|
export function emitSqlRunCalls(statements) {
|
|
13
11
|
return statements.map(emitSqlRunCall).join('\n');
|
|
14
12
|
}
|
|
13
|
+
/** Indented `up`/`down` body: one awaited driver call on `querier.db` per MongoDB command. */
|
|
14
|
+
export function emitMongoCommandCalls(statements) {
|
|
15
|
+
return statements.map((statement) => /*ts*/ ` await ${mongoCommandSource(statement, 'querier.db')};`).join('\n');
|
|
16
|
+
}
|
|
15
17
|
/** Body for `up` in a manual (empty) migration scaffold. */
|
|
16
18
|
export const EMPTY_MANUAL_MIGRATION_UP_INNER = ` // Add your migration logic here.
|
|
17
19
|
// Use one await querier.run("...") per SQL statement when possible (same style as generate:entities).
|
|
@@ -22,26 +24,46 @@ export const EMPTY_MANUAL_MIGRATION_UP_INNER = ` // Add your migration logic
|
|
|
22
24
|
export const EMPTY_MANUAL_MIGRATION_DOWN_INNER = ` // Add your rollback logic here.
|
|
23
25
|
// await querier.run("DROP TABLE IF EXISTS \\"users\\";");
|
|
24
26
|
`;
|
|
27
|
+
/** How a migration on each querier is scaffolded empty, and how a generated statement is spelled in it. */
|
|
28
|
+
export const migrationSource = {
|
|
29
|
+
SqlQuerier: {
|
|
30
|
+
emptyUp: EMPTY_MANUAL_MIGRATION_UP_INNER,
|
|
31
|
+
emptyDown: EMPTY_MANUAL_MIGRATION_DOWN_INNER,
|
|
32
|
+
emit: emitSqlRunCalls,
|
|
33
|
+
},
|
|
34
|
+
MongoQuerier: {
|
|
35
|
+
emptyUp: ` // Add your migration logic here, through the database handle.
|
|
36
|
+
// await querier.db.collection('users').updateMany({}, { $set: { active: true } });
|
|
37
|
+
`,
|
|
38
|
+
emptyDown: ` // Add your rollback logic here.
|
|
39
|
+
// await querier.db.collection('users').updateMany({}, { $unset: { active: '' } });
|
|
40
|
+
`,
|
|
41
|
+
emit: emitMongoCommandCalls,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
25
44
|
/**
|
|
26
45
|
* Full contents of a `export default { async up/down(querier) { ... } }` migration module.
|
|
27
46
|
*/
|
|
28
|
-
export function
|
|
47
|
+
export function buildMigrationModule(options) {
|
|
48
|
+
const querier = options.querier ?? 'SqlQuerier';
|
|
29
49
|
const iso = options.createdAt.toISOString();
|
|
30
50
|
const extra = options.docExtraLines?.map((line) => `\n * ${line}`).join('') ?? '';
|
|
31
|
-
return /*ts*/ `import type {
|
|
51
|
+
return /*ts*/ `import type { ${querier} } from 'uql-orm/migrate';
|
|
32
52
|
|
|
33
53
|
/**
|
|
34
54
|
* Migration: ${options.migrationName}
|
|
35
55
|
* Created: ${iso}${extra}
|
|
36
56
|
*/
|
|
37
57
|
export default {
|
|
38
|
-
async up(querier:
|
|
58
|
+
async up(querier: ${querier}): Promise<void> {
|
|
39
59
|
${options.upInner}
|
|
40
60
|
},
|
|
41
61
|
|
|
42
|
-
async down(querier:
|
|
62
|
+
async down(querier: ${querier}): Promise<void> {
|
|
43
63
|
${options.downInner}
|
|
44
64
|
},
|
|
45
65
|
};
|
|
46
66
|
`;
|
|
47
67
|
}
|
|
68
|
+
/** @deprecated Use {@link buildMigrationModule}. */
|
|
69
|
+
export const buildSqlQuerierMigrationModule = buildMigrationModule;
|
|
@@ -50,8 +50,7 @@ export type MongoCommandTarget = {
|
|
|
50
50
|
dropIndex(name: string): Promise<unknown>;
|
|
51
51
|
};
|
|
52
52
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Execute one emitted command. The single cast lives here, where the union it casts to is defined
|
|
55
|
-
* alongside the only code that writes these strings.
|
|
56
|
-
*/
|
|
53
|
+
/** Execute one emitted command. */
|
|
57
54
|
export declare function runMongoCommand(db: MongoCommandTarget, statement: string): Promise<unknown>;
|
|
55
|
+
/** The driver call {@link runMongoCommand} makes, as source on the handle `db` names, for a generated migration. */
|
|
56
|
+
export declare function mongoCommandSource(statement: string, db: string): string;
|
|
@@ -2,11 +2,18 @@ export function serializeMongoCommand(command) {
|
|
|
2
2
|
return JSON.stringify(command);
|
|
3
3
|
}
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Read one emitted command back. The single cast lives here, where the union it casts to is defined
|
|
6
6
|
* alongside the only code that writes these strings.
|
|
7
7
|
*/
|
|
8
|
+
function parseMongoCommand(statement) {
|
|
9
|
+
return JSON.parse(statement);
|
|
10
|
+
}
|
|
11
|
+
function unsupportedMongoCommand(statement) {
|
|
12
|
+
return new TypeError(`unsupported MongoDB migration command: ${statement}`);
|
|
13
|
+
}
|
|
14
|
+
/** Execute one emitted command. */
|
|
8
15
|
export function runMongoCommand(db, statement) {
|
|
9
|
-
const command =
|
|
16
|
+
const command = parseMongoCommand(statement);
|
|
10
17
|
switch (command.action) {
|
|
11
18
|
case 'createCollection':
|
|
12
19
|
return db.createCollection(command.name);
|
|
@@ -21,6 +28,25 @@ export function runMongoCommand(db, statement) {
|
|
|
21
28
|
default:
|
|
22
29
|
// Unreachable for a command this module produced; a hand-written statement lands here rather
|
|
23
30
|
// than being silently skipped, which is how `renameCollection` went unnoticed.
|
|
24
|
-
throw
|
|
31
|
+
throw unsupportedMongoCommand(statement);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** The driver call {@link runMongoCommand} makes, as source on the handle `db` names, for a generated migration. */
|
|
35
|
+
export function mongoCommandSource(statement, db) {
|
|
36
|
+
const command = parseMongoCommand(statement);
|
|
37
|
+
const literal = JSON.stringify;
|
|
38
|
+
switch (command.action) {
|
|
39
|
+
case 'createCollection':
|
|
40
|
+
return `${db}.createCollection(${literal(command.name)})`;
|
|
41
|
+
case 'dropCollection':
|
|
42
|
+
return `${db}.collection(${literal(command.name)}).drop()`;
|
|
43
|
+
case 'renameCollection':
|
|
44
|
+
return `${db}.renameCollection(${literal(command.from)}, ${literal(command.to)})`;
|
|
45
|
+
case 'createIndex':
|
|
46
|
+
return `${db}.collection(${literal(command.collection)}).createIndex(${literal(command.key)}, ${literal(command.options)})`;
|
|
47
|
+
case 'dropIndex':
|
|
48
|
+
return `${db}.collection(${literal(command.collection)}).dropIndex(${literal(command.name)})`;
|
|
49
|
+
default:
|
|
50
|
+
throw unsupportedMongoCommand(statement);
|
|
25
51
|
}
|
|
26
52
|
}
|
package/dist/migrate/index.d.ts
CHANGED
|
@@ -13,3 +13,4 @@ export { createSchemaGenerator, SqlSchemaGenerator } from './schemaGenerator.js'
|
|
|
13
13
|
export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
14
14
|
export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
15
15
|
export { JsonMigrationStorage } from './storage/jsonStorage.js';
|
|
16
|
+
export { MongoMigrationStorage } from './storage/mongoStorage.js';
|
package/dist/migrate/index.js
CHANGED
|
@@ -20,4 +20,5 @@ export { createSchemaGeneratorAsync } from './schemaGeneratorAsync.js';
|
|
|
20
20
|
// Storage implementations
|
|
21
21
|
export { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
22
22
|
export { JsonMigrationStorage } from './storage/jsonStorage.js';
|
|
23
|
+
export { MongoMigrationStorage } from './storage/mongoStorage.js';
|
|
23
24
|
// Schema sync
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorDialect, MigratorOptions, MongoQuerier, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, SyncOptions, Type } from '../type/index.js';
|
|
1
|
+
import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorDialect, MigratorOptions, MongoQuerier, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlQuerier, SyncOptions, Type } from '../type/index.js';
|
|
2
2
|
import { LoggerWrapper } from '../util/index.js';
|
|
3
3
|
import type { IMigrationBuilder } from './builder/types.js';
|
|
4
4
|
/**
|
|
@@ -34,11 +34,11 @@ export declare class Migrator {
|
|
|
34
34
|
/**
|
|
35
35
|
* Get all discovered migrations from the migrations directory
|
|
36
36
|
*/
|
|
37
|
-
getMigrations(): Promise<Migration[]>;
|
|
37
|
+
getMigrations(): Promise<Migration<Querier>[]>;
|
|
38
38
|
/**
|
|
39
39
|
* Get list of pending migrations (not yet executed)
|
|
40
40
|
*/
|
|
41
|
-
pending(): Promise<Migration[]>;
|
|
41
|
+
pending(): Promise<Migration<Querier>[]>;
|
|
42
42
|
/**
|
|
43
43
|
* Get list of executed migrations
|
|
44
44
|
*/
|
|
@@ -66,13 +66,23 @@ export declare class Migrator {
|
|
|
66
66
|
*/
|
|
67
67
|
private runInOrder;
|
|
68
68
|
/**
|
|
69
|
-
* Run a single migration within a transaction
|
|
69
|
+
* Run a single migration, within a transaction where the dialect has one for it
|
|
70
70
|
*/
|
|
71
|
-
runMigration(migration: Migration
|
|
71
|
+
runMigration(migration: Migration<Querier>, direction: 'up' | 'down'): Promise<MigrationResult>;
|
|
72
|
+
/**
|
|
73
|
+
* A migration querier, and how to run work in one transaction on it. MongoDB gets none: it creates
|
|
74
|
+
* collections and indexes outside any transaction. SQL asserts its querier before opening one, so a
|
|
75
|
+
* wrong querier reports which one the dialect needs rather than a missing `transaction`.
|
|
76
|
+
*/
|
|
77
|
+
private withMigrationQuerier;
|
|
78
|
+
/** What this dialect's migration files are written against. */
|
|
79
|
+
private get migrationQuerier();
|
|
72
80
|
/**
|
|
73
81
|
* Generate a new migration file
|
|
74
82
|
*/
|
|
75
83
|
generate(name: string): Promise<string>;
|
|
84
|
+
/** Writes a migration module on this dialect's querier to a new timestamped file, and returns its path. */
|
|
85
|
+
private writeMigration;
|
|
76
86
|
/**
|
|
77
87
|
* Generate a migration based on entity schema differences
|
|
78
88
|
*/
|
|
@@ -174,11 +184,11 @@ export declare class Migrator {
|
|
|
174
184
|
/**
|
|
175
185
|
* Load a migration from a file
|
|
176
186
|
*/
|
|
177
|
-
loadMigration(fileName: string): Promise<Migration | undefined>;
|
|
187
|
+
loadMigration(fileName: string): Promise<Migration<Querier> | undefined>;
|
|
178
188
|
/**
|
|
179
189
|
* Check if an object is a valid migration
|
|
180
190
|
*/
|
|
181
|
-
isMigration(obj: unknown): obj is MigrationDefinition
|
|
191
|
+
isMigration(obj: unknown): obj is MigrationDefinition<Querier>;
|
|
182
192
|
/**
|
|
183
193
|
* Extract migration name from filename
|
|
184
194
|
*/
|
|
@@ -195,14 +205,15 @@ export declare class Migrator {
|
|
|
195
205
|
/**
|
|
196
206
|
* Helper function to define a migration with proper typing
|
|
197
207
|
*/
|
|
198
|
-
export declare function defineMigration(migration: MigrationDefinition): MigrationDefinition
|
|
208
|
+
export declare function defineMigration<Q extends Querier = SqlQuerier>(migration: MigrationDefinition<Q>): MigrationDefinition<Q>;
|
|
199
209
|
/**
|
|
200
|
-
* Migration definition that uses the type-safe builder API.
|
|
210
|
+
* Migration definition that uses the type-safe builder API. The querier is the builder's own, so a
|
|
211
|
+
* data backfill it runs lands in the same transaction as the schema change.
|
|
201
212
|
*/
|
|
202
213
|
export interface BuilderMigrationDefinition {
|
|
203
214
|
readonly name?: string;
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
up(builder: IMigrationBuilder, querier: SqlQuerier): Promise<void>;
|
|
216
|
+
down(builder: IMigrationBuilder, querier: SqlQuerier): Promise<void>;
|
|
206
217
|
}
|
|
207
218
|
/**
|
|
208
219
|
* Define a migration using the type-safe builder API.
|
|
@@ -223,4 +234,4 @@ export interface BuilderMigrationDefinition {
|
|
|
223
234
|
* });
|
|
224
235
|
* ```
|
|
225
236
|
*/
|
|
226
|
-
export declare function defineBuilderMigration(migration: BuilderMigrationDefinition):
|
|
237
|
+
export declare function defineBuilderMigration(migration: BuilderMigrationDefinition): MigrationDefinition;
|
package/dist/migrate/migrator.js
CHANGED
|
@@ -3,14 +3,16 @@ import { basename, extname, join } from 'node:path';
|
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { getEntities, getMeta } from '../entity/index.js';
|
|
5
5
|
import { introspectSchema, SchemaAST } from '../schema/index.js';
|
|
6
|
-
import { isKnownMigratorDialect, isSqlQuerier } from '../type/index.js';
|
|
6
|
+
import { isKnownMigratorDialect, isMongoQuerier, isSqlQuerier } from '../type/index.js';
|
|
7
7
|
import { LoggerWrapper } from '../util/index.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { withMongoQuerierForMigrations, withSqlQuerierForMigrations } from './acquireQuerierForMigrations.js';
|
|
9
|
+
import { MigrationBuilder } from './builder/migrationBuilder.js';
|
|
10
|
+
import { buildMigrationModule, migrationSource, } from './codegen/migrationFile.js';
|
|
10
11
|
import { runMongoCommand } from './generator/mongoCommand.js';
|
|
11
12
|
import { introspectorFor } from './introspection/registry.js';
|
|
12
13
|
import { createSchemaGenerator } from './schemaGenerator.js';
|
|
13
14
|
import { DatabaseMigrationStorage } from './storage/databaseStorage.js';
|
|
15
|
+
import { MongoMigrationStorage } from './storage/mongoStorage.js';
|
|
14
16
|
/**
|
|
15
17
|
* Main class for managing database migrations
|
|
16
18
|
*/
|
|
@@ -40,9 +42,9 @@ export class Migrator {
|
|
|
40
42
|
this._defaultForeignKeyAction = options.defaultForeignKeyAction;
|
|
41
43
|
this.storage =
|
|
42
44
|
options.storage ??
|
|
43
|
-
|
|
44
|
-
tableName: options.tableName
|
|
45
|
-
|
|
45
|
+
(this.dialectName === 'mongodb'
|
|
46
|
+
? new MongoMigrationStorage(pool, { tableName: options.tableName })
|
|
47
|
+
: new DatabaseMigrationStorage(pool, { tableName: options.tableName }));
|
|
46
48
|
this.migrationsPath = options.migrationsPath ?? './migrations';
|
|
47
49
|
this._logger = new LoggerWrapper(options.logger, { logValues: options.logValues, slowQuery: options.slowQuery });
|
|
48
50
|
this._entities = options.entities;
|
|
@@ -153,22 +155,20 @@ export class Migrator {
|
|
|
153
155
|
return results;
|
|
154
156
|
}
|
|
155
157
|
/**
|
|
156
|
-
* Run a single migration within a transaction
|
|
158
|
+
* Run a single migration, within a transaction where the dialect has one for it
|
|
157
159
|
*/
|
|
158
160
|
async runMigration(migration, direction) {
|
|
159
161
|
const startTime = Date.now();
|
|
160
|
-
return
|
|
162
|
+
return this.withMigrationQuerier(async (querier, inTransaction) => {
|
|
161
163
|
try {
|
|
162
164
|
this.logger.logMigration(`${direction === 'up' ? 'Running' : 'Reverting'} migration: ${migration.name}`);
|
|
163
|
-
await
|
|
165
|
+
await inTransaction(async () => {
|
|
164
166
|
if (direction === 'up') {
|
|
165
167
|
await migration.up(querier);
|
|
166
|
-
// Log within the same transaction
|
|
167
168
|
await this.storage.logWithQuerier(querier, migration.name);
|
|
168
169
|
}
|
|
169
170
|
else {
|
|
170
171
|
await migration.down(querier);
|
|
171
|
-
// Unlog within the same transaction
|
|
172
172
|
await this.storage.unlogWithQuerier(querier, migration.name);
|
|
173
173
|
}
|
|
174
174
|
});
|
|
@@ -194,22 +194,40 @@ export class Migrator {
|
|
|
194
194
|
}
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* A migration querier, and how to run work in one transaction on it. MongoDB gets none: it creates
|
|
199
|
+
* collections and indexes outside any transaction. SQL asserts its querier before opening one, so a
|
|
200
|
+
* wrong querier reports which one the dialect needs rather than a missing `transaction`.
|
|
201
|
+
*/
|
|
202
|
+
withMigrationQuerier(task) {
|
|
203
|
+
return this.dialectName === 'mongodb'
|
|
204
|
+
? withMongoQuerierForMigrations(this.pool, 'Migrator', (querier) => task(querier, (work) => work()))
|
|
205
|
+
: withSqlQuerierForMigrations(this.pool, 'Migrator', (querier) => task(querier, (work) => querier.transaction(work)));
|
|
206
|
+
}
|
|
207
|
+
/** What this dialect's migration files are written against. */
|
|
208
|
+
get migrationQuerier() {
|
|
209
|
+
return this.dialectName === 'mongodb' ? 'MongoQuerier' : 'SqlQuerier';
|
|
210
|
+
}
|
|
197
211
|
/**
|
|
198
212
|
* Generate a new migration file
|
|
199
213
|
*/
|
|
200
214
|
async generate(name) {
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
const { emptyUp, emptyDown } = migrationSource[this.migrationQuerier];
|
|
216
|
+
const filePath = await this.writeMigration(name, { upInner: emptyUp, downInner: emptyDown });
|
|
217
|
+
this.logger.logInfo(`Created migration: ${filePath}`);
|
|
218
|
+
return filePath;
|
|
219
|
+
}
|
|
220
|
+
/** Writes a migration module on this dialect's querier to a new timestamped file, and returns its path. */
|
|
221
|
+
async writeMigration(name, body) {
|
|
222
|
+
const filePath = join(this.migrationsPath, `${this.getTimestamp()}_${this.slugify(name)}.ts`);
|
|
223
|
+
const content = buildMigrationModule({
|
|
205
224
|
migrationName: name,
|
|
206
225
|
createdAt: new Date(),
|
|
207
|
-
|
|
208
|
-
|
|
226
|
+
querier: this.migrationQuerier,
|
|
227
|
+
...body,
|
|
209
228
|
});
|
|
210
229
|
await mkdir(this.migrationsPath, { recursive: true });
|
|
211
230
|
await writeFile(filePath, content, 'utf-8');
|
|
212
|
-
this.logger.logInfo(`Created migration: ${filePath}`);
|
|
213
231
|
return filePath;
|
|
214
232
|
}
|
|
215
233
|
/**
|
|
@@ -236,19 +254,12 @@ export class Migrator {
|
|
|
236
254
|
this.logger.logInfo('No schema changes detected.');
|
|
237
255
|
return '';
|
|
238
256
|
}
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
const filePath = join(this.migrationsPath, fileName);
|
|
242
|
-
const down = [...downStatements].reverse();
|
|
243
|
-
const content = buildSqlQuerierMigrationModule({
|
|
244
|
-
migrationName: name,
|
|
245
|
-
createdAt: new Date(),
|
|
257
|
+
const { emit } = migrationSource[this.migrationQuerier];
|
|
258
|
+
const filePath = await this.writeMigration(name, {
|
|
246
259
|
docExtraLines: ['Generated from entity definitions'],
|
|
247
|
-
upInner:
|
|
248
|
-
downInner:
|
|
260
|
+
upInner: emit(upStatements),
|
|
261
|
+
downInner: emit([...downStatements].reverse()),
|
|
249
262
|
});
|
|
250
|
-
await mkdir(this.migrationsPath, { recursive: true });
|
|
251
|
-
await writeFile(filePath, content, 'utf-8');
|
|
252
263
|
this.logger.logInfo(`Created migration from entities: ${filePath}`);
|
|
253
264
|
return filePath;
|
|
254
265
|
}
|
|
@@ -474,15 +485,9 @@ export class Migrator {
|
|
|
474
485
|
return filteredDiff;
|
|
475
486
|
}
|
|
476
487
|
async executeSyncStatements(statements, options) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (this.dialectName === 'mongodb') {
|
|
481
|
-
await withQuerierForMigrations(this.pool, (querier) => this.executeMongoSyncStatements(statements, options, querier));
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
await withSqlQuerierForMigrations(this.pool, 'Migrator', (querier) => querier.transaction(() => this.executeSqlSyncStatements(statements, options, querier)));
|
|
485
|
-
}
|
|
488
|
+
await this.withMigrationQuerier((querier, inTransaction) => inTransaction(() => isMongoQuerier(querier)
|
|
489
|
+
? this.executeMongoSyncStatements(statements, options, querier)
|
|
490
|
+
: this.executeSqlSyncStatements(statements, options, querier)));
|
|
486
491
|
if (options.logging)
|
|
487
492
|
this.logger.logSchema('Schema synchronization completed');
|
|
488
493
|
}
|
|
@@ -618,5 +623,9 @@ export function defineMigration(migration) {
|
|
|
618
623
|
* ```
|
|
619
624
|
*/
|
|
620
625
|
export function defineBuilderMigration(migration) {
|
|
621
|
-
return
|
|
626
|
+
return {
|
|
627
|
+
...migration,
|
|
628
|
+
up: (querier) => migration.up(new MigrationBuilder(querier), querier),
|
|
629
|
+
down: (querier) => migration.down(new MigrationBuilder(querier), querier),
|
|
630
|
+
};
|
|
622
631
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MigrationStorage,
|
|
1
|
+
import type { MigrationStorage, Querier } from '../../type/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Stores migration state in a JSON file.
|
|
4
4
|
* Useful for development or environments without a database.
|
|
@@ -9,7 +9,7 @@ export declare class JsonMigrationStorage implements MigrationStorage {
|
|
|
9
9
|
constructor(filePath?: string);
|
|
10
10
|
ensureStorage(): Promise<void>;
|
|
11
11
|
executed(): Promise<string[]>;
|
|
12
|
-
logWithQuerier(_querier:
|
|
13
|
-
unlogWithQuerier(_querier:
|
|
12
|
+
logWithQuerier(_querier: Querier, migrationName: string): Promise<void>;
|
|
13
|
+
unlogWithQuerier(_querier: Querier, migrationName: string): Promise<void>;
|
|
14
14
|
private save;
|
|
15
15
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MigrationStorage, MongoQuerier, QuerierPool } from '../../type/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stores migration state in a MongoDB collection, named as the SQL table would be.
|
|
4
|
+
*/
|
|
5
|
+
export declare class MongoMigrationStorage implements MigrationStorage {
|
|
6
|
+
private readonly pool;
|
|
7
|
+
private readonly collectionName;
|
|
8
|
+
constructor(pool: QuerierPool, options?: {
|
|
9
|
+
tableName?: string;
|
|
10
|
+
});
|
|
11
|
+
/** Nothing to prepare: MongoDB creates the collection on its first insert. */
|
|
12
|
+
ensureStorage(): Promise<void>;
|
|
13
|
+
executed(): Promise<string[]>;
|
|
14
|
+
logWithQuerier(querier: MongoQuerier, migrationName: string): Promise<void>;
|
|
15
|
+
unlogWithQuerier(querier: MongoQuerier, migrationName: string): Promise<void>;
|
|
16
|
+
private collection;
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { withMongoQuerierForMigrations } from '../acquireQuerierForMigrations.js';
|
|
2
|
+
import { DEFAULT_MIGRATIONS_TABLE } from './databaseStorage.js';
|
|
3
|
+
/**
|
|
4
|
+
* Stores migration state in a MongoDB collection, named as the SQL table would be.
|
|
5
|
+
*/
|
|
6
|
+
export class MongoMigrationStorage {
|
|
7
|
+
pool;
|
|
8
|
+
collectionName;
|
|
9
|
+
constructor(pool, options = {}) {
|
|
10
|
+
this.pool = pool;
|
|
11
|
+
this.collectionName = options.tableName ?? DEFAULT_MIGRATIONS_TABLE;
|
|
12
|
+
}
|
|
13
|
+
/** Nothing to prepare: MongoDB creates the collection on its first insert. */
|
|
14
|
+
async ensureStorage() { }
|
|
15
|
+
executed() {
|
|
16
|
+
return withMongoQuerierForMigrations(this.pool, 'MongoMigrationStorage', async (querier) => {
|
|
17
|
+
const documents = await this.collection(querier)
|
|
18
|
+
.find({}, { sort: { _id: 1 } })
|
|
19
|
+
.toArray();
|
|
20
|
+
return documents.map((document) => document._id);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async logWithQuerier(querier, migrationName) {
|
|
24
|
+
await this.collection(querier).insertOne({ _id: migrationName, executed_at: new Date() });
|
|
25
|
+
}
|
|
26
|
+
async unlogWithQuerier(querier, migrationName) {
|
|
27
|
+
await this.collection(querier).deleteOne({ _id: migrationName });
|
|
28
|
+
}
|
|
29
|
+
collection(querier) {
|
|
30
|
+
return querier.db.collection(this.collectionName);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/dist/type/migration.d.ts
CHANGED
|
@@ -3,19 +3,19 @@ import type { FullColumnDefinition, TableDefinition } from '../migrate/builder/t
|
|
|
3
3
|
import type { IndexFacet } from '../schema/indexDifferences.js';
|
|
4
4
|
import type { SchemaAST } from '../schema/schemaAST.js';
|
|
5
5
|
import type { ColumnNode, ForeignKeyAction, IndexNode, IndexType, TableNode } from '../schema/types.js';
|
|
6
|
-
import type { EntityMeta, FieldOptions, IndexColumnSchema, LoggingOptions, SqlQuerier, Type, VectorIndexOptions } from './index.js';
|
|
6
|
+
import type { EntityMeta, FieldOptions, IndexColumnSchema, LoggingOptions, Querier, SqlQuerier, Type, VectorIndexOptions } from './index.js';
|
|
7
7
|
/**
|
|
8
|
-
* Defines a migration using a simple object literal
|
|
8
|
+
* Defines a migration using a simple object literal. `Q` is `MongoQuerier` for a MongoDB migration.
|
|
9
9
|
*/
|
|
10
|
-
export interface MigrationDefinition {
|
|
10
|
+
export interface MigrationDefinition<Q extends Querier = SqlQuerier> {
|
|
11
11
|
readonly name?: string;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
up(querier: Q): Promise<void>;
|
|
13
|
+
down(querier: Q): Promise<void>;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Represents a single database migration
|
|
17
17
|
*/
|
|
18
|
-
export interface Migration extends MigrationDefinition {
|
|
18
|
+
export interface Migration<Q extends Querier = SqlQuerier> extends MigrationDefinition<Q> {
|
|
19
19
|
/**
|
|
20
20
|
* Unique name/identifier for this migration (typically timestamp + description)
|
|
21
21
|
*/
|
|
@@ -30,13 +30,13 @@ export interface MigrationStorage {
|
|
|
30
30
|
*/
|
|
31
31
|
executed(): Promise<string[]>;
|
|
32
32
|
/**
|
|
33
|
-
* Mark a migration as executed (
|
|
33
|
+
* Mark a migration as executed, on the querier that ran it (inside its transaction, where there is one)
|
|
34
34
|
*/
|
|
35
|
-
logWithQuerier(querier:
|
|
35
|
+
logWithQuerier(querier: Querier, migrationName: string): Promise<void>;
|
|
36
36
|
/**
|
|
37
|
-
* Remove a migration from the executed list
|
|
37
|
+
* Remove a migration from the executed list, on the querier that reverted it
|
|
38
38
|
*/
|
|
39
|
-
unlogWithQuerier(querier:
|
|
39
|
+
unlogWithQuerier(querier: Querier, migrationName: string): Promise<void>;
|
|
40
40
|
/**
|
|
41
41
|
* Ensure the storage is initialized (e.g., create migrations table)
|
|
42
42
|
*/
|
|
@@ -51,11 +51,11 @@ export interface MigratorOptions {
|
|
|
51
51
|
*/
|
|
52
52
|
readonly migrationsPath?: string;
|
|
53
53
|
/**
|
|
54
|
-
* Custom storage implementation. Defaults to DatabaseMigrationStorage.
|
|
54
|
+
* Custom storage implementation. Defaults to DatabaseMigrationStorage, or MongoMigrationStorage on MongoDB.
|
|
55
55
|
*/
|
|
56
56
|
readonly storage?: MigrationStorage;
|
|
57
57
|
/**
|
|
58
|
-
* Table name for storing migration state. Defaults to 'uql_migrations'.
|
|
58
|
+
* Table, or MongoDB collection, name for storing migration state. Defaults to 'uql_migrations'.
|
|
59
59
|
*/
|
|
60
60
|
readonly tableName?: string;
|
|
61
61
|
/**
|
package/dist/type/querier.d.ts
CHANGED
|
@@ -144,6 +144,11 @@ export interface MongoQuerier extends Querier {
|
|
|
144
144
|
*/
|
|
145
145
|
readonly db: Db;
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Type guard for a querier over a MongoDB database. A handle alone does not tell: the SQLite, D1 and
|
|
149
|
+
* Turso queriers carry a `db` of their own.
|
|
150
|
+
*/
|
|
151
|
+
export declare function isMongoQuerier(querier: Querier): querier is MongoQuerier;
|
|
147
152
|
/**
|
|
148
153
|
* Context passed to global querier listeners.
|
|
149
154
|
*/
|
package/dist/type/querier.js
CHANGED
|
@@ -8,3 +8,10 @@ export function isSqlQuerier(querier) {
|
|
|
8
8
|
q.dialect !== undefined &&
|
|
9
9
|
typeof q.dialect.escapeIdChar === 'string');
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Type guard for a querier over a MongoDB database. A handle alone does not tell: the SQLite, D1 and
|
|
13
|
+
* Turso queriers carry a `db` of their own.
|
|
14
|
+
*/
|
|
15
|
+
export function isMongoQuerier(querier) {
|
|
16
|
+
return 'db' in querier && !isSqlQuerier(querier);
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://uql-orm.dev",
|
|
4
4
|
"description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, SQL Server, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.60.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|