@zincapp/znvault-migrate 1.0.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/README.md +74 -0
- package/dist/adapters/engine-adapter.d.ts +91 -0
- package/dist/adapters/engine-adapter.d.ts.map +1 -0
- package/dist/adapters/engine-adapter.js +1 -0
- package/dist/adapters/engine-adapter.js.map +1 -0
- package/dist/adapters/mysql/connection.d.ts +30 -0
- package/dist/adapters/mysql/connection.d.ts.map +1 -0
- package/dist/adapters/mysql/connection.js +132 -0
- package/dist/adapters/mysql/connection.js.map +1 -0
- package/dist/adapters/mysql/lock.d.ts +41 -0
- package/dist/adapters/mysql/lock.d.ts.map +1 -0
- package/dist/adapters/mysql/lock.js +77 -0
- package/dist/adapters/mysql/lock.js.map +1 -0
- package/dist/adapters/mysql/mysql-adapter.d.ts +11 -0
- package/dist/adapters/mysql/mysql-adapter.d.ts.map +1 -0
- package/dist/adapters/mysql/mysql-adapter.js +130 -0
- package/dist/adapters/mysql/mysql-adapter.js.map +1 -0
- package/dist/adapters/mysql/mysql-conn.d.ts +22 -0
- package/dist/adapters/mysql/mysql-conn.d.ts.map +1 -0
- package/dist/adapters/mysql/mysql-conn.js +1 -0
- package/dist/adapters/mysql/mysql-conn.js.map +1 -0
- package/dist/adapters/mysql/scaffolding.d.ts +49 -0
- package/dist/adapters/mysql/scaffolding.d.ts.map +1 -0
- package/dist/adapters/mysql/scaffolding.js +70 -0
- package/dist/adapters/mysql/scaffolding.js.map +1 -0
- package/dist/adapters/mysql/schema-migrations-repo.d.ts +42 -0
- package/dist/adapters/mysql/schema-migrations-repo.d.ts.map +1 -0
- package/dist/adapters/mysql/schema-migrations-repo.js +77 -0
- package/dist/adapters/mysql/schema-migrations-repo.js.map +1 -0
- package/dist/adapters/mysql/sql-splitter.d.ts +15 -0
- package/dist/adapters/mysql/sql-splitter.d.ts.map +1 -0
- package/dist/adapters/mysql/sql-splitter.js +160 -0
- package/dist/adapters/mysql/sql-splitter.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/core/baseline-marker.d.ts +2 -0
- package/dist/core/baseline-marker.d.ts.map +1 -0
- package/dist/core/baseline-marker.js +10 -0
- package/dist/core/baseline-marker.js.map +1 -0
- package/dist/core/checksum.d.ts +18 -0
- package/dist/core/checksum.d.ts.map +1 -0
- package/dist/core/checksum.js +30 -0
- package/dist/core/checksum.js.map +1 -0
- package/dist/core/migration-files.d.ts +8 -0
- package/dist/core/migration-files.d.ts.map +1 -0
- package/dist/core/migration-files.js +32 -0
- package/dist/core/migration-files.js.map +1 -0
- package/dist/core/planner.d.ts +36 -0
- package/dist/core/planner.d.ts.map +1 -0
- package/dist/core/planner.js +81 -0
- package/dist/core/planner.js.map +1 -0
- package/dist/core/runner.d.ts +159 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +301 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/types.d.ts +24 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lease/dynamic-secrets-client.d.ts +43 -0
- package/dist/lease/dynamic-secrets-client.d.ts.map +1 -0
- package/dist/lease/dynamic-secrets-client.js +110 -0
- package/dist/lease/dynamic-secrets-client.js.map +1 -0
- package/dist/lease/run-migrations.d.ts +169 -0
- package/dist/lease/run-migrations.d.ts.map +1 -0
- package/dist/lease/run-migrations.js +302 -0
- package/dist/lease/run-migrations.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Conn } from '../engine-adapter.js';
|
|
2
|
+
/**
|
|
3
|
+
* The MySQL adapter's concrete connection handle. Extends the engine-agnostic `Conn`
|
|
4
|
+
* (query rows-directly + end) with the two MySQL-only members the ported leaf modules
|
|
5
|
+
* need: the prepared-statement `execute` path (used by the schema_migrations repo to read
|
|
6
|
+
* affectedRows) and the captured `connectionId` (used by the lock's IS_USED_LOCK ownership
|
|
7
|
+
* check). `openConnection()` (Task 5's connection.ts) returns a MysqlConn as a `Conn`; the
|
|
8
|
+
* MySQL adapter methods downcast their own `Conn` back to `MysqlConn` internally since they
|
|
9
|
+
* know they created it. This keeps the public `Conn` engine-agnostic (a future PG adapter
|
|
10
|
+
* needs neither `execute` nor MySQL's connectionId) while porting the repo/lock faithfully.
|
|
11
|
+
*/
|
|
12
|
+
export interface MysqlConn extends Conn {
|
|
13
|
+
/**
|
|
14
|
+
* Parameterized execute (prepared statement). Returns the raw mysql2 result tuple
|
|
15
|
+
* [OkPacket|RowDataPacket[], FieldPacket[]] so callers can access affectedRows for
|
|
16
|
+
* DML statements as well as rows for SELECT statements.
|
|
17
|
+
*/
|
|
18
|
+
execute(sql: string, params?: unknown[]): Promise<[unknown, unknown]>;
|
|
19
|
+
/** The MySQL CONNECTION_ID() captured immediately after connecting. */
|
|
20
|
+
connectionId: number;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=mysql-conn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql-conn.d.ts","sourceRoot":"","sources":["../../../src/adapters/mysql/mysql-conn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAU,SAAQ,IAAI;IACrC;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql-conn.js","sourceRoot":"","sources":["../../../src/adapters/mysql/mysql-conn.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read and split the scaffolding SQL file (migration-helper procedures/functions).
|
|
3
|
+
* `filename` may be a bare name resolved against <migrationsDir>, OR an absolute
|
|
4
|
+
* path (used as-is, ignoring migrationsDir — lets one shared file serve both the
|
|
5
|
+
* pre and post phases, which have different migrationsDirs). Validation of the
|
|
6
|
+
* bare-vs-absolute distinction happens config-side (deploy-config-validate.ts).
|
|
7
|
+
* Returns the resolved absolute path and the executable statements.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readScaffoldingSql(migrationsDir: string, filename: string): {
|
|
10
|
+
path: string;
|
|
11
|
+
statements: string[];
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Structurally compatible with the real `Db` from './db.js' (query returns the
|
|
15
|
+
* result ROWS DIRECTLY, not the raw mysql2 [rows, fields] tuple — see db.ts's
|
|
16
|
+
* `query` implementation, which already unwraps the tuple before returning).
|
|
17
|
+
* Kept as its own narrow interface (rather than importing `Db`) so tests don't
|
|
18
|
+
* need to fake `execute`/`end`/`connectionId`, but the shape MUST match `Db.query`
|
|
19
|
+
* exactly or this cleanup silently drops nothing against production (see the
|
|
20
|
+
* 2026-07 ER-4006 incident: a `[rows] = await db.query(...)` tuple-destructure
|
|
21
|
+
* against this interface bound `rows` to the wrong thing and never dropped
|
|
22
|
+
* anything).
|
|
23
|
+
*/
|
|
24
|
+
export interface DefinerCleanupDb {
|
|
25
|
+
query(sql: string, params?: unknown[]): Promise<unknown[]>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Drop EVERY definer-carrying object (procedure, function, trigger, event) whose
|
|
29
|
+
* DEFINER is `<leaseUser>@%`, across all schemas, using DROP ... IF EXISTS.
|
|
30
|
+
*
|
|
31
|
+
* This is the ER-4006 structural guarantee: run before DROP USER (or at phase end),
|
|
32
|
+
* unconditionally. Once the user is the definer of nothing, DROP USER cannot fail
|
|
33
|
+
* with MySQL 8.4 ER 4006, regardless of what the migration created. Scoped to the
|
|
34
|
+
* lease user's OWN objects only (host-inclusive definer match), so it never touches
|
|
35
|
+
* the persistent routines account's helpers or customer objects. Returns the count.
|
|
36
|
+
*
|
|
37
|
+
* CALLER INVARIANT (for the migration runner): this function is NOT transactional
|
|
38
|
+
* across objects — DDL like DROP PROCEDURE/TRIGGER/EVENT cannot be wrapped in a
|
|
39
|
+
* MySQL transaction anyway. If a DROP throws partway through the sweep (e.g. a
|
|
40
|
+
* permissions error or a lock timeout on one object), the function aborts
|
|
41
|
+
* immediately and some definer-owned objects may be LEFT UNDROPPED. The caller
|
|
42
|
+
* MUST NOT proceed to DROP USER after a failed/partial sweep — doing so risks
|
|
43
|
+
* the exact ER 4006 failure this cleanup exists to prevent. On a thrown error,
|
|
44
|
+
* the caller should either retry the sweep (it is idempotent — DROP ... IF EXISTS)
|
|
45
|
+
* or abort the whole revoke and surface the error, but never skip straight to
|
|
46
|
+
* DROP USER.
|
|
47
|
+
*/
|
|
48
|
+
export declare function dropDefinerObjects(db: DefinerCleanupDb, leaseUser: string): Promise<number>;
|
|
49
|
+
//# sourceMappingURL=scaffolding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffolding.d.ts","sourceRoot":"","sources":["../../../src/adapters/mysql/scaffolding.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAOxC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CAC5D;AAOD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsCjG"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
|
+
import { splitStatements } from './sql-splitter.js';
|
|
4
|
+
/**
|
|
5
|
+
* Read and split the scaffolding SQL file (migration-helper procedures/functions).
|
|
6
|
+
* `filename` may be a bare name resolved against <migrationsDir>, OR an absolute
|
|
7
|
+
* path (used as-is, ignoring migrationsDir — lets one shared file serve both the
|
|
8
|
+
* pre and post phases, which have different migrationsDirs). Validation of the
|
|
9
|
+
* bare-vs-absolute distinction happens config-side (deploy-config-validate.ts).
|
|
10
|
+
* Returns the resolved absolute path and the executable statements.
|
|
11
|
+
*/
|
|
12
|
+
export function readScaffoldingSql(migrationsDir, filename) {
|
|
13
|
+
const path = isAbsolute(filename) ? filename : join(migrationsDir, filename);
|
|
14
|
+
if (!existsSync(path)) {
|
|
15
|
+
throw new Error(`scaffolding file not found: ${path}`);
|
|
16
|
+
}
|
|
17
|
+
const sql = readFileSync(path, 'utf8');
|
|
18
|
+
return { path, statements: splitStatements(sql) };
|
|
19
|
+
}
|
|
20
|
+
/** Backtick-quote a MySQL identifier (doubling embedded backticks). */
|
|
21
|
+
function q(ident) {
|
|
22
|
+
return '`' + ident.replace(/`/g, '``') + '`';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Drop EVERY definer-carrying object (procedure, function, trigger, event) whose
|
|
26
|
+
* DEFINER is `<leaseUser>@%`, across all schemas, using DROP ... IF EXISTS.
|
|
27
|
+
*
|
|
28
|
+
* This is the ER-4006 structural guarantee: run before DROP USER (or at phase end),
|
|
29
|
+
* unconditionally. Once the user is the definer of nothing, DROP USER cannot fail
|
|
30
|
+
* with MySQL 8.4 ER 4006, regardless of what the migration created. Scoped to the
|
|
31
|
+
* lease user's OWN objects only (host-inclusive definer match), so it never touches
|
|
32
|
+
* the persistent routines account's helpers or customer objects. Returns the count.
|
|
33
|
+
*
|
|
34
|
+
* CALLER INVARIANT (for the migration runner): this function is NOT transactional
|
|
35
|
+
* across objects — DDL like DROP PROCEDURE/TRIGGER/EVENT cannot be wrapped in a
|
|
36
|
+
* MySQL transaction anyway. If a DROP throws partway through the sweep (e.g. a
|
|
37
|
+
* permissions error or a lock timeout on one object), the function aborts
|
|
38
|
+
* immediately and some definer-owned objects may be LEFT UNDROPPED. The caller
|
|
39
|
+
* MUST NOT proceed to DROP USER after a failed/partial sweep — doing so risks
|
|
40
|
+
* the exact ER 4006 failure this cleanup exists to prevent. On a thrown error,
|
|
41
|
+
* the caller should either retry the sweep (it is idempotent — DROP ... IF EXISTS)
|
|
42
|
+
* or abort the whole revoke and surface the error, but never skip straight to
|
|
43
|
+
* DROP USER.
|
|
44
|
+
*/
|
|
45
|
+
export async function dropDefinerObjects(db, leaseUser) {
|
|
46
|
+
const definer = `${leaseUser}@%`;
|
|
47
|
+
let dropped = 0;
|
|
48
|
+
// Routines: procedures AND functions (ROUTINE_TYPE distinguishes; DROP keyword differs).
|
|
49
|
+
// NOTE: db.query() returns the rows array DIRECTLY (see DefinerCleanupDb doc comment
|
|
50
|
+
// above) — do NOT destructure as a [rows, fields] tuple.
|
|
51
|
+
const routineRows = (await db.query('SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE FROM information_schema.ROUTINES WHERE DEFINER = ?', [definer]));
|
|
52
|
+
for (const r of routineRows) {
|
|
53
|
+
const kw = r.ROUTINE_TYPE === 'FUNCTION' ? 'FUNCTION' : 'PROCEDURE';
|
|
54
|
+
await db.query(`DROP ${kw} IF EXISTS ${q(r.ROUTINE_SCHEMA)}.${q(r.ROUTINE_NAME)}`);
|
|
55
|
+
dropped++;
|
|
56
|
+
}
|
|
57
|
+
// Triggers.
|
|
58
|
+
const triggerRows = (await db.query('SELECT TRIGGER_SCHEMA, TRIGGER_NAME FROM information_schema.TRIGGERS WHERE DEFINER = ?', [definer]));
|
|
59
|
+
for (const t of triggerRows) {
|
|
60
|
+
await db.query(`DROP TRIGGER IF EXISTS ${q(t.TRIGGER_SCHEMA)}.${q(t.TRIGGER_NAME)}`);
|
|
61
|
+
dropped++;
|
|
62
|
+
}
|
|
63
|
+
// Events.
|
|
64
|
+
const eventRows = (await db.query('SELECT EVENT_SCHEMA, EVENT_NAME FROM information_schema.EVENTS WHERE DEFINER = ?', [definer]));
|
|
65
|
+
for (const e of eventRows) {
|
|
66
|
+
await db.query(`DROP EVENT IF EXISTS ${q(e.EVENT_SCHEMA)}.${q(e.EVENT_NAME)}`);
|
|
67
|
+
dropped++;
|
|
68
|
+
}
|
|
69
|
+
return dropped;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffolding.js","sourceRoot":"","sources":["../../../src/adapters/mysql/scaffolding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,aAAqB,EACrB,QAAgB;IAEhB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC7E,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;AACpD,CAAC;AAiBD,uEAAuE;AACvE,SAAS,CAAC,CAAC,KAAa;IACtB,OAAO,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAoB,EAAE,SAAiB;IAC9E,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,CAAC;IACjC,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,yFAAyF;IACzF,qFAAqF;IACrF,yDAAyD;IACzD,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CACjC,sGAAsG,EACtG,CAAC,OAAO,CAAC,CACV,CAA6E,CAAC;IAC/E,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,CAAC,CAAC,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;QACpE,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACnF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,YAAY;IACZ,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CACjC,wFAAwF,EACxF,CAAC,OAAO,CAAC,CACV,CAAuD,CAAC;IACzD,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,EAAE,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACrF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,UAAU;IACV,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,CAC/B,kFAAkF,EAClF,CAAC,OAAO,CAAC,CACV,CAAmD,CAAC;IACrD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC/E,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MysqlConn } from './mysql-conn.js';
|
|
2
|
+
import type { MigrationRow } from '../../core/types.js';
|
|
3
|
+
export type { MigrationRow };
|
|
4
|
+
/**
|
|
5
|
+
* Data-access layer for the schema_migrations table.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors SchemaMigrationsRepo.kt exactly:
|
|
8
|
+
* - Same CREATE TABLE DDL (version PK, checksum CHAR(64), checksum_algo VARCHAR(16),
|
|
9
|
+
* applied_at DATETIME(3), applied_by VARCHAR(128), execution_ms INT, success TINYINT(1),
|
|
10
|
+
* baselined TINYINT(1), ENGINE=InnoDB, CHARSET=utf8mb4).
|
|
11
|
+
* - success and baselined are normalized via Number() so the comparison is robust
|
|
12
|
+
* regardless of whether mysql2 returns TINYINT(1) as a JS boolean or number.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SchemaMigrationsRepo {
|
|
15
|
+
private readonly db;
|
|
16
|
+
constructor(db: MysqlConn);
|
|
17
|
+
/**
|
|
18
|
+
* Create the schema_migrations table if it does not already exist.
|
|
19
|
+
* Uses the text protocol (db.query) so the DDL executes in a single statement.
|
|
20
|
+
*/
|
|
21
|
+
ensureTable(): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Return all migration rows ordered by version ascending.
|
|
24
|
+
*/
|
|
25
|
+
all(): Promise<MigrationRow[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Insert a claim row with success=0. Called before executing a migration so
|
|
28
|
+
* that a crash mid-migration leaves a recoverable false-success marker.
|
|
29
|
+
*/
|
|
30
|
+
claim(version: string, checksum: string, appliedBy: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Flip success=1 and record execution_ms after a migration completes successfully.
|
|
33
|
+
* Throws if no row exists for the given version (defensive — claim() should always precede this).
|
|
34
|
+
*/
|
|
35
|
+
markSuccess(version: string, executionMs: number): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Insert a pre-applied (baselined) row with success=1, baselined=1.
|
|
38
|
+
* Used when seeding the baseline marker for a fresh database.
|
|
39
|
+
*/
|
|
40
|
+
seedBaseline(version: string, checksum: string, appliedBy: string): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=schema-migrations-repo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-migrations-repo.d.ts","sourceRoot":"","sources":["../../../src/adapters/mysql/schema-migrations-repo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,SAAS;IAE1C;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBlC;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAepC;;;OAGG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhF;;;OAGG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtE;;;OAGG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMxF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { CHECKSUM_ALGO } from '../../core/checksum.js';
|
|
2
|
+
/**
|
|
3
|
+
* Data-access layer for the schema_migrations table.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors SchemaMigrationsRepo.kt exactly:
|
|
6
|
+
* - Same CREATE TABLE DDL (version PK, checksum CHAR(64), checksum_algo VARCHAR(16),
|
|
7
|
+
* applied_at DATETIME(3), applied_by VARCHAR(128), execution_ms INT, success TINYINT(1),
|
|
8
|
+
* baselined TINYINT(1), ENGINE=InnoDB, CHARSET=utf8mb4).
|
|
9
|
+
* - success and baselined are normalized via Number() so the comparison is robust
|
|
10
|
+
* regardless of whether mysql2 returns TINYINT(1) as a JS boolean or number.
|
|
11
|
+
*/
|
|
12
|
+
export class SchemaMigrationsRepo {
|
|
13
|
+
db;
|
|
14
|
+
constructor(db) {
|
|
15
|
+
this.db = db;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create the schema_migrations table if it does not already exist.
|
|
19
|
+
* Uses the text protocol (db.query) so the DDL executes in a single statement.
|
|
20
|
+
*/
|
|
21
|
+
async ensureTable() {
|
|
22
|
+
await this.db.query(`
|
|
23
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
24
|
+
version VARCHAR(255) NOT NULL,
|
|
25
|
+
checksum CHAR(64) NOT NULL,
|
|
26
|
+
checksum_algo VARCHAR(16) NOT NULL,
|
|
27
|
+
applied_at DATETIME(3) NOT NULL,
|
|
28
|
+
applied_by VARCHAR(128) NOT NULL,
|
|
29
|
+
execution_ms INT NOT NULL DEFAULT 0,
|
|
30
|
+
success TINYINT(1) NOT NULL DEFAULT 0,
|
|
31
|
+
baselined TINYINT(1) NOT NULL DEFAULT 0,
|
|
32
|
+
PRIMARY KEY (version)
|
|
33
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Return all migration rows ordered by version ascending.
|
|
38
|
+
*/
|
|
39
|
+
async all() {
|
|
40
|
+
const rows = await this.db.query('SELECT version, checksum, checksum_algo, success, baselined FROM schema_migrations ORDER BY version');
|
|
41
|
+
return rows.map((r) => ({
|
|
42
|
+
version: r.version,
|
|
43
|
+
checksum: r.checksum,
|
|
44
|
+
checksumAlgo: r.checksum_algo,
|
|
45
|
+
// mysql2 may return TINYINT(1) as boolean true/false or as 0/1 depending
|
|
46
|
+
// on driver configuration and MySQL version — Number() normalizes both.
|
|
47
|
+
success: Number(r.success) === 1,
|
|
48
|
+
baselined: Number(r.baselined) === 1,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Insert a claim row with success=0. Called before executing a migration so
|
|
53
|
+
* that a crash mid-migration leaves a recoverable false-success marker.
|
|
54
|
+
*/
|
|
55
|
+
async claim(version, checksum, appliedBy) {
|
|
56
|
+
await this.db.execute('INSERT INTO schema_migrations (version, checksum, checksum_algo, applied_at, applied_by, execution_ms, success, baselined) VALUES (?, ?, ?, NOW(3), ?, 0, 0, 0)', [version, checksum, CHECKSUM_ALGO, appliedBy]);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Flip success=1 and record execution_ms after a migration completes successfully.
|
|
60
|
+
* Throws if no row exists for the given version (defensive — claim() should always precede this).
|
|
61
|
+
*/
|
|
62
|
+
async markSuccess(version, executionMs) {
|
|
63
|
+
const [okPacket] = await this.db.execute('UPDATE schema_migrations SET success = 1, execution_ms = ? WHERE version = ?', [executionMs, version]);
|
|
64
|
+
// mysql2 returns OkPacket with affectedRows for UPDATE statements
|
|
65
|
+
const affected = okPacket?.affectedRows ?? 0;
|
|
66
|
+
if (affected === 0) {
|
|
67
|
+
throw new Error(`no schema_migrations row for version ${version}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Insert a pre-applied (baselined) row with success=1, baselined=1.
|
|
72
|
+
* Used when seeding the baseline marker for a fresh database.
|
|
73
|
+
*/
|
|
74
|
+
async seedBaseline(version, checksum, appliedBy) {
|
|
75
|
+
await this.db.execute('INSERT INTO schema_migrations (version, checksum, checksum_algo, applied_at, applied_by, execution_ms, success, baselined) VALUES (?, ?, ?, NOW(3), ?, 0, 1, 1)', [version, checksum, CHECKSUM_ALGO, appliedBy]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-migrations-repo.js","sourceRoot":"","sources":["../../../src/adapters/mysql/schema-migrations-repo.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAKvD;;;;;;;;;GASG;AACH,MAAM,OAAO,oBAAoB;IACF;IAA7B,YAA6B,EAAa;QAAb,OAAE,GAAF,EAAE,CAAW;IAAG,CAAC;IAE9C;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;;;;;;;;;;;;KAYnB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG;QACP,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAC9B,qGAAqG,CACtG,CAAC;QACF,OAAQ,IAAkC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrD,OAAO,EAAE,CAAC,CAAC,OAAiB;YAC5B,QAAQ,EAAE,CAAC,CAAC,QAAkB;YAC9B,YAAY,EAAE,CAAC,CAAC,aAAuB;YACvC,yEAAyE;YACzE,wEAAwE;YACxE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC;SACrC,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,OAAe,EAAE,QAAgB,EAAE,SAAiB;QAC9D,MAAM,IAAI,CAAC,EAAE,CAAC,OAAO,CACnB,iKAAiK,EACjK,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAC,CAC9C,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,WAAmB;QACpD,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,OAAO,CACtC,8EAA8E,EAC9E,CAAC,WAAW,EAAE,OAAO,CAAC,CACvB,CAAC;QACF,kEAAkE;QAClE,MAAM,QAAQ,GAAI,QAAsC,EAAE,YAAY,IAAI,CAAC,CAAC;QAC5E,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,wCAAwC,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAAC,OAAe,EAAE,QAAgB,EAAE,SAAiB;QACrE,MAAM,IAAI,CAAC,EAAE,CAAC,OAAO,CACnB,iKAAiK,EACjK,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAC,CAC9C,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a SQL string into individual statements, respecting:
|
|
3
|
+
* - DELIMITER directives (case-insensitive, only at line start, only when no code seen yet)
|
|
4
|
+
* - Line comments (-- and #): copied verbatim, do NOT set sawCode
|
|
5
|
+
* - Block comments (/* ... * /): copied verbatim; executable comments (/*! ... * /) set sawCode
|
|
6
|
+
* - Quoted strings/identifiers: backslash-escape inside ' and "; doubled quote is close+reopen
|
|
7
|
+
* - Delimiter match → flush buffer
|
|
8
|
+
* - Non-whitespace outside comments → sawCode = true
|
|
9
|
+
* - flush() emits only when trimmed non-empty AND sawCode (suppresses comment-only buffers)
|
|
10
|
+
*
|
|
11
|
+
* NOTE: operates on the RAW file string — does NOT normalize CRLF→LF.
|
|
12
|
+
* A bare \r is NOT treated as a newline (so \rDELIMITER $$ is NOT recognized as a directive).
|
|
13
|
+
*/
|
|
14
|
+
export declare function splitStatements(sql: string): string[];
|
|
15
|
+
//# sourceMappingURL=sql-splitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-splitter.d.ts","sourceRoot":"","sources":["../../../src/adapters/mysql/sql-splitter.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAoJrD"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Faithful port of utilities/.../SqlStatementSplitter.kt. Same single-pass cursor + flags.
|
|
2
|
+
/**
|
|
3
|
+
* Split a SQL string into individual statements, respecting:
|
|
4
|
+
* - DELIMITER directives (case-insensitive, only at line start, only when no code seen yet)
|
|
5
|
+
* - Line comments (-- and #): copied verbatim, do NOT set sawCode
|
|
6
|
+
* - Block comments (/* ... * /): copied verbatim; executable comments (/*! ... * /) set sawCode
|
|
7
|
+
* - Quoted strings/identifiers: backslash-escape inside ' and "; doubled quote is close+reopen
|
|
8
|
+
* - Delimiter match → flush buffer
|
|
9
|
+
* - Non-whitespace outside comments → sawCode = true
|
|
10
|
+
* - flush() emits only when trimmed non-empty AND sawCode (suppresses comment-only buffers)
|
|
11
|
+
*
|
|
12
|
+
* NOTE: operates on the RAW file string — does NOT normalize CRLF→LF.
|
|
13
|
+
* A bare \r is NOT treated as a newline (so \rDELIMITER $$ is NOT recognized as a directive).
|
|
14
|
+
*/
|
|
15
|
+
export function splitStatements(sql) {
|
|
16
|
+
const statements = [];
|
|
17
|
+
let current = '';
|
|
18
|
+
let delimiter = ';';
|
|
19
|
+
let i = 0;
|
|
20
|
+
const n = sql.length;
|
|
21
|
+
let sawCode = false;
|
|
22
|
+
const isWs = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\r';
|
|
23
|
+
/**
|
|
24
|
+
* Returns true if position idx is at a line start.
|
|
25
|
+
* Scans backwards over space/tab/\r — a bare \r is NOT a line boundary.
|
|
26
|
+
* Only \n (or beginning-of-file) counts as a line start.
|
|
27
|
+
*/
|
|
28
|
+
const atLineStart = (idx) => {
|
|
29
|
+
let j = idx - 1;
|
|
30
|
+
while (j >= 0 && (sql[j] === ' ' || sql[j] === '\t' || sql[j] === '\r'))
|
|
31
|
+
j--;
|
|
32
|
+
return j < 0 || sql[j] === '\n';
|
|
33
|
+
};
|
|
34
|
+
const flush = () => {
|
|
35
|
+
const s = current.trim();
|
|
36
|
+
if (s.length > 0 && sawCode)
|
|
37
|
+
statements.push(s);
|
|
38
|
+
current = '';
|
|
39
|
+
sawCode = false;
|
|
40
|
+
};
|
|
41
|
+
const regionMatches = (idx, word, ci) => {
|
|
42
|
+
if (idx + word.length > n)
|
|
43
|
+
return false;
|
|
44
|
+
const seg = sql.substring(idx, idx + word.length);
|
|
45
|
+
return ci ? seg.toUpperCase() === word.toUpperCase() : seg === word;
|
|
46
|
+
};
|
|
47
|
+
while (i < n) {
|
|
48
|
+
const c = sql[i];
|
|
49
|
+
// DELIMITER directive — only at line start, only when no code seen yet
|
|
50
|
+
if ((c === 'D' || c === 'd') &&
|
|
51
|
+
atLineStart(i) &&
|
|
52
|
+
!sawCode &&
|
|
53
|
+
regionMatches(i, 'DELIMITER', true) &&
|
|
54
|
+
(i + 9 >= n || isWs(sql[i + 9] ?? '\n'))) {
|
|
55
|
+
// Skip past "DELIMITER" keyword
|
|
56
|
+
let j = i + 9;
|
|
57
|
+
// Skip horizontal whitespace before the token
|
|
58
|
+
while (j < n && (sql[j] === ' ' || sql[j] === '\t'))
|
|
59
|
+
j++;
|
|
60
|
+
const start = j;
|
|
61
|
+
// Collect the new delimiter token — stop at whitespace or trailing comment
|
|
62
|
+
while (j < n) {
|
|
63
|
+
const ch = sql[j];
|
|
64
|
+
if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r')
|
|
65
|
+
break;
|
|
66
|
+
if (ch === '-' && j + 1 < n && sql[j + 1] === '-')
|
|
67
|
+
break;
|
|
68
|
+
if (ch === '#')
|
|
69
|
+
break;
|
|
70
|
+
if (ch === '/' && j + 1 < n && sql[j + 1] === '*')
|
|
71
|
+
break;
|
|
72
|
+
j++;
|
|
73
|
+
}
|
|
74
|
+
const newDelim = sql.substring(start, j);
|
|
75
|
+
if (newDelim.length > 0)
|
|
76
|
+
delimiter = newDelim;
|
|
77
|
+
// Consume the rest of the line (up to but not including the \n)
|
|
78
|
+
while (j < n && sql[j] !== '\n')
|
|
79
|
+
j++;
|
|
80
|
+
i = j + 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// Line comment: -- (copied verbatim, does NOT set sawCode)
|
|
84
|
+
if (c === '-' && i + 1 < n && sql[i + 1] === '-') {
|
|
85
|
+
while (i < n && sql[i] !== '\n') {
|
|
86
|
+
current += sql[i];
|
|
87
|
+
i++;
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
// Line comment: # (copied verbatim, does NOT set sawCode)
|
|
92
|
+
if (c === '#') {
|
|
93
|
+
while (i < n && sql[i] !== '\n') {
|
|
94
|
+
current += sql[i];
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// Block comment: /* ... */ (executable /*! ... */ sets sawCode)
|
|
100
|
+
if (c === '/' && i + 1 < n && sql[i + 1] === '*') {
|
|
101
|
+
const isExec = i + 2 < n && sql[i + 2] === '!';
|
|
102
|
+
current += c;
|
|
103
|
+
current += sql[i + 1];
|
|
104
|
+
i += 2;
|
|
105
|
+
while (i < n && !(sql[i] === '*' && i + 1 < n && sql[i + 1] === '/')) {
|
|
106
|
+
current += sql[i];
|
|
107
|
+
i++;
|
|
108
|
+
}
|
|
109
|
+
if (i + 1 < n) {
|
|
110
|
+
current += '*/';
|
|
111
|
+
i += 2;
|
|
112
|
+
}
|
|
113
|
+
if (isExec)
|
|
114
|
+
sawCode = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Quoted strings and identifiers: ', ", `
|
|
118
|
+
if (c === "'" || c === '"' || c === '`') {
|
|
119
|
+
sawCode = true;
|
|
120
|
+
const quote = c;
|
|
121
|
+
current += c;
|
|
122
|
+
i++;
|
|
123
|
+
while (i < n) {
|
|
124
|
+
const q = sql[i];
|
|
125
|
+
current += q;
|
|
126
|
+
// Backslash escape inside ' and " (not backtick)
|
|
127
|
+
if (q === '\\' && (quote === "'" || quote === '"') && i + 1 < n) {
|
|
128
|
+
current += sql[i + 1];
|
|
129
|
+
i += 2;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
// Doubled quote = close-then-reopen (same quote-toggle path)
|
|
133
|
+
if (q === quote) {
|
|
134
|
+
if (i + 1 < n && sql[i + 1] === quote) {
|
|
135
|
+
current += quote;
|
|
136
|
+
i += 2;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
i++;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
i++;
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
// Delimiter match → flush the buffer
|
|
147
|
+
if (regionMatches(i, delimiter, false)) {
|
|
148
|
+
flush();
|
|
149
|
+
i += delimiter.length;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
// Any non-whitespace char outside a comment → sawCode = true
|
|
153
|
+
if (!/\s/.test(c))
|
|
154
|
+
sawCode = true;
|
|
155
|
+
current += c;
|
|
156
|
+
i++;
|
|
157
|
+
}
|
|
158
|
+
flush();
|
|
159
|
+
return statements;
|
|
160
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-splitter.js","sourceRoot":"","sources":["../../../src/adapters/mysql/sql-splitter.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for a single migration phase (pre or post deploy).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the plugin's `MigrationConfig` shape (`znvault-plugin-payara/src/cli/types.ts`)
|
|
5
|
+
* plus the `engine` discriminator, which is new to this package (§5 of the
|
|
6
|
+
* znvault-migrate shared-library design).
|
|
7
|
+
*/
|
|
8
|
+
export interface MigrationConfig {
|
|
9
|
+
/**
|
|
10
|
+
* REQUIRED discriminator. Only 'mysql' is supported by this project; 'postgres' is
|
|
11
|
+
* accepted by the type for forward-compat but rejected at validation until a
|
|
12
|
+
* PostgreSQL adapter ships (see the znvault-migrate PostgreSQL design, §11).
|
|
13
|
+
*/
|
|
14
|
+
engine: 'mysql' | 'postgres';
|
|
15
|
+
/** dynamic-secrets role (lease-only; NO raw creds). */
|
|
16
|
+
roleId: string;
|
|
17
|
+
/** Override if the lease doesn't pin one. */
|
|
18
|
+
database?: string;
|
|
19
|
+
/** Absolute path to the directory of migration files. */
|
|
20
|
+
migrationsDir: string;
|
|
21
|
+
/** Bare filename (relative to migrationsDir) OR an absolute path; MySQL-only. */
|
|
22
|
+
scaffoldingFile?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validate a {@link MigrationConfig}. Pure, synchronous, no I/O.
|
|
26
|
+
*
|
|
27
|
+
* Ports the plugin's `validateMigrationBlock`
|
|
28
|
+
* (`znvault-plugin-payara/src/cli/deploy-config-validate.ts:16-42`) for the
|
|
29
|
+
* `roleId`/`migrationsDir`/`scaffoldingFile` rules, and adds the `engine`
|
|
30
|
+
* discriminator rules that are new to this package (spec §5).
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateMigrationConfig(cfg: MigrationConfig): {
|
|
33
|
+
errors: string[];
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,MAAM,EAAE,OAAO,GAAG,UAAU,CAAC;IAC7B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAkClF"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Path: znvault-migrate/src/config.ts
|
|
2
|
+
import { isAbsolute } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Validate a {@link MigrationConfig}. Pure, synchronous, no I/O.
|
|
5
|
+
*
|
|
6
|
+
* Ports the plugin's `validateMigrationBlock`
|
|
7
|
+
* (`znvault-plugin-payara/src/cli/deploy-config-validate.ts:16-42`) for the
|
|
8
|
+
* `roleId`/`migrationsDir`/`scaffoldingFile` rules, and adds the `engine`
|
|
9
|
+
* discriminator rules that are new to this package (spec §5).
|
|
10
|
+
*/
|
|
11
|
+
export function validateMigrationConfig(cfg) {
|
|
12
|
+
const errors = [];
|
|
13
|
+
const engine = cfg.engine;
|
|
14
|
+
if (engine !== 'mysql' && engine !== 'postgres') {
|
|
15
|
+
errors.push("engine is required and must be 'mysql' or 'postgres'.");
|
|
16
|
+
}
|
|
17
|
+
else if (engine === 'postgres') {
|
|
18
|
+
errors.push('postgres migrations are not yet supported (the PostgreSQL adapter is a deferred project — see the znvault-migrate PostgreSQL design)');
|
|
19
|
+
}
|
|
20
|
+
if (!cfg.roleId || cfg.roleId.trim() === '') {
|
|
21
|
+
errors.push('roleId is required (the dynamic-secrets write role).');
|
|
22
|
+
}
|
|
23
|
+
if (!cfg.migrationsDir || cfg.migrationsDir.trim() === '') {
|
|
24
|
+
errors.push('migrationsDir is required.');
|
|
25
|
+
}
|
|
26
|
+
if (cfg.scaffoldingFile !== undefined) {
|
|
27
|
+
if (typeof cfg.scaffoldingFile !== 'string' || cfg.scaffoldingFile.length === 0) {
|
|
28
|
+
errors.push('scaffoldingFile must be a non-empty filename or absolute path.');
|
|
29
|
+
}
|
|
30
|
+
else if (!isAbsolute(cfg.scaffoldingFile) &&
|
|
31
|
+
(cfg.scaffoldingFile.includes('/') || cfg.scaffoldingFile.includes('\\'))) {
|
|
32
|
+
errors.push('scaffoldingFile must be a bare filename (relative to migrationsDir) or an absolute path — a relative path with separators is not allowed.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { errors };
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AA0BvC;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAoB;IAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,MAAM,MAAM,GAAI,GAA4B,CAAC,MAAM,CAAC;IACpD,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;SAAM,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,CAAC,IAAI,CACT,sIAAsI,CACvI,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,GAAG,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACtC,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChF,MAAM,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAChF,CAAC;aAAM,IACL,CAAC,UAAU,CAAC,GAAG,CAAC,eAAe,CAAC;YAChC,CAAC,GAAG,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EACzE,CAAC;YACD,MAAM,CAAC,IAAI,CACT,2IAA2I,CAC5I,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline-marker.d.ts","sourceRoot":"","sources":["../../src/core/baseline-marker.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline-marker.js","sourceRoot":"","sources":["../../src/core/baseline-marker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,MAAM,EAAE,GAAG,qCAAqC,CAAC;AACjD,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC;QAAC,OAAO,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const CHECKSUM_ALGO = "sha256-lf-v1";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the canonical sha256-lf-v1 checksum of a migration file buffer.
|
|
4
|
+
*
|
|
5
|
+
* Algorithm (byte-level — never decode to string):
|
|
6
|
+
* 1. Strip UTF-8 BOM (EF BB BF) at offset 0, if present.
|
|
7
|
+
* 2. Remove ALL 0x0D bytes (bare CR and CR in CRLF pairs).
|
|
8
|
+
* 3. SHA-256 the resulting bytes.
|
|
9
|
+
* 4. Return lowercase hex.
|
|
10
|
+
*
|
|
11
|
+
* This matches the Kotlin MigrationChecksummer implementation exactly.
|
|
12
|
+
*/
|
|
13
|
+
export declare function canonicalChecksum(buf: Buffer): string;
|
|
14
|
+
/**
|
|
15
|
+
* Read a migration file from disk and return its canonical sha256-lf-v1 checksum.
|
|
16
|
+
*/
|
|
17
|
+
export declare function canonicalChecksumFile(path: string): string;
|
|
18
|
+
//# sourceMappingURL=checksum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checksum.d.ts","sourceRoot":"","sources":["../../src/core/checksum.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,iBAAiB,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CASrD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D"}
|