@deterministic-code/migraters 0.0.0-stage → 0.0.2
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 +1 -3
- package/bundle.yaml +262 -0
- package/cli.yaml +264 -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 +79 -4
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ISqlDialect } from '../abstractions/sql-dialect.ts';
|
|
2
|
+
export declare class ConnectionResolver {
|
|
3
|
+
private readonly env;
|
|
4
|
+
constructor(env?: NodeJS.ProcessEnv);
|
|
5
|
+
fromEnvironment(dialect: ISqlDialect): string | null;
|
|
6
|
+
resolve(dialect: ISqlDialect, fromFlag: string | null): string | null;
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class ConnectionResolver {
|
|
2
|
+
env;
|
|
3
|
+
constructor(env = process.env) {
|
|
4
|
+
this.env = env;
|
|
5
|
+
}
|
|
6
|
+
fromEnvironment(dialect) {
|
|
7
|
+
for (const name of dialect.connectionEnvironmentVariables) {
|
|
8
|
+
const value = this.env[name];
|
|
9
|
+
if (value)
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
resolve(dialect, fromFlag) {
|
|
15
|
+
if (fromFlag)
|
|
16
|
+
return fromFlag;
|
|
17
|
+
return this.fromEnvironment(dialect);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SqlDialectFactory } from "../abstractions/sql-dialect-factory.js";
|
|
2
|
+
import { MysqlDialect } from "../dialects/mysql-dialect.js";
|
|
3
|
+
import { OracleDialect } from "../dialects/oracle-dialect.js";
|
|
4
|
+
import { PostgresDialect } from "../dialects/postgres-dialect.js";
|
|
5
|
+
import { SqliteDialect } from "../dialects/sqlite-dialect.js";
|
|
6
|
+
import { SqlServerDialect } from "../dialects/sqlserver-dialect.js";
|
|
7
|
+
import { ConnectionResolver } from "./connection-resolver.js";
|
|
8
|
+
export const defaultDialectFactory = new SqlDialectFactory([
|
|
9
|
+
new SqliteDialect(),
|
|
10
|
+
new PostgresDialect(),
|
|
11
|
+
new MysqlDialect(),
|
|
12
|
+
new SqlServerDialect(),
|
|
13
|
+
new OracleDialect(),
|
|
14
|
+
]);
|
|
15
|
+
export const connectionResolver = new ConnectionResolver();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const interopDefault = (mod) => (mod.default ?? mod);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const fillTemplate: (text: string, vars: Record<string, string>) => string;
|
|
2
|
+
export declare const loadMessage: (relativePath: string, vars?: Record<string, string>) => Promise<string>;
|
|
3
|
+
export declare const loadScaffold: (name: "up" | "down", vars: Record<string, string>) => Promise<string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { templatesRoot } from "./templates-root.js";
|
|
4
|
+
export const fillTemplate = (text, vars) => {
|
|
5
|
+
let out = text;
|
|
6
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
7
|
+
out = out.replaceAll(`{{${key}}}`, value);
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
};
|
|
11
|
+
export const loadMessage = async (relativePath, vars = {}) => {
|
|
12
|
+
const text = await readFile(join(templatesRoot(), 'messages', `${relativePath}.txt`), 'utf8');
|
|
13
|
+
return fillTemplate(text, vars);
|
|
14
|
+
};
|
|
15
|
+
export const loadScaffold = async (name, vars) => {
|
|
16
|
+
const text = await readFile(join(templatesRoot(), 'scaffold', `${name}.sql`), 'utf8');
|
|
17
|
+
return fillTemplate(text, vars);
|
|
18
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const GO_LINE = /^\s*GO\s*$/i;
|
|
2
|
+
/** Split migration SQL on whole-line GO batch separators. */
|
|
3
|
+
export function splitOnGo(text) {
|
|
4
|
+
const batches = [];
|
|
5
|
+
let buf = '';
|
|
6
|
+
for (const line of text.replace(/\r\n/g, '\n').split('\n')) {
|
|
7
|
+
if (GO_LINE.test(line)) {
|
|
8
|
+
const s = buf.trim();
|
|
9
|
+
if (s.length > 0)
|
|
10
|
+
batches.push(s);
|
|
11
|
+
buf = '';
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
buf = buf.length > 0 ? `${buf}\n${line}` : line;
|
|
15
|
+
}
|
|
16
|
+
const tail = buf.trim();
|
|
17
|
+
if (tail.length > 0)
|
|
18
|
+
batches.push(tail);
|
|
19
|
+
return batches;
|
|
20
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { templatesRoot } from "./templates-root.js";
|
|
4
|
+
export const readSqlTemplate = async (dialect, name) => (await readFile(join(templatesRoot(), 'sql', dialect, `${name}.sql`), 'utf8')).trim();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
4
|
+
/** Repo `shared/templates`; after build, `dist/templates` (copied from shared). */
|
|
5
|
+
export const templatesRoot = () => {
|
|
6
|
+
const normalized = HERE.replaceAll('\\', '/');
|
|
7
|
+
if (normalized.endsWith('/dist/infrastructure')) {
|
|
8
|
+
return join(HERE, '..', 'templates');
|
|
9
|
+
}
|
|
10
|
+
return join(HERE, '..', '..', '..', 'shared', 'templates');
|
|
11
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type SelectedDialects = {
|
|
2
|
+
names: string[];
|
|
3
|
+
packages: {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
}[];
|
|
7
|
+
allDriverPackages: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare const normalizeInitProvider: (raw: string) => string | undefined;
|
|
10
|
+
export declare const resolveInitDialects: (raw: string[], all: boolean) => string[];
|
|
11
|
+
export declare const bundleYamlPath: () => string;
|
|
12
|
+
export declare const loadSelectedPackages: (dialects: string[]) => Promise<SelectedDialects>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { parse } from 'yaml';
|
|
6
|
+
import { cliSpec } from "./cli-spec.js";
|
|
7
|
+
const stripKey = (raw) => raw.replace(/[\s_-]/g, '').toLowerCase();
|
|
8
|
+
export const normalizeInitProvider = (raw) => {
|
|
9
|
+
const key = stripKey(raw);
|
|
10
|
+
const direct = cliSpec.providers.find((p) => stripKey(p) === key);
|
|
11
|
+
if (direct)
|
|
12
|
+
return direct;
|
|
13
|
+
return cliSpec.provider_aliases[key];
|
|
14
|
+
};
|
|
15
|
+
export const resolveInitDialects = (raw, all) => {
|
|
16
|
+
if (all)
|
|
17
|
+
return [...cliSpec.providers];
|
|
18
|
+
if (raw.length === 0) {
|
|
19
|
+
throw new Error('pass --dialect <provider> at least once, or --all');
|
|
20
|
+
}
|
|
21
|
+
const selected = new Set();
|
|
22
|
+
for (const value of raw) {
|
|
23
|
+
const name = normalizeInitProvider(value);
|
|
24
|
+
if (!name) {
|
|
25
|
+
throw new Error(`unsupported provider: ${value}`);
|
|
26
|
+
}
|
|
27
|
+
selected.add(name);
|
|
28
|
+
}
|
|
29
|
+
return cliSpec.providers.filter((name) => selected.has(name));
|
|
30
|
+
};
|
|
31
|
+
export const bundleYamlPath = () => {
|
|
32
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
const candidates = [
|
|
34
|
+
join(here, '..', 'bundle.yaml'),
|
|
35
|
+
join(here, '..', '..', 'bundle.yaml'),
|
|
36
|
+
join(process.cwd(), 'bundle.yaml'),
|
|
37
|
+
];
|
|
38
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
39
|
+
if (!found) {
|
|
40
|
+
throw new Error('bundle.yaml not found');
|
|
41
|
+
}
|
|
42
|
+
return found;
|
|
43
|
+
};
|
|
44
|
+
export const loadSelectedPackages = async (dialects) => {
|
|
45
|
+
const raw = parse(await readFile(bundleYamlPath(), 'utf8'));
|
|
46
|
+
const byName = raw.languages?.typescript?.dialects ?? {};
|
|
47
|
+
const allDriverPackages = [
|
|
48
|
+
...new Set(Object.values(byName).flatMap((d) => (d.packages ?? []).map((p) => p.name))),
|
|
49
|
+
];
|
|
50
|
+
const packages = dialects.flatMap((name) => byName[name]?.packages ?? []);
|
|
51
|
+
return { names: dialects, packages, allDriverPackages };
|
|
52
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CreateArg, CreateResponse, DownArg, IMigrater, SetupArg, UpArg } from './abstractions/migrater.ts';
|
|
2
|
+
import type { ISqlDialect } from './abstractions/sql-dialect.ts';
|
|
3
|
+
export declare class Migrater implements IMigrater {
|
|
4
|
+
private readonly dialect;
|
|
5
|
+
constructor(dialect: ISqlDialect);
|
|
6
|
+
get name(): string;
|
|
7
|
+
setup({ connection, migrationsPath }: SetupArg): Promise<void>;
|
|
8
|
+
up({ connection, migrationsPath, one }: UpArg): Promise<string[]>;
|
|
9
|
+
down({ connection, migrationsPath }: DownArg): Promise<string | null>;
|
|
10
|
+
create({ name, migrationsPath }: CreateArg): Promise<CreateResponse>;
|
|
11
|
+
}
|
|
12
|
+
export declare const getMigrater: (provider: string) => IMigrater;
|
package/dist/migrater.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { isMigrationName, nextSequence, pad4 } from "./create-sequence.js";
|
|
3
|
+
import { applyOneDown, applyOneUp, discoverMigrations } from "./datasource-migrate.js";
|
|
4
|
+
import { defaultDialectFactory } from "./infrastructure/default-factory.js";
|
|
5
|
+
import { fillTemplate, loadMessage, loadScaffold } from "./infrastructure/message-templates.js";
|
|
6
|
+
const invalidNameTpl = await loadMessage('errors/invalid-name');
|
|
7
|
+
export class Migrater {
|
|
8
|
+
dialect;
|
|
9
|
+
constructor(dialect) {
|
|
10
|
+
this.dialect = dialect;
|
|
11
|
+
}
|
|
12
|
+
get name() {
|
|
13
|
+
return this.dialect.name;
|
|
14
|
+
}
|
|
15
|
+
async setup({ connection, migrationsPath }) {
|
|
16
|
+
await this.dialect.prepareSetup(connection);
|
|
17
|
+
const client = await this.dialect.createClient(connection);
|
|
18
|
+
try {
|
|
19
|
+
await client.exec(this.dialect.migratesDdl);
|
|
20
|
+
await client.exec(this.dialect.migrateLogsDdl);
|
|
21
|
+
await mkdir(migrationsPath, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
await client.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async up({ connection, migrationsPath, one = false }) {
|
|
28
|
+
const client = await this.dialect.createClient(connection);
|
|
29
|
+
try {
|
|
30
|
+
const migrations = await discoverMigrations({
|
|
31
|
+
migratePath: migrationsPath,
|
|
32
|
+
dialect: this.dialect.name,
|
|
33
|
+
});
|
|
34
|
+
const applied = [];
|
|
35
|
+
for (;;) {
|
|
36
|
+
const result = await applyOneUp({ client, migrations });
|
|
37
|
+
if (!result.applied || result.name == null)
|
|
38
|
+
break;
|
|
39
|
+
applied.push(result.name);
|
|
40
|
+
if (one)
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
return applied;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
await client.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async down({ connection, migrationsPath }) {
|
|
50
|
+
const client = await this.dialect.createClient(connection);
|
|
51
|
+
try {
|
|
52
|
+
const migrations = await discoverMigrations({
|
|
53
|
+
migratePath: migrationsPath,
|
|
54
|
+
dialect: this.dialect.name,
|
|
55
|
+
});
|
|
56
|
+
const result = await applyOneDown({ client, migrations });
|
|
57
|
+
return result.rolledBack ? result.name : null;
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
await client.close();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async create({ name, migrationsPath }) {
|
|
64
|
+
if (!isMigrationName(name)) {
|
|
65
|
+
throw new Error(fillTemplate(invalidNameTpl, { name }));
|
|
66
|
+
}
|
|
67
|
+
await mkdir(migrationsPath, { recursive: true });
|
|
68
|
+
let entries = [];
|
|
69
|
+
try {
|
|
70
|
+
entries = await readdir(migrationsPath);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (!(err instanceof Error && 'code' in err && err.code === 'ENOENT'))
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
const seq = pad4(nextSequence(entries));
|
|
77
|
+
const upName = `${seq}_${name}_up.sql`;
|
|
78
|
+
const downName = `${seq}_${name}_down.sql`;
|
|
79
|
+
await writeFile(`${migrationsPath}/${upName}`, await loadScaffold('up', { name }));
|
|
80
|
+
await writeFile(`${migrationsPath}/${downName}`, await loadScaffold('down', { name }));
|
|
81
|
+
return { upName, downName };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export const getMigrater = (provider) => 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
|
+
/** The subset of a better-sqlite3 Database that `insertLogStarted` reaches through `_raw` when a caller injects a synchronous handle. */
|
|
5
|
+
export interface SqliteRawDatabase {
|
|
6
|
+
prepare(source: string): {
|
|
7
|
+
run(...params: MigrationSqlParam[]): {
|
|
8
|
+
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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const pathExists: (p: string) => Promise<boolean>;
|
package/dist/sql.d.ts
ADDED
package/dist/sql.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { cliSpec } from "./cli-spec.js";
|
|
2
|
+
const stripKey = (raw) => raw.toLowerCase().replace(/[\s_\-]/g, '');
|
|
3
|
+
export const normalizeDialect = (raw) => {
|
|
4
|
+
if (!raw)
|
|
5
|
+
return null;
|
|
6
|
+
const key = stripKey(raw);
|
|
7
|
+
if (cliSpec.providers.includes(key))
|
|
8
|
+
return key;
|
|
9
|
+
for (const [alias, canonical] of Object.entries(cliSpec.provider_aliases ?? {})) {
|
|
10
|
+
if (stripKey(alias) === key)
|
|
11
|
+
return canonical;
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
};
|
|
15
|
+
export { pathExists } from "./path-exists.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{{usage}}
|
|
2
|
+
|
|
3
|
+
Creates a new <NNNN>_<name>_up.sql + <NNNN>_<name>_down.sql pair scaffolded
|
|
4
|
+
with TODO placeholders. NNNN is one greater than the highest 1-4-digit
|
|
5
|
+
sequence prefix already present in the migrations directory (starts at 0001).
|
|
6
|
+
Legacy timestamp-prefixed filenames are ignored for numbering.
|
|
7
|
+
|
|
8
|
+
--provider Database dialect. Required.
|
|
9
|
+
--name snake_case slug for the new migration. Required. Must
|
|
10
|
+
match /^[a-z][a-z0-9_]*$/.
|
|
11
|
+
--migrations-path Migrations directory (default: ./sql/<dialect>/migrations).
|
|
12
|
+
Created with mkdir -p if it does not exist.
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
{{command}} --provider sqlite --name add_users
|
|
16
|
+
{{command}} --provider postgres --name backfill_email_lower --migrations-path ./db/changes/postgres
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{{usage}}
|
|
2
|
+
|
|
3
|
+
Rolls back the MOST RECENTLY applied migration (one step). DESTRUCTIVE: prints a
|
|
4
|
+
random 4-letter uppercase token on stderr and refuses to proceed unless the
|
|
5
|
+
operator types it back on stdin (or passes --confirm <TOKEN> matching it).
|
|
6
|
+
|
|
7
|
+
--provider Database dialect. Required.
|
|
8
|
+
--connection Connection string. Required (or set the per-dialect env var below).
|
|
9
|
+
--migrations-path Full path to the migrations directory.
|
|
10
|
+
--migrations-root Root prefix; the script then looks at <migrations-root>/<dialect>/migrations.
|
|
11
|
+
Defaults to 'sql'. Ignored when --migrations-path is also set.
|
|
12
|
+
--confirm Skip the interactive prompt. The TOKEN must match the value
|
|
13
|
+
printed on stderr at runtime — there is no fixed bypass value.
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
# sqlite — bare path or sqlite:// URL.
|
|
17
|
+
{{command}} --provider sqlite --connection ./app.sqlite
|
|
18
|
+
{{command}} --provider sqlite --connection sqlite:///absolute/path/app.sqlite
|
|
19
|
+
|
|
20
|
+
# postgres — URL or keyword form.
|
|
21
|
+
{{command}} --provider postgres --connection postgresql://user:pass@host:5432/dbname
|
|
22
|
+
|
|
23
|
+
# mysql — URL form.
|
|
24
|
+
{{command}} --provider mysql --connection mysql://user:pass@host:3306/dbname
|
|
25
|
+
|
|
26
|
+
# sqlserver / oracle — pass a driver-native connection string.
|
|
27
|
+
{{command}} --provider sqlserver --connection "Server=host;Database=dbname;User Id=user;Password=pass;"
|
|
28
|
+
{{command}} --provider oracle --connection "user/pass@host:1521/service"
|
|
29
|
+
|
|
30
|
+
# env-var fallback when --connection is omitted:
|
|
31
|
+
SQLITE_PATH=./app.sqlite {{command}} --provider sqlite
|
|
32
|
+
DATABASE_URL=postgresql://... {{command}} --provider postgres
|
|
33
|
+
|
|
34
|
+
# custom migrations layout (default looks under sql/<dialect>/migrations):
|
|
35
|
+
{{command}} --provider sqlite --connection ./app.sqlite --migrations-path ./db/changes/sqlite
|
|
36
|
+
|
|
37
|
+
Per-dialect env vars consulted when --connection is omitted:
|
|
38
|
+
sqlite SQLITE_PATH, DB_PATH
|
|
39
|
+
postgres PG_CONNECTION_STRING, DATABASE_URL
|
|
40
|
+
mysql MYSQL_URL, DATABASE_URL
|
|
41
|
+
sqlserver MSSQL_URL, DATABASE_URL
|
|
42
|
+
oracle ORACLE_CONNECT_STRING, DATABASE_URL
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{{usage}}
|
|
2
|
+
|
|
3
|
+
Selects the dialects this tree will compile and install. Repeat --dialect
|
|
4
|
+
once per provider. Re-running replaces the previous set. --all restores
|
|
5
|
+
every dialect. Does not take --connection.
|
|
6
|
+
|
|
7
|
+
--dialect Dialect to enable. Repeat for each dialect. Aliases from this file apply.
|
|
8
|
+
--all Enable every dialect. Restores the committed full-provider set.
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
{{command}} --dialect sqlite --dialect postgres
|
|
12
|
+
{{command}} --dialect oracle
|
|
13
|
+
{{command}} --all
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{{usage}}
|
|
2
|
+
|
|
3
|
+
Creates the bookkeeping tables (migrates, migrate_logs) for the chosen dialect
|
|
4
|
+
and the migrations directory (mkdir -p). Idempotent — re-running against an
|
|
5
|
+
already-setup database is a no-op. For sqlite, also mkdir -p of the parent
|
|
6
|
+
directory of --connection so a fresh-tree path like ./.test/foo.sqlite works.
|
|
7
|
+
|
|
8
|
+
--provider Database dialect. Required.
|
|
9
|
+
--connection Connection string. Required (or set the per-dialect env var below).
|
|
10
|
+
--migrations-path Migrations directory (default: ./sql/<dialect>/migrations).
|
|
11
|
+
--and-up After setup, immediately invoke migrate-up so the chain is applied in one step.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
# sqlite — bare path or sqlite:// URL; both create a file on disk.
|
|
15
|
+
{{command}} --provider sqlite --connection ./app.sqlite
|
|
16
|
+
{{command}} --provider sqlite --connection sqlite:///absolute/path/app.sqlite
|
|
17
|
+
|
|
18
|
+
# postgres — URL or keyword form.
|
|
19
|
+
{{command}} --provider postgres --connection postgresql://user:pass@host:5432/dbname
|
|
20
|
+
|
|
21
|
+
# mysql — URL form.
|
|
22
|
+
{{command}} --provider mysql --connection mysql://user:pass@host:3306/dbname
|
|
23
|
+
|
|
24
|
+
# sqlserver / oracle — pass a driver-native connection string.
|
|
25
|
+
{{command}} --provider sqlserver --connection "Server=host;Database=dbname;User Id=user;Password=pass;"
|
|
26
|
+
{{command}} --provider oracle --connection "user/pass@host:1521/service"
|
|
27
|
+
|
|
28
|
+
# env-var fallback when --connection is omitted:
|
|
29
|
+
SQLITE_PATH=./app.sqlite {{command}} --provider sqlite
|
|
30
|
+
DATABASE_URL=postgresql://... {{command}} --provider postgres
|
|
31
|
+
DATABASE_URL=mysql://... {{command}} --provider mysql
|
|
32
|
+
|
|
33
|
+
# explicit in-memory sqlite:
|
|
34
|
+
{{command}} --provider sqlite --connection :memory:
|
|
35
|
+
|
|
36
|
+
Per-dialect env vars consulted when --connection is omitted:
|
|
37
|
+
sqlite SQLITE_PATH, DB_PATH
|
|
38
|
+
postgres PG_CONNECTION_STRING, DATABASE_URL
|
|
39
|
+
mysql MYSQL_URL, DATABASE_URL
|
|
40
|
+
sqlserver MSSQL_URL, DATABASE_URL
|
|
41
|
+
oracle ORACLE_CONNECT_STRING, DATABASE_URL
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{{usage}}
|
|
2
|
+
|
|
3
|
+
Applies ALL pending migrations in sequence, each in its own transaction.
|
|
4
|
+
|
|
5
|
+
--provider Database dialect. Required.
|
|
6
|
+
--connection Connection string. Required (or set the per-dialect env var below).
|
|
7
|
+
--migrations-path Full path to the migrations directory (e.g. sql/sqlite/migrations).
|
|
8
|
+
--migrations-root Root prefix; the script then looks at <migrations-root>/<dialect>/migrations.
|
|
9
|
+
Defaults to 'sql'. Ignored when --migrations-path is also set.
|
|
10
|
+
--one Apply only the next pending migration, then exit. Default is to apply all pending migrations.
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
# sqlite — bare path or sqlite:// URL; both create a file on disk.
|
|
14
|
+
{{command}} --provider sqlite --connection ./app.sqlite
|
|
15
|
+
{{command}} --provider sqlite --connection sqlite:///absolute/path/app.sqlite
|
|
16
|
+
|
|
17
|
+
# postgres — URL or keyword form.
|
|
18
|
+
{{command}} --provider postgres --connection postgresql://user:pass@host:5432/dbname
|
|
19
|
+
|
|
20
|
+
# mysql — URL form.
|
|
21
|
+
{{command}} --provider mysql --connection mysql://user:pass@host:3306/dbname
|
|
22
|
+
|
|
23
|
+
# sqlserver / oracle — pass a driver-native connection string.
|
|
24
|
+
{{command}} --provider sqlserver --connection "Server=host;Database=dbname;User Id=user;Password=pass;"
|
|
25
|
+
{{command}} --provider oracle --connection "user/pass@host:1521/service"
|
|
26
|
+
|
|
27
|
+
# env-var fallback when --connection is omitted:
|
|
28
|
+
SQLITE_PATH=./app.sqlite {{command}} --provider sqlite
|
|
29
|
+
DATABASE_URL=postgresql://... {{command}} --provider postgres
|
|
30
|
+
|
|
31
|
+
# custom migrations layout (default looks under sql/<dialect>/migrations):
|
|
32
|
+
{{command}} --provider sqlite --connection ./app.sqlite --migrations-path ./db/changes/sqlite
|
|
33
|
+
{{command}} --provider sqlite --connection ./app.sqlite --migrations-root ./db/changes
|
|
34
|
+
|
|
35
|
+
# one-shot: apply the next pending migration only, then exit.
|
|
36
|
+
{{command}} --provider sqlite --connection ./app.sqlite --one
|
|
37
|
+
|
|
38
|
+
Per-dialect env vars consulted when --connection is omitted:
|
|
39
|
+
sqlite SQLITE_PATH, DB_PATH
|
|
40
|
+
postgres PG_CONNECTION_STRING, DATABASE_URL
|
|
41
|
+
mysql MYSQL_URL, DATABASE_URL
|
|
42
|
+
sqlserver MSSQL_URL, DATABASE_URL
|
|
43
|
+
oracle ORACLE_CONNECT_STRING, DATABASE_URL
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{name}}: stored {{stored}}..., current {{current}}...
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Migration checksum drift detected — the migration file changed AFTER it was applied:
|
|
2
|
+
{{lines}}
|
|
3
|
+
|
|
4
|
+
The DB and source disagree about the schema you're running. Either the codegen
|
|
5
|
+
regenerated an already-applied migration in place (the canonical bug — emit a NEW
|
|
6
|
+
migration file instead), or the migrate state was hand-edited.
|
|
7
|
+
|
|
8
|
+
Recovery options:
|
|
9
|
+
1. Emit a new migration that brings the DB up to the current schema, then deploy.
|
|
10
|
+
2. For showcase/dev DBs with no real data: wipe the DB file and let migrate:up re-run from scratch.
|
|
11
|
+
3. If you've verified the DB matches the new file: MIGRATE_ALLOW_CHECKSUM_DRIFT=1 npm run migrate:up
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--confirm value ({{supplied}}) does not match the token printed above ({{token}}). Aborting.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Type {{token}} to confirm rollback:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
failed to read confirmation from stdin — aborting.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Confirmation token did not match — aborting.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
migrate-down requires interactive confirmation when stdin is not a TTY — pass --confirm {{token}} (this exact token, printed above) to proceed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--name must be snake_case: lowercase letter then [a-z0-9_] (got "{{name}}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
migrate-path is not a directory: {{path}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
missing --connection — pass --connection <url> (e.g. ./app.sqlite for sqlite) or set one of: {{envVars}}. Run with --help for examples.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
missing --name — pass --name <snake_case_slug>.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
missing --name
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
missing --provider — pass --provider <{{providers}}>. Run with --help for examples.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
missing --provider
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
missing value for {{flag}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Cannot roll back "{{name}}": no <stem>_down.sql sibling found
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Cannot roll back "{{name}}": no rollback_*.sql sibling found
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Most recently applied migration is not present under --migrate-path
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sqlite file: {{path}} does not exist — run 'migrate-setup --provider sqlite --connection {{path}}' to create it
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
unknown arg: {{arg}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
unsupported provider: {{provider}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Applied: {{name}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Created: {{file}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
No more pending migrations.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
No pending migrations.
|