@deterministic-code/migraters 0.0.0-stage → 0.0.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/bundle.yaml +262 -0
- package/dist/abstractions/migrater.d.ts +28 -0
- package/dist/abstractions/migrater.js +1 -0
- package/dist/abstractions/sql-dialect-base.d.ts +16 -0
- package/dist/abstractions/sql-dialect-base.js +19 -0
- package/dist/abstractions/sql-dialect-factory.d.ts +8 -0
- package/dist/abstractions/sql-dialect-factory.js +22 -0
- package/dist/abstractions/sql-dialect.d.ts +16 -0
- package/dist/abstractions/sql-dialect.js +1 -0
- package/dist/bin/migrate-create.d.ts +2 -0
- package/dist/bin/migrate-create.js +71 -0
- package/dist/bin/migrate-down.d.ts +2 -0
- package/dist/bin/migrate-down.js +135 -0
- package/dist/bin/migrate-init.d.ts +2 -0
- package/dist/bin/migrate-init.js +77 -0
- package/dist/bin/migrate-setup.d.ts +2 -0
- package/dist/bin/migrate-setup.js +87 -0
- package/dist/bin/migrate-up.d.ts +2 -0
- package/dist/bin/migrate-up.js +97 -0
- package/dist/cli-contract.d.ts +6 -0
- package/dist/cli-contract.js +19 -0
- package/dist/cli-spec.d.ts +24 -0
- package/dist/cli-spec.js +33 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +41 -0
- package/dist/create-sequence.d.ts +4 -0
- package/dist/create-sequence.js +16 -0
- package/dist/datasource-migrate.d.ts +31 -0
- package/dist/datasource-migrate.js +206 -0
- package/dist/dialects/mysql-dialect.d.ts +12 -0
- package/dist/dialects/mysql-dialect.js +56 -0
- package/dist/dialects/oracle-dialect.d.ts +18 -0
- package/dist/dialects/oracle-dialect.js +76 -0
- package/dist/dialects/postgres-dialect.d.ts +11 -0
- package/dist/dialects/postgres-dialect.js +58 -0
- package/dist/dialects/sqlite-dialect.d.ts +13 -0
- package/dist/dialects/sqlite-dialect.js +96 -0
- package/dist/dialects/sqlserver-dialect.d.ts +12 -0
- package/dist/dialects/sqlserver-dialect.js +73 -0
- package/dist/help.d.ts +2 -0
- package/dist/help.js +10 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/infrastructure/connection-resolver.d.ts +7 -0
- package/dist/infrastructure/connection-resolver.js +19 -0
- package/dist/infrastructure/default-factory.d.ts +4 -0
- package/dist/infrastructure/default-factory.js +15 -0
- package/dist/infrastructure/interop-default.d.ts +3 -0
- package/dist/infrastructure/interop-default.js +1 -0
- package/dist/infrastructure/message-templates.d.ts +3 -0
- package/dist/infrastructure/message-templates.js +18 -0
- package/dist/infrastructure/split-on-go.d.ts +2 -0
- package/dist/infrastructure/split-on-go.js +20 -0
- package/dist/infrastructure/sql-templates.d.ts +2 -0
- package/dist/infrastructure/sql-templates.js +4 -0
- package/dist/infrastructure/templates-root.d.ts +2 -0
- package/dist/infrastructure/templates-root.js +11 -0
- package/dist/init-select.d.ts +12 -0
- package/dist/init-select.js +52 -0
- package/dist/migrater.d.ts +12 -0
- package/dist/migrater.js +84 -0
- package/dist/migration-client.d.ts +21 -0
- package/dist/migration-client.js +1 -0
- package/dist/path-exists.d.ts +1 -0
- package/dist/path-exists.js +10 -0
- package/dist/sql.d.ts +3 -0
- package/dist/sql.js +15 -0
- package/dist/templates/help/create.txt +16 -0
- package/dist/templates/help/down.txt +42 -0
- package/dist/templates/help/init.txt +13 -0
- package/dist/templates/help/setup.txt +41 -0
- package/dist/templates/help/up.txt +43 -0
- package/dist/templates/messages/checksum-drift-line.txt +1 -0
- package/dist/templates/messages/checksum-drift.txt +11 -0
- package/dist/templates/messages/down/confirm-mismatch.txt +1 -0
- package/dist/templates/messages/down/confirm-prompt.txt +1 -0
- package/dist/templates/messages/down/confirm-stdin-failed.txt +1 -0
- package/dist/templates/messages/down/confirm-token-mismatch.txt +1 -0
- package/dist/templates/messages/down/confirm-tty-required.txt +1 -0
- package/dist/templates/messages/down/destructive-header.txt +3 -0
- package/dist/templates/messages/errors/invalid-name.txt +1 -0
- package/dist/templates/messages/errors/migrate-path-not-dir.txt +1 -0
- package/dist/templates/messages/errors/missing-connection.txt +1 -0
- package/dist/templates/messages/errors/missing-name-hint.txt +1 -0
- package/dist/templates/messages/errors/missing-name.txt +1 -0
- package/dist/templates/messages/errors/missing-provider-hint.txt +1 -0
- package/dist/templates/messages/errors/missing-provider.txt +1 -0
- package/dist/templates/messages/errors/missing-value-for-flag.txt +1 -0
- package/dist/templates/messages/errors/rollback-no-down-sibling.txt +1 -0
- package/dist/templates/messages/errors/rollback-no-rollback-sibling.txt +1 -0
- package/dist/templates/messages/errors/rollback-not-in-path.txt +1 -0
- package/dist/templates/messages/errors/sqlite-prerequisite.txt +1 -0
- package/dist/templates/messages/errors/unknown-arg.txt +1 -0
- package/dist/templates/messages/errors/unsupported-provider.txt +1 -0
- package/dist/templates/messages/setup-complete.txt +9 -0
- package/dist/templates/messages/status/applied.txt +1 -0
- package/dist/templates/messages/status/created.txt +1 -0
- package/dist/templates/messages/status/no-more-pending.txt +1 -0
- package/dist/templates/messages/status/no-pending.txt +1 -0
- package/dist/templates/messages/status/no-rollback.txt +1 -0
- package/dist/templates/messages/status/rolled-back.txt +1 -0
- package/dist/templates/scaffold/down.sql +1 -0
- package/dist/templates/scaffold/up.sql +1 -0
- package/dist/templates/sql/mysql/migrate_logs.sql +11 -0
- package/dist/templates/sql/mysql/migrates.sql +7 -0
- package/dist/templates/sql/oracle/migrate_logs.sql +14 -0
- package/dist/templates/sql/oracle/migrates.sql +10 -0
- package/dist/templates/sql/postgres/migrate_logs.sql +11 -0
- package/dist/templates/sql/postgres/migrates.sql +7 -0
- package/dist/templates/sql/sqlite/migrate_logs.sql +11 -0
- package/dist/templates/sql/sqlite/migrates.sql +7 -0
- package/dist/templates/sql/sqlserver/migrate_logs.sql +12 -0
- package/dist/templates/sql/sqlserver/migrates.sql +8 -0
- package/package.json +78 -4
- package/src/abstractions/migrater.ts +13 -0
- package/src/abstractions/sql-dialect-base.ts +36 -0
- package/src/abstractions/sql-dialect-factory.ts +29 -0
- package/src/abstractions/sql-dialect.ts +17 -0
- package/src/bin/migrate-create.ts +80 -0
- package/src/bin/migrate-down.ts +163 -0
- package/src/bin/migrate-init.ts +88 -0
- package/src/bin/migrate-setup.ts +98 -0
- package/src/bin/migrate-up.ts +111 -0
- package/src/cli-contract.ts +25 -0
- package/src/cli-spec.ts +64 -0
- package/src/cli.ts +52 -0
- package/src/create-sequence.ts +17 -0
- package/src/datasource-migrate.ts +314 -0
- package/src/dialects/mysql-dialect.test.ts +93 -0
- package/src/dialects/mysql-dialect.ts +67 -0
- package/src/dialects/oracle-dialect.test.ts +118 -0
- package/src/dialects/oracle-dialect.ts +91 -0
- package/src/dialects/postgres-dialect.test.ts +80 -0
- package/src/dialects/postgres-dialect.ts +65 -0
- package/src/dialects/sqlite-dialect.rollback.test.ts +30 -0
- package/src/dialects/sqlite-dialect.test.ts +77 -0
- package/src/dialects/sqlite-dialect.ts +96 -0
- package/src/dialects/sqlserver-dialect.test.ts +99 -0
- package/src/dialects/sqlserver-dialect.ts +87 -0
- package/src/generate-help.test.ts +33 -0
- package/src/generate-help.ts +173 -0
- package/src/help.test.ts +48 -0
- package/src/help.ts +12 -0
- package/src/index.ts +23 -0
- package/src/infrastructure/connection-resolver.ts +18 -0
- package/src/infrastructure/default-factory.test.ts +18 -0
- package/src/infrastructure/default-factory.ts +17 -0
- package/src/infrastructure/interop-default.test.ts +9 -0
- package/src/infrastructure/interop-default.ts +2 -0
- package/src/infrastructure/message-templates.ts +30 -0
- package/src/infrastructure/split-on-go.ts +19 -0
- package/src/infrastructure/sql-templates.ts +10 -0
- package/src/infrastructure/templates-root.ts +13 -0
- package/src/init-select.test.ts +26 -0
- package/src/init-select.ts +73 -0
- package/src/migrater.ts +92 -0
- package/src/migration-client.ts +21 -0
- package/src/path-exists.ts +10 -0
- package/src/setup-sql.test.ts +16 -0
- package/src/sql.ts +17 -0
- package/tsconfig.build.json +19 -0
- package/README.md +0 -3
package/src/migrater.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import type {
|
|
3
|
+
CreateArg,
|
|
4
|
+
CreateResponse,
|
|
5
|
+
DownArg,
|
|
6
|
+
IMigrater,
|
|
7
|
+
SetupArg,
|
|
8
|
+
UpArg,
|
|
9
|
+
} from './abstractions/migrater.ts';
|
|
10
|
+
import type { ISqlDialect } from './abstractions/sql-dialect.ts';
|
|
11
|
+
import { isMigrationName, nextSequence, pad4 } from './create-sequence.ts';
|
|
12
|
+
import { applyOneDown, applyOneUp, discoverMigrations } from './datasource-migrate.ts';
|
|
13
|
+
import { defaultDialectFactory } from './infrastructure/default-factory.ts';
|
|
14
|
+
import { fillTemplate, loadMessage, loadScaffold } from './infrastructure/message-templates.ts';
|
|
15
|
+
|
|
16
|
+
const invalidNameTpl = await loadMessage('errors/invalid-name');
|
|
17
|
+
|
|
18
|
+
export class Migrater implements IMigrater {
|
|
19
|
+
constructor(private readonly dialect: ISqlDialect) {}
|
|
20
|
+
|
|
21
|
+
get name(): string {
|
|
22
|
+
return this.dialect.name;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async setup({ connection, migrationsPath }: SetupArg): Promise<void> {
|
|
26
|
+
await this.dialect.prepareSetup(connection);
|
|
27
|
+
const client = await this.dialect.createClient(connection);
|
|
28
|
+
try {
|
|
29
|
+
await client.exec(this.dialect.migratesDdl);
|
|
30
|
+
await client.exec(this.dialect.migrateLogsDdl);
|
|
31
|
+
await mkdir(migrationsPath, { recursive: true });
|
|
32
|
+
} finally {
|
|
33
|
+
await client.close();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async up({ connection, migrationsPath, one = false }: UpArg): Promise<string[]> {
|
|
38
|
+
const client = await this.dialect.createClient(connection);
|
|
39
|
+
try {
|
|
40
|
+
const migrations = await discoverMigrations({
|
|
41
|
+
migratePath: migrationsPath,
|
|
42
|
+
dialect: this.dialect.name,
|
|
43
|
+
});
|
|
44
|
+
const applied: string[] = [];
|
|
45
|
+
for (;;) {
|
|
46
|
+
const result = await applyOneUp({ client, migrations });
|
|
47
|
+
if (!result.applied || result.name == null) break;
|
|
48
|
+
applied.push(result.name);
|
|
49
|
+
if (one) break;
|
|
50
|
+
}
|
|
51
|
+
return applied;
|
|
52
|
+
} finally {
|
|
53
|
+
await client.close();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async down({ connection, migrationsPath }: DownArg): Promise<string | null> {
|
|
58
|
+
const client = await this.dialect.createClient(connection);
|
|
59
|
+
try {
|
|
60
|
+
const migrations = await discoverMigrations({
|
|
61
|
+
migratePath: migrationsPath,
|
|
62
|
+
dialect: this.dialect.name,
|
|
63
|
+
});
|
|
64
|
+
const result = await applyOneDown({ client, migrations });
|
|
65
|
+
return result.rolledBack ? result.name : null;
|
|
66
|
+
} finally {
|
|
67
|
+
await client.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async create({ name, migrationsPath }: CreateArg): Promise<CreateResponse> {
|
|
72
|
+
if (!isMigrationName(name)) {
|
|
73
|
+
throw new Error(fillTemplate(invalidNameTpl, { name }));
|
|
74
|
+
}
|
|
75
|
+
await mkdir(migrationsPath, { recursive: true });
|
|
76
|
+
let entries: string[] = [];
|
|
77
|
+
try {
|
|
78
|
+
entries = await readdir(migrationsPath);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (!(err instanceof Error && 'code' in err && err.code === 'ENOENT')) throw err;
|
|
81
|
+
}
|
|
82
|
+
const seq = pad4(nextSequence(entries));
|
|
83
|
+
const upName = `${seq}_${name}_up.sql`;
|
|
84
|
+
const downName = `${seq}_${name}_down.sql`;
|
|
85
|
+
await writeFile(`${migrationsPath}/${upName}`, await loadScaffold('up', { name }));
|
|
86
|
+
await writeFile(`${migrationsPath}/${downName}`, await loadScaffold('down', { name }));
|
|
87
|
+
return { upName, downName };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const getMigrater = (provider: string): IMigrater =>
|
|
92
|
+
new Migrater(defaultDialectFactory.get(provider));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type MigrationRowValue = string | number | bigint | boolean | Date | null;
|
|
2
|
+
export type MigrationRow = Record<string, MigrationRowValue>;
|
|
3
|
+
export type MigrationSqlParam = string | number | bigint | boolean | Date | null;
|
|
4
|
+
|
|
5
|
+
/** The subset of a better-sqlite3 Database that `insertLogStarted` reaches through `_raw` when a caller injects a synchronous handle. */
|
|
6
|
+
export interface SqliteRawDatabase {
|
|
7
|
+
prepare(source: string): {
|
|
8
|
+
run(...params: MigrationSqlParam[]): { lastInsertRowid: number | bigint };
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Provider-agnostic migration client used by IMigrater.up, IMigrater.down, and IMigrater.setup. */
|
|
13
|
+
export interface MigrationClient {
|
|
14
|
+
dialect: import('./sql.ts').SqlDialect;
|
|
15
|
+
exec(sql: string): Promise<void>;
|
|
16
|
+
query(sql: string, params?: MigrationSqlParam[]): Promise<MigrationRow[]>;
|
|
17
|
+
run(sql: string, params?: MigrationSqlParam[]): Promise<void>;
|
|
18
|
+
transaction<T>(fn: () => Promise<T>): Promise<T>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
_raw?: SqliteRawDatabase;
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { getMigrater } from './migrater.ts';
|
|
3
|
+
import { defaultDialectFactory } from './infrastructure/default-factory.ts';
|
|
4
|
+
|
|
5
|
+
describe('IMigrater setup bookkeeping', () => {
|
|
6
|
+
it('loads the shared DDL templates for every dialect', () => {
|
|
7
|
+
for (const dialect of defaultDialectFactory.all()) {
|
|
8
|
+
expect(dialect.migratesDdl).toMatch(/migrates/i);
|
|
9
|
+
expect(dialect.migrateLogsDdl).toMatch(/migrate_logs/i);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('rejects an unknown dialect', () => {
|
|
14
|
+
expect(() => getMigrater('dbase')).toThrow(/Unknown SQL dialect/);
|
|
15
|
+
});
|
|
16
|
+
});
|
package/src/sql.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cliSpec } from './cli-spec.ts';
|
|
2
|
+
|
|
3
|
+
export type SqlDialect = 'sqlite' | 'mysql' | 'postgres' | 'sqlserver' | 'oracle';
|
|
4
|
+
|
|
5
|
+
const stripKey = (raw: string): string => raw.toLowerCase().replace(/[\s_\-]/g, '');
|
|
6
|
+
|
|
7
|
+
export const normalizeDialect = (raw: string | null | undefined): SqlDialect | null => {
|
|
8
|
+
if (!raw) return null;
|
|
9
|
+
const key = stripKey(raw);
|
|
10
|
+
if ((cliSpec.providers as string[]).includes(key)) return key as SqlDialect;
|
|
11
|
+
for (const [alias, canonical] of Object.entries(cliSpec.provider_aliases ?? {})) {
|
|
12
|
+
if (stripKey(alias) === key) return canonical as SqlDialect;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export { pathExists } from './path-exists.ts';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "nodenext",
|
|
4
|
+
"moduleResolution": "nodenext",
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"rewriteRelativeImportExtensions": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*.ts"],
|
|
18
|
+
"exclude": ["src/**/*.test.ts", "src/generate-help.ts"]
|
|
19
|
+
}
|
package/README.md
DELETED