@deterministic-code/migraters 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/bundle.yaml +262 -0
  2. package/dist/abstractions/migrater.d.ts +28 -0
  3. package/dist/abstractions/migrater.js +1 -0
  4. package/dist/abstractions/sql-dialect-base.d.ts +16 -0
  5. package/dist/abstractions/sql-dialect-base.js +19 -0
  6. package/dist/abstractions/sql-dialect-factory.d.ts +8 -0
  7. package/dist/abstractions/sql-dialect-factory.js +22 -0
  8. package/dist/abstractions/sql-dialect.d.ts +16 -0
  9. package/dist/abstractions/sql-dialect.js +1 -0
  10. package/dist/bin/migrate-create.d.ts +2 -0
  11. package/dist/bin/migrate-create.js +71 -0
  12. package/dist/bin/migrate-down.d.ts +2 -0
  13. package/dist/bin/migrate-down.js +135 -0
  14. package/dist/bin/migrate-init.d.ts +2 -0
  15. package/dist/bin/migrate-init.js +77 -0
  16. package/dist/bin/migrate-setup.d.ts +2 -0
  17. package/dist/bin/migrate-setup.js +87 -0
  18. package/dist/bin/migrate-up.d.ts +2 -0
  19. package/dist/bin/migrate-up.js +97 -0
  20. package/dist/cli-contract.d.ts +6 -0
  21. package/dist/cli-contract.js +19 -0
  22. package/dist/cli-spec.d.ts +24 -0
  23. package/dist/cli-spec.js +33 -0
  24. package/dist/cli.d.ts +9 -0
  25. package/dist/cli.js +41 -0
  26. package/dist/create-sequence.d.ts +4 -0
  27. package/dist/create-sequence.js +16 -0
  28. package/dist/datasource-migrate.d.ts +31 -0
  29. package/dist/datasource-migrate.js +206 -0
  30. package/dist/dialects/mysql-dialect.d.ts +12 -0
  31. package/dist/dialects/mysql-dialect.js +56 -0
  32. package/dist/dialects/oracle-dialect.d.ts +18 -0
  33. package/dist/dialects/oracle-dialect.js +76 -0
  34. package/dist/dialects/postgres-dialect.d.ts +11 -0
  35. package/dist/dialects/postgres-dialect.js +58 -0
  36. package/dist/dialects/sqlite-dialect.d.ts +13 -0
  37. package/dist/dialects/sqlite-dialect.js +96 -0
  38. package/dist/dialects/sqlserver-dialect.d.ts +12 -0
  39. package/dist/dialects/sqlserver-dialect.js +73 -0
  40. package/dist/help.d.ts +2 -0
  41. package/dist/help.js +10 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.js +6 -0
  44. package/dist/infrastructure/connection-resolver.d.ts +7 -0
  45. package/dist/infrastructure/connection-resolver.js +19 -0
  46. package/dist/infrastructure/default-factory.d.ts +4 -0
  47. package/dist/infrastructure/default-factory.js +15 -0
  48. package/dist/infrastructure/interop-default.d.ts +3 -0
  49. package/dist/infrastructure/interop-default.js +1 -0
  50. package/dist/infrastructure/message-templates.d.ts +3 -0
  51. package/dist/infrastructure/message-templates.js +18 -0
  52. package/dist/infrastructure/split-on-go.d.ts +2 -0
  53. package/dist/infrastructure/split-on-go.js +20 -0
  54. package/dist/infrastructure/sql-templates.d.ts +2 -0
  55. package/dist/infrastructure/sql-templates.js +4 -0
  56. package/dist/infrastructure/templates-root.d.ts +2 -0
  57. package/dist/infrastructure/templates-root.js +11 -0
  58. package/dist/init-select.d.ts +12 -0
  59. package/dist/init-select.js +52 -0
  60. package/dist/migrater.d.ts +12 -0
  61. package/dist/migrater.js +84 -0
  62. package/dist/migration-client.d.ts +21 -0
  63. package/dist/migration-client.js +1 -0
  64. package/dist/path-exists.d.ts +1 -0
  65. package/dist/path-exists.js +10 -0
  66. package/dist/sql.d.ts +3 -0
  67. package/dist/sql.js +15 -0
  68. package/dist/templates/help/create.txt +16 -0
  69. package/dist/templates/help/down.txt +42 -0
  70. package/dist/templates/help/init.txt +13 -0
  71. package/dist/templates/help/setup.txt +41 -0
  72. package/dist/templates/help/up.txt +43 -0
  73. package/dist/templates/messages/checksum-drift-line.txt +1 -0
  74. package/dist/templates/messages/checksum-drift.txt +11 -0
  75. package/dist/templates/messages/down/confirm-mismatch.txt +1 -0
  76. package/dist/templates/messages/down/confirm-prompt.txt +1 -0
  77. package/dist/templates/messages/down/confirm-stdin-failed.txt +1 -0
  78. package/dist/templates/messages/down/confirm-token-mismatch.txt +1 -0
  79. package/dist/templates/messages/down/confirm-tty-required.txt +1 -0
  80. package/dist/templates/messages/down/destructive-header.txt +3 -0
  81. package/dist/templates/messages/errors/invalid-name.txt +1 -0
  82. package/dist/templates/messages/errors/migrate-path-not-dir.txt +1 -0
  83. package/dist/templates/messages/errors/missing-connection.txt +1 -0
  84. package/dist/templates/messages/errors/missing-name-hint.txt +1 -0
  85. package/dist/templates/messages/errors/missing-name.txt +1 -0
  86. package/dist/templates/messages/errors/missing-provider-hint.txt +1 -0
  87. package/dist/templates/messages/errors/missing-provider.txt +1 -0
  88. package/dist/templates/messages/errors/missing-value-for-flag.txt +1 -0
  89. package/dist/templates/messages/errors/rollback-no-down-sibling.txt +1 -0
  90. package/dist/templates/messages/errors/rollback-no-rollback-sibling.txt +1 -0
  91. package/dist/templates/messages/errors/rollback-not-in-path.txt +1 -0
  92. package/dist/templates/messages/errors/sqlite-prerequisite.txt +1 -0
  93. package/dist/templates/messages/errors/unknown-arg.txt +1 -0
  94. package/dist/templates/messages/errors/unsupported-provider.txt +1 -0
  95. package/dist/templates/messages/setup-complete.txt +9 -0
  96. package/dist/templates/messages/status/applied.txt +1 -0
  97. package/dist/templates/messages/status/created.txt +1 -0
  98. package/dist/templates/messages/status/no-more-pending.txt +1 -0
  99. package/dist/templates/messages/status/no-pending.txt +1 -0
  100. package/dist/templates/messages/status/no-rollback.txt +1 -0
  101. package/dist/templates/messages/status/rolled-back.txt +1 -0
  102. package/dist/templates/scaffold/down.sql +1 -0
  103. package/dist/templates/scaffold/up.sql +1 -0
  104. package/dist/templates/sql/mysql/migrate_logs.sql +11 -0
  105. package/dist/templates/sql/mysql/migrates.sql +7 -0
  106. package/dist/templates/sql/oracle/migrate_logs.sql +14 -0
  107. package/dist/templates/sql/oracle/migrates.sql +10 -0
  108. package/dist/templates/sql/postgres/migrate_logs.sql +11 -0
  109. package/dist/templates/sql/postgres/migrates.sql +7 -0
  110. package/dist/templates/sql/sqlite/migrate_logs.sql +11 -0
  111. package/dist/templates/sql/sqlite/migrates.sql +7 -0
  112. package/dist/templates/sql/sqlserver/migrate_logs.sql +12 -0
  113. package/dist/templates/sql/sqlserver/migrates.sql +8 -0
  114. package/package.json +78 -4
  115. package/src/abstractions/migrater.ts +13 -0
  116. package/src/abstractions/sql-dialect-base.ts +36 -0
  117. package/src/abstractions/sql-dialect-factory.ts +29 -0
  118. package/src/abstractions/sql-dialect.ts +17 -0
  119. package/src/bin/migrate-create.ts +80 -0
  120. package/src/bin/migrate-down.ts +163 -0
  121. package/src/bin/migrate-init.ts +88 -0
  122. package/src/bin/migrate-setup.ts +98 -0
  123. package/src/bin/migrate-up.ts +111 -0
  124. package/src/cli-contract.ts +25 -0
  125. package/src/cli-spec.ts +64 -0
  126. package/src/cli.ts +52 -0
  127. package/src/create-sequence.ts +17 -0
  128. package/src/datasource-migrate.ts +314 -0
  129. package/src/dialects/mysql-dialect.test.ts +93 -0
  130. package/src/dialects/mysql-dialect.ts +67 -0
  131. package/src/dialects/oracle-dialect.test.ts +118 -0
  132. package/src/dialects/oracle-dialect.ts +91 -0
  133. package/src/dialects/postgres-dialect.test.ts +80 -0
  134. package/src/dialects/postgres-dialect.ts +65 -0
  135. package/src/dialects/sqlite-dialect.rollback.test.ts +30 -0
  136. package/src/dialects/sqlite-dialect.test.ts +77 -0
  137. package/src/dialects/sqlite-dialect.ts +96 -0
  138. package/src/dialects/sqlserver-dialect.test.ts +99 -0
  139. package/src/dialects/sqlserver-dialect.ts +87 -0
  140. package/src/generate-help.test.ts +33 -0
  141. package/src/generate-help.ts +173 -0
  142. package/src/help.test.ts +48 -0
  143. package/src/help.ts +12 -0
  144. package/src/index.ts +23 -0
  145. package/src/infrastructure/connection-resolver.ts +18 -0
  146. package/src/infrastructure/default-factory.test.ts +18 -0
  147. package/src/infrastructure/default-factory.ts +17 -0
  148. package/src/infrastructure/interop-default.test.ts +9 -0
  149. package/src/infrastructure/interop-default.ts +2 -0
  150. package/src/infrastructure/message-templates.ts +30 -0
  151. package/src/infrastructure/split-on-go.ts +19 -0
  152. package/src/infrastructure/sql-templates.ts +10 -0
  153. package/src/infrastructure/templates-root.ts +13 -0
  154. package/src/init-select.test.ts +26 -0
  155. package/src/init-select.ts +73 -0
  156. package/src/migrater.ts +92 -0
  157. package/src/migration-client.ts +21 -0
  158. package/src/path-exists.ts +10 -0
  159. package/src/setup-sql.test.ts +16 -0
  160. package/src/sql.ts +17 -0
  161. package/tsconfig.build.json +19 -0
  162. package/README.md +0 -3
@@ -0,0 +1,18 @@
1
+ import type { MigrationClient } from '../migration-client.ts';
2
+ import { SqlDialectBase } from '../abstractions/sql-dialect-base.ts';
3
+ interface OracleConnectionConfig {
4
+ user: string;
5
+ password: string;
6
+ connectString: string;
7
+ }
8
+ export declare const parseOracleConnection: (connection: string | OracleConnectionConfig) => OracleConnectionConfig;
9
+ export declare class OracleDialect extends SqlDialectBase {
10
+ readonly name: "oracle";
11
+ readonly connectionEnvironmentVariables: readonly ["ORACLE_CONNECT_STRING", "DATABASE_URL"];
12
+ readonly migratesDdl: string;
13
+ readonly migrateLogsDdl: string;
14
+ translatePlaceholders(sql: string): string;
15
+ limitClause(n: number): string;
16
+ createClient(connection: string): Promise<MigrationClient>;
17
+ }
18
+ export {};
@@ -0,0 +1,76 @@
1
+ import { interopDefault } from "../infrastructure/interop-default.js";
2
+ import { readSqlTemplate } from "../infrastructure/sql-templates.js";
3
+ import { SqlDialectBase } from "../abstractions/sql-dialect-base.js";
4
+ const [migratesDdl, migrateLogsDdl] = await Promise.all([
5
+ readSqlTemplate('oracle', 'migrates'),
6
+ readSqlTemplate('oracle', 'migrate_logs'),
7
+ ]);
8
+ export const parseOracleConnection = (connection) => {
9
+ if (typeof connection === 'object' && connection)
10
+ return connection;
11
+ const m = /^([^/]+)\/([^@]+)@(.+)$/.exec(String(connection));
12
+ if (!m) {
13
+ throw new Error(`Oracle connection must look like user/password@connectString`);
14
+ }
15
+ return { user: m[1], password: m[2], connectString: m[3] };
16
+ };
17
+ const lowercaseKeys = (row) => {
18
+ const out = {};
19
+ for (const [k, v] of Object.entries(row))
20
+ out[k.toLowerCase()] = v;
21
+ return out;
22
+ };
23
+ export class OracleDialect extends SqlDialectBase {
24
+ name = 'oracle';
25
+ connectionEnvironmentVariables = ['ORACLE_CONNECT_STRING', 'DATABASE_URL'];
26
+ migratesDdl = migratesDdl;
27
+ migrateLogsDdl = migrateLogsDdl;
28
+ translatePlaceholders(sql) {
29
+ let n = 0;
30
+ return sql.replace(/\?/g, () => `:${++n}`);
31
+ }
32
+ limitClause(n) {
33
+ return `FETCH FIRST ${n} ROWS ONLY`;
34
+ }
35
+ async createClient(connection) {
36
+ const oracledb = interopDefault(await import('oracledb'));
37
+ const conn = await oracledb.getConnection(parseOracleConnection(connection));
38
+ const translate = (sql) => this.translatePlaceholders(sql);
39
+ return {
40
+ dialect: 'oracle',
41
+ async exec(sql) {
42
+ await conn.execute(sql, [], { autoCommit: true });
43
+ },
44
+ async query(sql, params = []) {
45
+ const r = await conn.execute(translate(sql), params, {
46
+ outFormat: oracledb.OUT_FORMAT_OBJECT,
47
+ });
48
+ return (r.rows ?? []).map(lowercaseKeys);
49
+ },
50
+ async run(sql, params = []) {
51
+ await conn.execute(translate(sql), params, {
52
+ autoCommit: false,
53
+ });
54
+ },
55
+ async transaction(fn) {
56
+ try {
57
+ const r = await fn();
58
+ await conn.commit();
59
+ return r;
60
+ }
61
+ catch (e) {
62
+ try {
63
+ await conn.rollback();
64
+ }
65
+ catch (rollbackErr) {
66
+ console.warn('oracle rollback failed after transaction error; original error rethrown', rollbackErr);
67
+ }
68
+ throw e;
69
+ }
70
+ },
71
+ async close() {
72
+ await conn.close();
73
+ },
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,11 @@
1
+ import type { MigrationClient } from '../migration-client.ts';
2
+ import { SqlDialectBase } from '../abstractions/sql-dialect-base.ts';
3
+ export declare class PostgresDialect extends SqlDialectBase {
4
+ readonly name: "postgres";
5
+ readonly connectionEnvironmentVariables: readonly ["PG_CONNECTION_STRING", "DATABASE_URL"];
6
+ readonly migratesDdl: string;
7
+ readonly migrateLogsDdl: string;
8
+ nowExpr(): string;
9
+ translatePlaceholders(sql: string): string;
10
+ createClient(connection: string): Promise<MigrationClient>;
11
+ }
@@ -0,0 +1,58 @@
1
+ import { readSqlTemplate } from "../infrastructure/sql-templates.js";
2
+ import { SqlDialectBase } from "../abstractions/sql-dialect-base.js";
3
+ const [migratesDdl, migrateLogsDdl] = await Promise.all([
4
+ readSqlTemplate('postgres', 'migrates'),
5
+ readSqlTemplate('postgres', 'migrate_logs'),
6
+ ]);
7
+ export class PostgresDialect extends SqlDialectBase {
8
+ name = 'postgres';
9
+ connectionEnvironmentVariables = ['PG_CONNECTION_STRING', 'DATABASE_URL'];
10
+ migratesDdl = migratesDdl;
11
+ migrateLogsDdl = migrateLogsDdl;
12
+ nowExpr() {
13
+ return 'NOW()';
14
+ }
15
+ translatePlaceholders(sql) {
16
+ let n = 0;
17
+ return sql.replace(/\?/g, () => `$${++n}`);
18
+ }
19
+ async createClient(connection) {
20
+ const { default: pg } = await import('pg');
21
+ const client = new pg.Client({ connectionString: connection });
22
+ await client.connect();
23
+ const translate = (sql) => this.translatePlaceholders(sql);
24
+ return {
25
+ dialect: 'postgres',
26
+ async exec(sql) {
27
+ await client.query(sql);
28
+ },
29
+ async query(sql, params = []) {
30
+ const r = await client.query(translate(sql), params);
31
+ return r.rows;
32
+ },
33
+ async run(sql, params = []) {
34
+ await client.query(translate(sql), params);
35
+ },
36
+ async transaction(fn) {
37
+ await client.query('BEGIN');
38
+ try {
39
+ const r = await fn();
40
+ await client.query('COMMIT');
41
+ return r;
42
+ }
43
+ catch (e) {
44
+ try {
45
+ await client.query('ROLLBACK');
46
+ }
47
+ catch (rollbackErr) {
48
+ console.warn('postgres ROLLBACK failed after transaction error; original error rethrown', rollbackErr);
49
+ }
50
+ throw e;
51
+ }
52
+ },
53
+ async close() {
54
+ await client.end();
55
+ },
56
+ };
57
+ }
58
+ }
@@ -0,0 +1,13 @@
1
+ import type { MigrationClient } from '../migration-client.ts';
2
+ import { SqlDialectBase } from '../abstractions/sql-dialect-base.ts';
3
+ export declare const sqliteFilesystemPath: (connection: string) => string | null;
4
+ export declare class SqliteDialect extends SqlDialectBase {
5
+ readonly name: "sqlite";
6
+ readonly connectionEnvironmentVariables: readonly ["SQLITE_PATH", "DB_PATH"];
7
+ readonly migratesDdl: string;
8
+ readonly migrateLogsDdl: string;
9
+ readonly usesLastInsertRowid = true;
10
+ prerequisiteError(connection: string): Promise<string | null>;
11
+ prepareSetup(connection: string): Promise<void>;
12
+ createClient(connection: string): Promise<MigrationClient>;
13
+ }
@@ -0,0 +1,96 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { pathExists } from "../path-exists.js";
4
+ import { readSqlTemplate } from "../infrastructure/sql-templates.js";
5
+ import { fillTemplate, loadMessage } from "../infrastructure/message-templates.js";
6
+ import { SqlDialectBase } from "../abstractions/sql-dialect-base.js";
7
+ const [migratesDdl, migrateLogsDdl, sqlitePrerequisiteTpl] = await Promise.all([
8
+ readSqlTemplate('sqlite', 'migrates'),
9
+ readSqlTemplate('sqlite', 'migrate_logs'),
10
+ loadMessage('errors/sqlite-prerequisite'),
11
+ ]);
12
+ export const sqliteFilesystemPath = (connection) => {
13
+ let s = connection.trim();
14
+ const eq = s.indexOf('=');
15
+ if (eq >= 0 && /data\s*source/i.test(s.slice(0, eq))) {
16
+ s = s.slice(eq + 1).trim();
17
+ }
18
+ else if (/^sqlite:\/\//i.test(s)) {
19
+ s = s.slice('sqlite://'.length);
20
+ }
21
+ else if (/^sqlite:/i.test(s)) {
22
+ s = s.slice('sqlite:'.length);
23
+ }
24
+ else if (/^file:/i.test(s)) {
25
+ s = s.slice('file:'.length);
26
+ }
27
+ if (s === ':memory:' || s === '')
28
+ return null;
29
+ return s;
30
+ };
31
+ export class SqliteDialect extends SqlDialectBase {
32
+ name = 'sqlite';
33
+ connectionEnvironmentVariables = ['SQLITE_PATH', 'DB_PATH'];
34
+ migratesDdl = migratesDdl;
35
+ migrateLogsDdl = migrateLogsDdl;
36
+ usesLastInsertRowid = true;
37
+ async prerequisiteError(connection) {
38
+ const path = sqliteFilesystemPath(connection);
39
+ if (path === null)
40
+ return null;
41
+ if (await pathExists(path))
42
+ return null;
43
+ return fillTemplate(sqlitePrerequisiteTpl, { path });
44
+ }
45
+ async prepareSetup(connection) {
46
+ const filePath = sqliteFilesystemPath(connection);
47
+ if (filePath !== null) {
48
+ await mkdir(dirname(filePath), { recursive: true });
49
+ }
50
+ }
51
+ async createClient(connection) {
52
+ if (!connection)
53
+ throw new Error('sqlite requires a database file path');
54
+ const { default: Database } = await import('better-sqlite3');
55
+ const db = new Database(connection);
56
+ return {
57
+ dialect: 'sqlite',
58
+ async exec(sql) {
59
+ db.exec(sql);
60
+ },
61
+ async query(sql, params = []) {
62
+ const stmt = db.prepare(sql);
63
+ try {
64
+ return stmt.all(...params);
65
+ }
66
+ catch {
67
+ stmt.run(...params);
68
+ return [];
69
+ }
70
+ },
71
+ async run(sql, params = []) {
72
+ db.prepare(sql).run(...params);
73
+ },
74
+ async transaction(fn) {
75
+ db.exec('BEGIN');
76
+ try {
77
+ const r = await fn();
78
+ db.exec('COMMIT');
79
+ return r;
80
+ }
81
+ catch (e) {
82
+ try {
83
+ db.exec('ROLLBACK');
84
+ }
85
+ catch (rollbackErr) {
86
+ console.warn('sqlite ROLLBACK failed after transaction error; original error rethrown', rollbackErr);
87
+ }
88
+ throw e;
89
+ }
90
+ },
91
+ async close() {
92
+ db.close();
93
+ },
94
+ };
95
+ }
96
+ }
@@ -0,0 +1,12 @@
1
+ import type { MigrationClient } from '../migration-client.ts';
2
+ import { SqlDialectBase } from '../abstractions/sql-dialect-base.ts';
3
+ export declare class SqlServerDialect extends SqlDialectBase {
4
+ readonly name: "sqlserver";
5
+ readonly connectionEnvironmentVariables: readonly ["MSSQL_URL", "DATABASE_URL"];
6
+ readonly migratesDdl: string;
7
+ readonly migrateLogsDdl: string;
8
+ quoteIdent(ident: string): string;
9
+ nowExpr(): string;
10
+ limitClause(n: number): string;
11
+ createClient(connection: string): Promise<MigrationClient>;
12
+ }
@@ -0,0 +1,73 @@
1
+ import { interopDefault } from "../infrastructure/interop-default.js";
2
+ import { readSqlTemplate } from "../infrastructure/sql-templates.js";
3
+ import { SqlDialectBase } from "../abstractions/sql-dialect-base.js";
4
+ const [migratesDdl, migrateLogsDdl] = await Promise.all([
5
+ readSqlTemplate('sqlserver', 'migrates'),
6
+ readSqlTemplate('sqlserver', 'migrate_logs'),
7
+ ]);
8
+ const bindMssql = (req, sql, params) => {
9
+ let n = 0;
10
+ return sql.replace(/\?/g, () => {
11
+ const name = `p${n}`;
12
+ req.input(name, params[n]);
13
+ n++;
14
+ return `@${name}`;
15
+ });
16
+ };
17
+ export class SqlServerDialect extends SqlDialectBase {
18
+ name = 'sqlserver';
19
+ connectionEnvironmentVariables = ['MSSQL_URL', 'DATABASE_URL'];
20
+ migratesDdl = migratesDdl;
21
+ migrateLogsDdl = migrateLogsDdl;
22
+ quoteIdent(ident) {
23
+ return `[${ident}]`;
24
+ }
25
+ nowExpr() {
26
+ return 'SYSUTCDATETIME()';
27
+ }
28
+ limitClause(n) {
29
+ return `OFFSET 0 ROWS FETCH NEXT ${n} ROWS ONLY`;
30
+ }
31
+ async createClient(connection) {
32
+ const mssql = interopDefault(await import('mssql'));
33
+ const pool = await mssql.connect(connection);
34
+ return {
35
+ dialect: 'sqlserver',
36
+ async exec(sql) {
37
+ await pool.request().batch(sql);
38
+ },
39
+ async query(sql, params = []) {
40
+ const req = pool.request();
41
+ const translated = bindMssql(req, sql, params);
42
+ const r = await req.query(translated);
43
+ return r.recordset ?? [];
44
+ },
45
+ async run(sql, params = []) {
46
+ const req = pool.request();
47
+ const translated = bindMssql(req, sql, params);
48
+ await req.query(translated);
49
+ },
50
+ async transaction(fn) {
51
+ const tx = pool.transaction();
52
+ await tx.begin();
53
+ try {
54
+ const r = await fn();
55
+ await tx.commit();
56
+ return r;
57
+ }
58
+ catch (e) {
59
+ try {
60
+ await tx.rollback();
61
+ }
62
+ catch (rollbackErr) {
63
+ console.warn('sqlserver rollback failed after transaction error; original error rethrown', rollbackErr);
64
+ }
65
+ throw e;
66
+ }
67
+ },
68
+ async close() {
69
+ await pool.close();
70
+ },
71
+ };
72
+ }
73
+ }
package/dist/help.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const loadHelp: (command: string) => Promise<string>;
2
+ export declare const loadProgramUsage: () => Promise<string>;
package/dist/help.js ADDED
@@ -0,0 +1,10 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { fillHelpTemplate, programUsageText, verbFromCommand } from "./cli-contract.js";
4
+ import { templatesRoot } from "./infrastructure/templates-root.js";
5
+ export const loadHelp = async (command) => {
6
+ const verb = verbFromCommand(command);
7
+ const text = await readFile(join(templatesRoot(), 'help', `${verb}.txt`), 'utf8');
8
+ return fillHelpTemplate(text, command);
9
+ };
10
+ export const loadProgramUsage = async () => programUsageText();
@@ -0,0 +1,7 @@
1
+ export * from './datasource-migrate.ts';
2
+ export type { CreateArg, CreateResponse, DownArg, IMigrater, SetupArg, UpArg, } from './abstractions/migrater.ts';
3
+ export { getMigrater, Migrater } from './migrater.ts';
4
+ export { fillHelpTemplate, migrateCommand, verbFromCommand, type MigrateVerb, } from './cli-contract.ts';
5
+ export { loadHelp, loadProgramUsage } from './help.ts';
6
+ export { fillTemplate, loadMessage, loadScaffold, } from './infrastructure/message-templates.ts';
7
+ export { normalizeDialect, pathExists, type SqlDialect } from './sql.ts';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./datasource-migrate.js";
2
+ export { getMigrater, Migrater } from "./migrater.js";
3
+ export { fillHelpTemplate, migrateCommand, verbFromCommand, } from "./cli-contract.js";
4
+ export { loadHelp, loadProgramUsage } from "./help.js";
5
+ export { fillTemplate, loadMessage, loadScaffold, } from "./infrastructure/message-templates.js";
6
+ export { normalizeDialect, pathExists } from "./sql.js";
@@ -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;