@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.
Files changed (116) hide show
  1. package/README.md +1 -3
  2. package/bundle.yaml +262 -0
  3. package/cli.yaml +264 -0
  4. package/dist/abstractions/migrater.d.ts +28 -0
  5. package/dist/abstractions/migrater.js +1 -0
  6. package/dist/abstractions/sql-dialect-base.d.ts +16 -0
  7. package/dist/abstractions/sql-dialect-base.js +19 -0
  8. package/dist/abstractions/sql-dialect-factory.d.ts +8 -0
  9. package/dist/abstractions/sql-dialect-factory.js +22 -0
  10. package/dist/abstractions/sql-dialect.d.ts +16 -0
  11. package/dist/abstractions/sql-dialect.js +1 -0
  12. package/dist/bin/migrate-create.d.ts +2 -0
  13. package/dist/bin/migrate-create.js +71 -0
  14. package/dist/bin/migrate-down.d.ts +2 -0
  15. package/dist/bin/migrate-down.js +135 -0
  16. package/dist/bin/migrate-init.d.ts +2 -0
  17. package/dist/bin/migrate-init.js +77 -0
  18. package/dist/bin/migrate-setup.d.ts +2 -0
  19. package/dist/bin/migrate-setup.js +87 -0
  20. package/dist/bin/migrate-up.d.ts +2 -0
  21. package/dist/bin/migrate-up.js +97 -0
  22. package/dist/cli-contract.d.ts +6 -0
  23. package/dist/cli-contract.js +19 -0
  24. package/dist/cli-spec.d.ts +24 -0
  25. package/dist/cli-spec.js +33 -0
  26. package/dist/cli.d.ts +9 -0
  27. package/dist/cli.js +41 -0
  28. package/dist/create-sequence.d.ts +4 -0
  29. package/dist/create-sequence.js +16 -0
  30. package/dist/datasource-migrate.d.ts +31 -0
  31. package/dist/datasource-migrate.js +206 -0
  32. package/dist/dialects/mysql-dialect.d.ts +12 -0
  33. package/dist/dialects/mysql-dialect.js +56 -0
  34. package/dist/dialects/oracle-dialect.d.ts +18 -0
  35. package/dist/dialects/oracle-dialect.js +76 -0
  36. package/dist/dialects/postgres-dialect.d.ts +11 -0
  37. package/dist/dialects/postgres-dialect.js +58 -0
  38. package/dist/dialects/sqlite-dialect.d.ts +13 -0
  39. package/dist/dialects/sqlite-dialect.js +96 -0
  40. package/dist/dialects/sqlserver-dialect.d.ts +12 -0
  41. package/dist/dialects/sqlserver-dialect.js +73 -0
  42. package/dist/help.d.ts +2 -0
  43. package/dist/help.js +10 -0
  44. package/dist/index.d.ts +7 -0
  45. package/dist/index.js +6 -0
  46. package/dist/infrastructure/connection-resolver.d.ts +7 -0
  47. package/dist/infrastructure/connection-resolver.js +19 -0
  48. package/dist/infrastructure/default-factory.d.ts +4 -0
  49. package/dist/infrastructure/default-factory.js +15 -0
  50. package/dist/infrastructure/interop-default.d.ts +3 -0
  51. package/dist/infrastructure/interop-default.js +1 -0
  52. package/dist/infrastructure/message-templates.d.ts +3 -0
  53. package/dist/infrastructure/message-templates.js +18 -0
  54. package/dist/infrastructure/split-on-go.d.ts +2 -0
  55. package/dist/infrastructure/split-on-go.js +20 -0
  56. package/dist/infrastructure/sql-templates.d.ts +2 -0
  57. package/dist/infrastructure/sql-templates.js +4 -0
  58. package/dist/infrastructure/templates-root.d.ts +2 -0
  59. package/dist/infrastructure/templates-root.js +11 -0
  60. package/dist/init-select.d.ts +12 -0
  61. package/dist/init-select.js +52 -0
  62. package/dist/migrater.d.ts +12 -0
  63. package/dist/migrater.js +84 -0
  64. package/dist/migration-client.d.ts +21 -0
  65. package/dist/migration-client.js +1 -0
  66. package/dist/path-exists.d.ts +1 -0
  67. package/dist/path-exists.js +10 -0
  68. package/dist/sql.d.ts +3 -0
  69. package/dist/sql.js +15 -0
  70. package/dist/templates/help/create.txt +16 -0
  71. package/dist/templates/help/down.txt +42 -0
  72. package/dist/templates/help/init.txt +13 -0
  73. package/dist/templates/help/setup.txt +41 -0
  74. package/dist/templates/help/up.txt +43 -0
  75. package/dist/templates/messages/checksum-drift-line.txt +1 -0
  76. package/dist/templates/messages/checksum-drift.txt +11 -0
  77. package/dist/templates/messages/down/confirm-mismatch.txt +1 -0
  78. package/dist/templates/messages/down/confirm-prompt.txt +1 -0
  79. package/dist/templates/messages/down/confirm-stdin-failed.txt +1 -0
  80. package/dist/templates/messages/down/confirm-token-mismatch.txt +1 -0
  81. package/dist/templates/messages/down/confirm-tty-required.txt +1 -0
  82. package/dist/templates/messages/down/destructive-header.txt +3 -0
  83. package/dist/templates/messages/errors/invalid-name.txt +1 -0
  84. package/dist/templates/messages/errors/migrate-path-not-dir.txt +1 -0
  85. package/dist/templates/messages/errors/missing-connection.txt +1 -0
  86. package/dist/templates/messages/errors/missing-name-hint.txt +1 -0
  87. package/dist/templates/messages/errors/missing-name.txt +1 -0
  88. package/dist/templates/messages/errors/missing-provider-hint.txt +1 -0
  89. package/dist/templates/messages/errors/missing-provider.txt +1 -0
  90. package/dist/templates/messages/errors/missing-value-for-flag.txt +1 -0
  91. package/dist/templates/messages/errors/rollback-no-down-sibling.txt +1 -0
  92. package/dist/templates/messages/errors/rollback-no-rollback-sibling.txt +1 -0
  93. package/dist/templates/messages/errors/rollback-not-in-path.txt +1 -0
  94. package/dist/templates/messages/errors/sqlite-prerequisite.txt +1 -0
  95. package/dist/templates/messages/errors/unknown-arg.txt +1 -0
  96. package/dist/templates/messages/errors/unsupported-provider.txt +1 -0
  97. package/dist/templates/messages/setup-complete.txt +9 -0
  98. package/dist/templates/messages/status/applied.txt +1 -0
  99. package/dist/templates/messages/status/created.txt +1 -0
  100. package/dist/templates/messages/status/no-more-pending.txt +1 -0
  101. package/dist/templates/messages/status/no-pending.txt +1 -0
  102. package/dist/templates/messages/status/no-rollback.txt +1 -0
  103. package/dist/templates/messages/status/rolled-back.txt +1 -0
  104. package/dist/templates/scaffold/down.sql +1 -0
  105. package/dist/templates/scaffold/up.sql +1 -0
  106. package/dist/templates/sql/mysql/migrate_logs.sql +11 -0
  107. package/dist/templates/sql/mysql/migrates.sql +7 -0
  108. package/dist/templates/sql/oracle/migrate_logs.sql +14 -0
  109. package/dist/templates/sql/oracle/migrates.sql +10 -0
  110. package/dist/templates/sql/postgres/migrate_logs.sql +11 -0
  111. package/dist/templates/sql/postgres/migrates.sql +7 -0
  112. package/dist/templates/sql/sqlite/migrate_logs.sql +11 -0
  113. package/dist/templates/sql/sqlite/migrates.sql +7 -0
  114. package/dist/templates/sql/sqlserver/migrate_logs.sql +12 -0
  115. package/dist/templates/sql/sqlserver/migrates.sql +8 -0
  116. 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,4 @@
1
+ import { SqlDialectFactory } from '../abstractions/sql-dialect-factory.ts';
2
+ import { ConnectionResolver } from './connection-resolver.ts';
3
+ export declare const defaultDialectFactory: SqlDialectFactory;
4
+ export declare const connectionResolver: ConnectionResolver;
@@ -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,3 @@
1
+ export declare const interopDefault: <T>(mod: {
2
+ default?: T;
3
+ }) => T;
@@ -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,2 @@
1
+ /** Split migration SQL on whole-line GO batch separators. */
2
+ export declare function splitOnGo(text: string): string[];
@@ -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,2 @@
1
+ import type { SqlDialect } from '../sql.ts';
2
+ export declare const readSqlTemplate: (dialect: SqlDialect, name: "migrates" | "migrate_logs") => Promise<string>;
@@ -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,2 @@
1
+ /** Repo `shared/templates`; after build, `dist/templates` (copied from shared). */
2
+ export declare const templatesRoot: () => string;
@@ -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;
@@ -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>;
@@ -0,0 +1,10 @@
1
+ import { access } from 'node:fs/promises';
2
+ export const pathExists = async (p) => {
3
+ try {
4
+ await access(p);
5
+ return true;
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ };
package/dist/sql.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type SqlDialect = 'sqlite' | 'mysql' | 'postgres' | 'sqlserver' | 'oracle';
2
+ export declare const normalizeDialect: (raw: string | null | undefined) => SqlDialect | null;
3
+ export { pathExists } from './path-exists.ts';
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,3 @@
1
+
2
+ ⚠ DESTRUCTIVE: this rolls back the most recently applied migration on {{dialect}}.
3
+ Confirmation token: {{token}}
@@ -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,9 @@
1
+ Setup complete: migrates and migrate_logs ready ({{dialect}}).
2
+ Migrations directory: {{migrationsPath}}
3
+
4
+ Next steps:
5
+ # create a new migration
6
+ {{createExample}}
7
+
8
+ # apply pending migrations
9
+ {{upExample}}
@@ -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.