sealed-migrations 0.1.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/LICENSE +21 -0
- package/README.md +180 -0
- package/dist/index.cjs +709 -0
- package/dist/index.d.cts +151 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +663 -0
- package/package.json +71 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The subset of a database connection the runner needs. A `mysql2/promise`
|
|
4
|
+
* Connection satisfies it structurally (so a consumer can pass
|
|
5
|
+
* `mysql.createConnection(...)` with no cast), and so does any compatible
|
|
6
|
+
* driver or test fake. The consumer owns the driver, so this package has no
|
|
7
|
+
* runtime dependency on mysql2 (or any specific client).
|
|
8
|
+
*
|
|
9
|
+
* `params` and the row tuples are intentionally loose: this is the untyped
|
|
10
|
+
* driver boundary, and a stricter shape would reject real driver types through
|
|
11
|
+
* parameter contravariance. Callers narrow rows internally.
|
|
12
|
+
*/
|
|
13
|
+
interface MigrationConnection {
|
|
14
|
+
query: <T = any>(sql: string) => Promise<[T, any]>;
|
|
15
|
+
execute: <T = any>(sql: string, params?: any) => Promise<[T, any]>;
|
|
16
|
+
beginTransaction: () => Promise<void>;
|
|
17
|
+
commit: () => Promise<void>;
|
|
18
|
+
rollback: () => Promise<void>;
|
|
19
|
+
end?: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
type Logger = Pick<Console, 'log' | 'warn' | 'error'>;
|
|
22
|
+
interface RunnerConfig {
|
|
23
|
+
/** Path to the migrations directory (absolute, or relative to `process.cwd()`). */
|
|
24
|
+
migrationsDir: string;
|
|
25
|
+
/** Opens a connection the runner owns and closes for a single command. */
|
|
26
|
+
connect: () => Promise<MigrationConnection>;
|
|
27
|
+
/** MySQL advisory lock name serialising up/down/seal/rehash. */
|
|
28
|
+
lockName?: string;
|
|
29
|
+
/** Seconds to wait for the advisory lock (default 120). */
|
|
30
|
+
lockTimeoutSec?: number;
|
|
31
|
+
/** Version treated as the baseline (default `001_baseline`). */
|
|
32
|
+
baselineVersion?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Database name, used only to detect an existing schema for the baseline
|
|
35
|
+
* short-circuit. Omit to always execute the baseline SQL.
|
|
36
|
+
*/
|
|
37
|
+
databaseName?: string;
|
|
38
|
+
/** Optional pre-flight assertion, e.g. refuse a non-MariaDB server. */
|
|
39
|
+
assertServer?: (conn: MigrationConnection) => Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Wraps retryable writes, e.g. a Galera deadlock retry. Defaults to a plain
|
|
42
|
+
* pass-through, so a single-writer setup needs no configuration.
|
|
43
|
+
*/
|
|
44
|
+
withRetry?: <T>(fn: () => Promise<T>, label: string) => Promise<T>;
|
|
45
|
+
/** Recorded in `schema_migrations.app_version`. */
|
|
46
|
+
appVersion?: string | null;
|
|
47
|
+
/** Recorded in `schema_migrations.executed_by`. */
|
|
48
|
+
executedBy?: string | null;
|
|
49
|
+
/** Sink for progress messages (default `console`). */
|
|
50
|
+
logger?: Logger;
|
|
51
|
+
}
|
|
52
|
+
type TxMode = 'transactional' | 'non_transactional' | 'baseline_mark';
|
|
53
|
+
interface ParsedMigrationName {
|
|
54
|
+
isDev: boolean;
|
|
55
|
+
sortNumber: number;
|
|
56
|
+
version: string;
|
|
57
|
+
description: string;
|
|
58
|
+
}
|
|
59
|
+
interface Migration extends ParsedMigrationName {
|
|
60
|
+
upSql: string;
|
|
61
|
+
downSql: string;
|
|
62
|
+
checksum: string;
|
|
63
|
+
}
|
|
64
|
+
interface AppliedMigration {
|
|
65
|
+
checksum: string;
|
|
66
|
+
appliedAt?: unknown;
|
|
67
|
+
}
|
|
68
|
+
type AppliedMap = Map<string, AppliedMigration>;
|
|
69
|
+
interface ValidateOptions {
|
|
70
|
+
/** How a checksum mismatch on an applied dev migration is handled. */
|
|
71
|
+
devChecksumMismatch?: 'error' | 'warn';
|
|
72
|
+
}
|
|
73
|
+
interface UpOptions {
|
|
74
|
+
to?: string;
|
|
75
|
+
allowDev?: boolean;
|
|
76
|
+
}
|
|
77
|
+
interface DownOptions {
|
|
78
|
+
to?: string;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/commands.d.ts
|
|
82
|
+
declare function assertDevMigrationsAllowed(migrations: Migration[], allowDev: boolean): void;
|
|
83
|
+
/** Next free number = max(numbered repo folders ∪ numbered applied versions) + 1. */
|
|
84
|
+
declare function computeNextSealNumber(migrations: Migration[], appliedVersions: string[]): string;
|
|
85
|
+
declare function commandCurrent(conn: MigrationConnection, migrations: Migration[], logger: Logger): Promise<void>;
|
|
86
|
+
declare function commandStatus(conn: MigrationConnection, migrations: Migration[], logger: Logger): Promise<void>;
|
|
87
|
+
declare function commandUp(conn: MigrationConnection, migrations: Migration[], config: RunnerConfig, options: UpOptions, logger: Logger): Promise<void>;
|
|
88
|
+
declare function commandDown(conn: MigrationConnection, migrations: Migration[], options: DownOptions, logger: Logger): Promise<void>;
|
|
89
|
+
declare function commandSeal(conn: MigrationConnection, migrations: Migration[], migrationsDir: string, slug: string | undefined, logger: Logger): Promise<void>;
|
|
90
|
+
declare function commandRehash(conn: MigrationConnection, migrations: Migration[], slug: string | undefined, logger: Logger): Promise<void>;
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/migrations.d.ts
|
|
93
|
+
declare const MIGRATION_DIR_PATTERN: RegExp;
|
|
94
|
+
declare const DEV_MIGRATION_DIR_PATTERN: RegExp;
|
|
95
|
+
/** SHA-256 of `up.sql + "\n--down-sql--\n" + down.sql`, content only. */
|
|
96
|
+
declare function hashMigration(upSql: string, downSql: string): string;
|
|
97
|
+
declare function isDevVersion(version: string): boolean;
|
|
98
|
+
declare function parseMigrationDirName(dirName: string): ParsedMigrationName;
|
|
99
|
+
/**
|
|
100
|
+
* Normalises a `seal`/`rehash` argument into a `dev_<slug>` version. Accepts
|
|
101
|
+
* both `foo` and `dev_foo`, and refuses sealed (numbered) versions, which are
|
|
102
|
+
* immutable.
|
|
103
|
+
*/
|
|
104
|
+
declare function normalizeDevVersion(input: string | undefined): string;
|
|
105
|
+
/**
|
|
106
|
+
* Reads every migration folder, sorted numbered-ascending then dev
|
|
107
|
+
* alphabetical. Duplicate numeric prefixes stay deterministic via the folder
|
|
108
|
+
* name tie-break.
|
|
109
|
+
*/
|
|
110
|
+
declare function readMigrations(migrationsDir: string): Promise<Migration[]>;
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/runner.d.ts
|
|
113
|
+
interface ParsedArgs {
|
|
114
|
+
args: Record<string, string | boolean>;
|
|
115
|
+
positionals: string[];
|
|
116
|
+
}
|
|
117
|
+
/** Minimal `--flag`, `--key=value`, `--key value` and positional parser. */
|
|
118
|
+
declare function parseArgs(argv: string[]): ParsedArgs;
|
|
119
|
+
interface Runner {
|
|
120
|
+
up: (options?: UpOptions) => Promise<void>;
|
|
121
|
+
down: (options: DownOptions) => Promise<void>;
|
|
122
|
+
seal: (slug: string) => Promise<void>;
|
|
123
|
+
rehash: (slug: string) => Promise<void>;
|
|
124
|
+
current: () => Promise<void>;
|
|
125
|
+
status: () => Promise<void>;
|
|
126
|
+
/** Parses `argv` (without node/script), dispatches, and throws on error. */
|
|
127
|
+
runCli: (argv: string[]) => Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
declare function createRunner(config: RunnerConfig): Runner;
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/sql.d.ts
|
|
132
|
+
declare function splitSqlStatements(sqlFileContent: string): string[];
|
|
133
|
+
/**
|
|
134
|
+
* Picks the transaction mode for a migration file. An explicit
|
|
135
|
+
* `-- migrate: tx | no-tx | auto` directive wins; otherwise DDL forces the
|
|
136
|
+
* non-transactional path (MySQL/MariaDB cannot roll back DDL).
|
|
137
|
+
*/
|
|
138
|
+
declare function resolveTxMode(sql: string, statements: string[]): 'transactional' | 'non_transactional';
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/state.d.ts
|
|
141
|
+
/**
|
|
142
|
+
* Checks applied state against the repo:
|
|
143
|
+
* - a dev migration applied elsewhere but missing here is only a warning
|
|
144
|
+
* (the normal state of a shared dev DB with parallel branches),
|
|
145
|
+
* - a numbered migration missing here is a hard error (sealed history is strict),
|
|
146
|
+
* - a checksum mismatch is a hard error, except a dev mismatch in `warn` mode
|
|
147
|
+
* (the `down` path, so the SQL-first edit loop can start).
|
|
148
|
+
*/
|
|
149
|
+
declare function validateAppliedState(migrations: Migration[], appliedByVersion: AppliedMap, options?: ValidateOptions, logger?: Logger): void;
|
|
150
|
+
//#endregion
|
|
151
|
+
export { type AppliedMap, type AppliedMigration, DEV_MIGRATION_DIR_PATTERN, type DownOptions, type Logger, MIGRATION_DIR_PATTERN, type Migration, type MigrationConnection, type ParsedArgs, type ParsedMigrationName, type Runner, type RunnerConfig, type TxMode, type UpOptions, type ValidateOptions, assertDevMigrationsAllowed, commandCurrent, commandDown, commandRehash, commandSeal, commandStatus, commandUp, computeNextSealNumber, createRunner, hashMigration, isDevVersion, normalizeDevVersion, parseArgs, parseMigrationDirName, readMigrations, resolveTxMode, splitSqlStatements, validateAppliedState };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The subset of a database connection the runner needs. A `mysql2/promise`
|
|
4
|
+
* Connection satisfies it structurally (so a consumer can pass
|
|
5
|
+
* `mysql.createConnection(...)` with no cast), and so does any compatible
|
|
6
|
+
* driver or test fake. The consumer owns the driver, so this package has no
|
|
7
|
+
* runtime dependency on mysql2 (or any specific client).
|
|
8
|
+
*
|
|
9
|
+
* `params` and the row tuples are intentionally loose: this is the untyped
|
|
10
|
+
* driver boundary, and a stricter shape would reject real driver types through
|
|
11
|
+
* parameter contravariance. Callers narrow rows internally.
|
|
12
|
+
*/
|
|
13
|
+
interface MigrationConnection {
|
|
14
|
+
query: <T = any>(sql: string) => Promise<[T, any]>;
|
|
15
|
+
execute: <T = any>(sql: string, params?: any) => Promise<[T, any]>;
|
|
16
|
+
beginTransaction: () => Promise<void>;
|
|
17
|
+
commit: () => Promise<void>;
|
|
18
|
+
rollback: () => Promise<void>;
|
|
19
|
+
end?: () => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
type Logger = Pick<Console, 'log' | 'warn' | 'error'>;
|
|
22
|
+
interface RunnerConfig {
|
|
23
|
+
/** Path to the migrations directory (absolute, or relative to `process.cwd()`). */
|
|
24
|
+
migrationsDir: string;
|
|
25
|
+
/** Opens a connection the runner owns and closes for a single command. */
|
|
26
|
+
connect: () => Promise<MigrationConnection>;
|
|
27
|
+
/** MySQL advisory lock name serialising up/down/seal/rehash. */
|
|
28
|
+
lockName?: string;
|
|
29
|
+
/** Seconds to wait for the advisory lock (default 120). */
|
|
30
|
+
lockTimeoutSec?: number;
|
|
31
|
+
/** Version treated as the baseline (default `001_baseline`). */
|
|
32
|
+
baselineVersion?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Database name, used only to detect an existing schema for the baseline
|
|
35
|
+
* short-circuit. Omit to always execute the baseline SQL.
|
|
36
|
+
*/
|
|
37
|
+
databaseName?: string;
|
|
38
|
+
/** Optional pre-flight assertion, e.g. refuse a non-MariaDB server. */
|
|
39
|
+
assertServer?: (conn: MigrationConnection) => Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Wraps retryable writes, e.g. a Galera deadlock retry. Defaults to a plain
|
|
42
|
+
* pass-through, so a single-writer setup needs no configuration.
|
|
43
|
+
*/
|
|
44
|
+
withRetry?: <T>(fn: () => Promise<T>, label: string) => Promise<T>;
|
|
45
|
+
/** Recorded in `schema_migrations.app_version`. */
|
|
46
|
+
appVersion?: string | null;
|
|
47
|
+
/** Recorded in `schema_migrations.executed_by`. */
|
|
48
|
+
executedBy?: string | null;
|
|
49
|
+
/** Sink for progress messages (default `console`). */
|
|
50
|
+
logger?: Logger;
|
|
51
|
+
}
|
|
52
|
+
type TxMode = 'transactional' | 'non_transactional' | 'baseline_mark';
|
|
53
|
+
interface ParsedMigrationName {
|
|
54
|
+
isDev: boolean;
|
|
55
|
+
sortNumber: number;
|
|
56
|
+
version: string;
|
|
57
|
+
description: string;
|
|
58
|
+
}
|
|
59
|
+
interface Migration extends ParsedMigrationName {
|
|
60
|
+
upSql: string;
|
|
61
|
+
downSql: string;
|
|
62
|
+
checksum: string;
|
|
63
|
+
}
|
|
64
|
+
interface AppliedMigration {
|
|
65
|
+
checksum: string;
|
|
66
|
+
appliedAt?: unknown;
|
|
67
|
+
}
|
|
68
|
+
type AppliedMap = Map<string, AppliedMigration>;
|
|
69
|
+
interface ValidateOptions {
|
|
70
|
+
/** How a checksum mismatch on an applied dev migration is handled. */
|
|
71
|
+
devChecksumMismatch?: 'error' | 'warn';
|
|
72
|
+
}
|
|
73
|
+
interface UpOptions {
|
|
74
|
+
to?: string;
|
|
75
|
+
allowDev?: boolean;
|
|
76
|
+
}
|
|
77
|
+
interface DownOptions {
|
|
78
|
+
to?: string;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/commands.d.ts
|
|
82
|
+
declare function assertDevMigrationsAllowed(migrations: Migration[], allowDev: boolean): void;
|
|
83
|
+
/** Next free number = max(numbered repo folders ∪ numbered applied versions) + 1. */
|
|
84
|
+
declare function computeNextSealNumber(migrations: Migration[], appliedVersions: string[]): string;
|
|
85
|
+
declare function commandCurrent(conn: MigrationConnection, migrations: Migration[], logger: Logger): Promise<void>;
|
|
86
|
+
declare function commandStatus(conn: MigrationConnection, migrations: Migration[], logger: Logger): Promise<void>;
|
|
87
|
+
declare function commandUp(conn: MigrationConnection, migrations: Migration[], config: RunnerConfig, options: UpOptions, logger: Logger): Promise<void>;
|
|
88
|
+
declare function commandDown(conn: MigrationConnection, migrations: Migration[], options: DownOptions, logger: Logger): Promise<void>;
|
|
89
|
+
declare function commandSeal(conn: MigrationConnection, migrations: Migration[], migrationsDir: string, slug: string | undefined, logger: Logger): Promise<void>;
|
|
90
|
+
declare function commandRehash(conn: MigrationConnection, migrations: Migration[], slug: string | undefined, logger: Logger): Promise<void>;
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/migrations.d.ts
|
|
93
|
+
declare const MIGRATION_DIR_PATTERN: RegExp;
|
|
94
|
+
declare const DEV_MIGRATION_DIR_PATTERN: RegExp;
|
|
95
|
+
/** SHA-256 of `up.sql + "\n--down-sql--\n" + down.sql`, content only. */
|
|
96
|
+
declare function hashMigration(upSql: string, downSql: string): string;
|
|
97
|
+
declare function isDevVersion(version: string): boolean;
|
|
98
|
+
declare function parseMigrationDirName(dirName: string): ParsedMigrationName;
|
|
99
|
+
/**
|
|
100
|
+
* Normalises a `seal`/`rehash` argument into a `dev_<slug>` version. Accepts
|
|
101
|
+
* both `foo` and `dev_foo`, and refuses sealed (numbered) versions, which are
|
|
102
|
+
* immutable.
|
|
103
|
+
*/
|
|
104
|
+
declare function normalizeDevVersion(input: string | undefined): string;
|
|
105
|
+
/**
|
|
106
|
+
* Reads every migration folder, sorted numbered-ascending then dev
|
|
107
|
+
* alphabetical. Duplicate numeric prefixes stay deterministic via the folder
|
|
108
|
+
* name tie-break.
|
|
109
|
+
*/
|
|
110
|
+
declare function readMigrations(migrationsDir: string): Promise<Migration[]>;
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/runner.d.ts
|
|
113
|
+
interface ParsedArgs {
|
|
114
|
+
args: Record<string, string | boolean>;
|
|
115
|
+
positionals: string[];
|
|
116
|
+
}
|
|
117
|
+
/** Minimal `--flag`, `--key=value`, `--key value` and positional parser. */
|
|
118
|
+
declare function parseArgs(argv: string[]): ParsedArgs;
|
|
119
|
+
interface Runner {
|
|
120
|
+
up: (options?: UpOptions) => Promise<void>;
|
|
121
|
+
down: (options: DownOptions) => Promise<void>;
|
|
122
|
+
seal: (slug: string) => Promise<void>;
|
|
123
|
+
rehash: (slug: string) => Promise<void>;
|
|
124
|
+
current: () => Promise<void>;
|
|
125
|
+
status: () => Promise<void>;
|
|
126
|
+
/** Parses `argv` (without node/script), dispatches, and throws on error. */
|
|
127
|
+
runCli: (argv: string[]) => Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
declare function createRunner(config: RunnerConfig): Runner;
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/sql.d.ts
|
|
132
|
+
declare function splitSqlStatements(sqlFileContent: string): string[];
|
|
133
|
+
/**
|
|
134
|
+
* Picks the transaction mode for a migration file. An explicit
|
|
135
|
+
* `-- migrate: tx | no-tx | auto` directive wins; otherwise DDL forces the
|
|
136
|
+
* non-transactional path (MySQL/MariaDB cannot roll back DDL).
|
|
137
|
+
*/
|
|
138
|
+
declare function resolveTxMode(sql: string, statements: string[]): 'transactional' | 'non_transactional';
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/state.d.ts
|
|
141
|
+
/**
|
|
142
|
+
* Checks applied state against the repo:
|
|
143
|
+
* - a dev migration applied elsewhere but missing here is only a warning
|
|
144
|
+
* (the normal state of a shared dev DB with parallel branches),
|
|
145
|
+
* - a numbered migration missing here is a hard error (sealed history is strict),
|
|
146
|
+
* - a checksum mismatch is a hard error, except a dev mismatch in `warn` mode
|
|
147
|
+
* (the `down` path, so the SQL-first edit loop can start).
|
|
148
|
+
*/
|
|
149
|
+
declare function validateAppliedState(migrations: Migration[], appliedByVersion: AppliedMap, options?: ValidateOptions, logger?: Logger): void;
|
|
150
|
+
//#endregion
|
|
151
|
+
export { type AppliedMap, type AppliedMigration, DEV_MIGRATION_DIR_PATTERN, type DownOptions, type Logger, MIGRATION_DIR_PATTERN, type Migration, type MigrationConnection, type ParsedArgs, type ParsedMigrationName, type Runner, type RunnerConfig, type TxMode, type UpOptions, type ValidateOptions, assertDevMigrationsAllowed, commandCurrent, commandDown, commandRehash, commandSeal, commandStatus, commandUp, computeNextSealNumber, createRunner, hashMigration, isDevVersion, normalizeDevVersion, parseArgs, parseMigrationDirName, readMigrations, resolveTxMode, splitSqlStatements, validateAppliedState };
|