@noego/proper 0.0.8 → 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/bin/cli.js +185 -52
- package/bin/cli.js.map +1 -1
- package/bin/cli.mjs +187 -53
- package/bin/cli.mjs.map +1 -1
- package/bin/index.d.mts +104 -2
- package/bin/index.d.ts +104 -2
- package/bin/index.js +187 -52
- package/bin/index.js.map +1 -1
- package/bin/index.mjs +184 -52
- package/bin/index.mjs.map +1 -1
- package/lib/runner.js.map +1 -1
- package/lib/runner.mjs.map +1 -1
- package/package.json +5 -3
package/bin/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import mysql from 'mysql2/promise';
|
|
2
|
+
import * as sqlite from 'sqlite';
|
|
2
3
|
|
|
3
4
|
interface MigrationConfig {
|
|
4
5
|
config_file?: string;
|
|
@@ -15,6 +16,20 @@ interface MigrationConfig {
|
|
|
15
16
|
sqlite?: {
|
|
16
17
|
database: string;
|
|
17
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* PostgreSQL. Either a single `connectionString` (also readable from
|
|
21
|
+
* `process.env.DATABASE_URL` when omitted) or discrete fields.
|
|
22
|
+
* `password` falls back to `process.env.PG_PASSWORD`.
|
|
23
|
+
*/
|
|
24
|
+
pg?: {
|
|
25
|
+
connectionString?: string;
|
|
26
|
+
host?: string;
|
|
27
|
+
user?: string;
|
|
28
|
+
database?: string;
|
|
29
|
+
password?: string;
|
|
30
|
+
port?: number;
|
|
31
|
+
ssl?: boolean | Record<string, unknown>;
|
|
32
|
+
};
|
|
18
33
|
seeds?: {
|
|
19
34
|
migrationsDir?: string;
|
|
20
35
|
dataDir?: string;
|
|
@@ -31,6 +46,92 @@ interface ISQLRunner {
|
|
|
31
46
|
execute(sql: string, params?: any[]): Promise<any>;
|
|
32
47
|
end(): Promise<void>;
|
|
33
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Base class that implements the common contract and helper utilities that are
|
|
51
|
+
* shared between the different dialect runners. The concrete subclasses only
|
|
52
|
+
* need to implement the three primitive methods `_query`, `_execute` and
|
|
53
|
+
* `_end` that perform the actual driver-specific interaction. Everything else
|
|
54
|
+
* – such as ensuring a uniform return shape – is handled here once.
|
|
55
|
+
*/
|
|
56
|
+
declare abstract class BaseSQLRunner implements ISQLRunner {
|
|
57
|
+
abstract _query(sql: string, params?: any[]): Promise<any>;
|
|
58
|
+
abstract _execute(sql: string, params?: any[]): Promise<any>;
|
|
59
|
+
abstract _end(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Ensures that both MySQL and SQLite return the same tuple shape that callers
|
|
62
|
+
* expect: `[rowsOrResult, extra]`. For SQLite there is no `extra` metadata
|
|
63
|
+
* comparable to MySQL's `FieldPacket[]`, so we just use `undefined`.
|
|
64
|
+
*/
|
|
65
|
+
query(sql: string, params?: any[]): Promise<any>;
|
|
66
|
+
execute(sql: string, params?: any[]): Promise<any>;
|
|
67
|
+
end(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
declare class SQLRunner extends BaseSQLRunner {
|
|
70
|
+
private connection;
|
|
71
|
+
constructor(connection: mysql.Connection);
|
|
72
|
+
_query(sql: string, params?: any[]): Promise<[mysql.OkPacket | mysql.RowDataPacket[] | mysql.ResultSetHeader[] | mysql.RowDataPacket[][] | mysql.OkPacket[] | mysql.ProcedureCallPacket, mysql.FieldPacket[]]>;
|
|
73
|
+
_execute(sql: string, params?: any[]): Promise<[mysql.OkPacket | mysql.RowDataPacket[] | mysql.ResultSetHeader[] | mysql.RowDataPacket[][] | mysql.OkPacket[] | mysql.ProcedureCallPacket, mysql.FieldPacket[]]>;
|
|
74
|
+
_end(): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
declare class SQLiteRunner extends BaseSQLRunner {
|
|
77
|
+
private connection;
|
|
78
|
+
constructor(connection: sqlite.Database | any);
|
|
79
|
+
private prepareStatement;
|
|
80
|
+
private finalizeStatement;
|
|
81
|
+
private statementAll;
|
|
82
|
+
private statementRun;
|
|
83
|
+
_query(sql: string, params?: any[]): Promise<any>;
|
|
84
|
+
_execute(sql: string, params?: any[]): Promise<any>;
|
|
85
|
+
/**
|
|
86
|
+
* Checks if SQL is empty or contains only comments/whitespace.
|
|
87
|
+
* Returns true if there is no actual SQL to execute.
|
|
88
|
+
*/
|
|
89
|
+
private isEmptySQL;
|
|
90
|
+
private isMultiStatement;
|
|
91
|
+
private executeMultiStatement;
|
|
92
|
+
private removeComments;
|
|
93
|
+
_end(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Minimal structural type for a `pg` Client or Pool (or anything shaped like
|
|
97
|
+
* one, e.g. a Hyperdrive/Neon client). We only rely on `query()` and `end()`.
|
|
98
|
+
*/
|
|
99
|
+
interface PgQueryable {
|
|
100
|
+
query(text: string, values?: any[]): Promise<{
|
|
101
|
+
rows: any[];
|
|
102
|
+
rowCount: number | null;
|
|
103
|
+
}>;
|
|
104
|
+
end?(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* PostgreSQL runner.
|
|
108
|
+
*
|
|
109
|
+
* Proper's internal bookkeeping statements use MySQL-style `?` placeholders;
|
|
110
|
+
* Postgres wants `$1..$n`, so parameterised statements are rewritten here.
|
|
111
|
+
* Migration files themselves are executed verbatim with no parameters — a
|
|
112
|
+
* parameter-less `query()` goes through the simple protocol, which allows
|
|
113
|
+
* multiple `;`-separated statements in one call, so no client-side splitting
|
|
114
|
+
* (as SQLite needs) is required.
|
|
115
|
+
*/
|
|
116
|
+
declare class PgRunner extends BaseSQLRunner {
|
|
117
|
+
private connection;
|
|
118
|
+
constructor(connection: PgQueryable);
|
|
119
|
+
/** `?` -> `$1`, `$2`, ... outside of string literals / comments. */
|
|
120
|
+
static toPositional(sql: string): string;
|
|
121
|
+
private run;
|
|
122
|
+
_query(sql: string, params?: any[]): Promise<(any[] | {
|
|
123
|
+
rows: any[];
|
|
124
|
+
rowCount: number | null;
|
|
125
|
+
})[]>;
|
|
126
|
+
_execute(sql: string, params?: any[]): Promise<({
|
|
127
|
+
rows: any[];
|
|
128
|
+
rowCount: number | null;
|
|
129
|
+
} | {
|
|
130
|
+
changes: number;
|
|
131
|
+
lastID: undefined;
|
|
132
|
+
})[]>;
|
|
133
|
+
_end(): Promise<void>;
|
|
134
|
+
}
|
|
34
135
|
|
|
35
136
|
declare abstract class MigrationNode {
|
|
36
137
|
name: string;
|
|
@@ -76,10 +177,11 @@ declare class MigrationDirectoryReader {
|
|
|
76
177
|
private read_strategy;
|
|
77
178
|
private sqlrunner;
|
|
78
179
|
private dialect;
|
|
79
|
-
constructor(directory: string, read_strategy: any, sqlrunner: ISQLRunner, dialect?: 'sql' | 'sqlite');
|
|
180
|
+
constructor(directory: string, read_strategy: any, sqlrunner: ISQLRunner, dialect?: 'sql' | 'sqlite' | 'pg');
|
|
80
181
|
/**
|
|
81
182
|
* Resolves the appropriate file for a migration based on dialect.
|
|
82
183
|
* Priority: dialect-specific file > generic file
|
|
184
|
+
* File extensions: `.mysql.up.sql`, `.sqlite.up.sql`, `.pg.up.sql`.
|
|
83
185
|
*/
|
|
84
186
|
private resolveFile;
|
|
85
187
|
/**
|
|
@@ -176,4 +278,4 @@ type SeedFactory = {
|
|
|
176
278
|
};
|
|
177
279
|
declare function createSeedFactory(options: SeedFactoryOptions): SeedFactory;
|
|
178
280
|
|
|
179
|
-
export { type MigrationConfig, MySQLMigrationRunner as MigrationRunner, MigrationRunnerFactory, type SeedContext, type SeedFactory, type SeedFactoryOptions, createSeedFactory, loadMigrationConfig, runSeedsWithRunner };
|
|
281
|
+
export { type ISQLRunner, type MigrationConfig, MySQLMigrationRunner as MigrationRunner, MigrationRunnerFactory, type PgQueryable, PgRunner, SQLRunner, SQLiteRunner, type SeedContext, type SeedFactory, type SeedFactoryOptions, createSeedFactory, loadMigrationConfig, runSeedsWithRunner };
|
package/bin/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import mysql from 'mysql2/promise';
|
|
2
|
+
import * as sqlite from 'sqlite';
|
|
2
3
|
|
|
3
4
|
interface MigrationConfig {
|
|
4
5
|
config_file?: string;
|
|
@@ -15,6 +16,20 @@ interface MigrationConfig {
|
|
|
15
16
|
sqlite?: {
|
|
16
17
|
database: string;
|
|
17
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* PostgreSQL. Either a single `connectionString` (also readable from
|
|
21
|
+
* `process.env.DATABASE_URL` when omitted) or discrete fields.
|
|
22
|
+
* `password` falls back to `process.env.PG_PASSWORD`.
|
|
23
|
+
*/
|
|
24
|
+
pg?: {
|
|
25
|
+
connectionString?: string;
|
|
26
|
+
host?: string;
|
|
27
|
+
user?: string;
|
|
28
|
+
database?: string;
|
|
29
|
+
password?: string;
|
|
30
|
+
port?: number;
|
|
31
|
+
ssl?: boolean | Record<string, unknown>;
|
|
32
|
+
};
|
|
18
33
|
seeds?: {
|
|
19
34
|
migrationsDir?: string;
|
|
20
35
|
dataDir?: string;
|
|
@@ -31,6 +46,92 @@ interface ISQLRunner {
|
|
|
31
46
|
execute(sql: string, params?: any[]): Promise<any>;
|
|
32
47
|
end(): Promise<void>;
|
|
33
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Base class that implements the common contract and helper utilities that are
|
|
51
|
+
* shared between the different dialect runners. The concrete subclasses only
|
|
52
|
+
* need to implement the three primitive methods `_query`, `_execute` and
|
|
53
|
+
* `_end` that perform the actual driver-specific interaction. Everything else
|
|
54
|
+
* – such as ensuring a uniform return shape – is handled here once.
|
|
55
|
+
*/
|
|
56
|
+
declare abstract class BaseSQLRunner implements ISQLRunner {
|
|
57
|
+
abstract _query(sql: string, params?: any[]): Promise<any>;
|
|
58
|
+
abstract _execute(sql: string, params?: any[]): Promise<any>;
|
|
59
|
+
abstract _end(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Ensures that both MySQL and SQLite return the same tuple shape that callers
|
|
62
|
+
* expect: `[rowsOrResult, extra]`. For SQLite there is no `extra` metadata
|
|
63
|
+
* comparable to MySQL's `FieldPacket[]`, so we just use `undefined`.
|
|
64
|
+
*/
|
|
65
|
+
query(sql: string, params?: any[]): Promise<any>;
|
|
66
|
+
execute(sql: string, params?: any[]): Promise<any>;
|
|
67
|
+
end(): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
declare class SQLRunner extends BaseSQLRunner {
|
|
70
|
+
private connection;
|
|
71
|
+
constructor(connection: mysql.Connection);
|
|
72
|
+
_query(sql: string, params?: any[]): Promise<[mysql.OkPacket | mysql.RowDataPacket[] | mysql.ResultSetHeader[] | mysql.RowDataPacket[][] | mysql.OkPacket[] | mysql.ProcedureCallPacket, mysql.FieldPacket[]]>;
|
|
73
|
+
_execute(sql: string, params?: any[]): Promise<[mysql.OkPacket | mysql.RowDataPacket[] | mysql.ResultSetHeader[] | mysql.RowDataPacket[][] | mysql.OkPacket[] | mysql.ProcedureCallPacket, mysql.FieldPacket[]]>;
|
|
74
|
+
_end(): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
declare class SQLiteRunner extends BaseSQLRunner {
|
|
77
|
+
private connection;
|
|
78
|
+
constructor(connection: sqlite.Database | any);
|
|
79
|
+
private prepareStatement;
|
|
80
|
+
private finalizeStatement;
|
|
81
|
+
private statementAll;
|
|
82
|
+
private statementRun;
|
|
83
|
+
_query(sql: string, params?: any[]): Promise<any>;
|
|
84
|
+
_execute(sql: string, params?: any[]): Promise<any>;
|
|
85
|
+
/**
|
|
86
|
+
* Checks if SQL is empty or contains only comments/whitespace.
|
|
87
|
+
* Returns true if there is no actual SQL to execute.
|
|
88
|
+
*/
|
|
89
|
+
private isEmptySQL;
|
|
90
|
+
private isMultiStatement;
|
|
91
|
+
private executeMultiStatement;
|
|
92
|
+
private removeComments;
|
|
93
|
+
_end(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Minimal structural type for a `pg` Client or Pool (or anything shaped like
|
|
97
|
+
* one, e.g. a Hyperdrive/Neon client). We only rely on `query()` and `end()`.
|
|
98
|
+
*/
|
|
99
|
+
interface PgQueryable {
|
|
100
|
+
query(text: string, values?: any[]): Promise<{
|
|
101
|
+
rows: any[];
|
|
102
|
+
rowCount: number | null;
|
|
103
|
+
}>;
|
|
104
|
+
end?(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* PostgreSQL runner.
|
|
108
|
+
*
|
|
109
|
+
* Proper's internal bookkeeping statements use MySQL-style `?` placeholders;
|
|
110
|
+
* Postgres wants `$1..$n`, so parameterised statements are rewritten here.
|
|
111
|
+
* Migration files themselves are executed verbatim with no parameters — a
|
|
112
|
+
* parameter-less `query()` goes through the simple protocol, which allows
|
|
113
|
+
* multiple `;`-separated statements in one call, so no client-side splitting
|
|
114
|
+
* (as SQLite needs) is required.
|
|
115
|
+
*/
|
|
116
|
+
declare class PgRunner extends BaseSQLRunner {
|
|
117
|
+
private connection;
|
|
118
|
+
constructor(connection: PgQueryable);
|
|
119
|
+
/** `?` -> `$1`, `$2`, ... outside of string literals / comments. */
|
|
120
|
+
static toPositional(sql: string): string;
|
|
121
|
+
private run;
|
|
122
|
+
_query(sql: string, params?: any[]): Promise<(any[] | {
|
|
123
|
+
rows: any[];
|
|
124
|
+
rowCount: number | null;
|
|
125
|
+
})[]>;
|
|
126
|
+
_execute(sql: string, params?: any[]): Promise<({
|
|
127
|
+
rows: any[];
|
|
128
|
+
rowCount: number | null;
|
|
129
|
+
} | {
|
|
130
|
+
changes: number;
|
|
131
|
+
lastID: undefined;
|
|
132
|
+
})[]>;
|
|
133
|
+
_end(): Promise<void>;
|
|
134
|
+
}
|
|
34
135
|
|
|
35
136
|
declare abstract class MigrationNode {
|
|
36
137
|
name: string;
|
|
@@ -76,10 +177,11 @@ declare class MigrationDirectoryReader {
|
|
|
76
177
|
private read_strategy;
|
|
77
178
|
private sqlrunner;
|
|
78
179
|
private dialect;
|
|
79
|
-
constructor(directory: string, read_strategy: any, sqlrunner: ISQLRunner, dialect?: 'sql' | 'sqlite');
|
|
180
|
+
constructor(directory: string, read_strategy: any, sqlrunner: ISQLRunner, dialect?: 'sql' | 'sqlite' | 'pg');
|
|
80
181
|
/**
|
|
81
182
|
* Resolves the appropriate file for a migration based on dialect.
|
|
82
183
|
* Priority: dialect-specific file > generic file
|
|
184
|
+
* File extensions: `.mysql.up.sql`, `.sqlite.up.sql`, `.pg.up.sql`.
|
|
83
185
|
*/
|
|
84
186
|
private resolveFile;
|
|
85
187
|
/**
|
|
@@ -176,4 +278,4 @@ type SeedFactory = {
|
|
|
176
278
|
};
|
|
177
279
|
declare function createSeedFactory(options: SeedFactoryOptions): SeedFactory;
|
|
178
280
|
|
|
179
|
-
export { type MigrationConfig, MySQLMigrationRunner as MigrationRunner, MigrationRunnerFactory, type SeedContext, type SeedFactory, type SeedFactoryOptions, createSeedFactory, loadMigrationConfig, runSeedsWithRunner };
|
|
281
|
+
export { type ISQLRunner, type MigrationConfig, MySQLMigrationRunner as MigrationRunner, MigrationRunnerFactory, type PgQueryable, PgRunner, SQLRunner, SQLiteRunner, type SeedContext, type SeedFactory, type SeedFactoryOptions, createSeedFactory, loadMigrationConfig, runSeedsWithRunner };
|
package/bin/index.js
CHANGED
|
@@ -68,6 +68,9 @@ var index_exports = {};
|
|
|
68
68
|
__export(index_exports, {
|
|
69
69
|
MigrationRunner: () => MySQLMigrationRunner,
|
|
70
70
|
MigrationRunnerFactory: () => MigrationRunnerFactory,
|
|
71
|
+
PgRunner: () => PgRunner,
|
|
72
|
+
SQLRunner: () => SQLRunner,
|
|
73
|
+
SQLiteRunner: () => SQLiteRunner,
|
|
71
74
|
createSeedFactory: () => createSeedFactory,
|
|
72
75
|
loadMigrationConfig: () => loadMigrationConfig,
|
|
73
76
|
runSeedsWithRunner: () => runSeedsWithRunner
|
|
@@ -76,9 +79,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
76
79
|
|
|
77
80
|
// framework/MigrationRunner.ts
|
|
78
81
|
var import_fs3 = __toESM(require("fs"));
|
|
79
|
-
var import_promise = __toESM(require("mysql2/promise"));
|
|
80
|
-
var sqlite = __toESM(require("sqlite"));
|
|
81
|
-
var sqlite3 = __toESM(require("sqlite3"));
|
|
82
82
|
|
|
83
83
|
// framework/MigrationDirectoryReader.ts
|
|
84
84
|
var import_fs = __toESM(require("fs"));
|
|
@@ -370,9 +370,10 @@ var MigrationDirectoryReader = class {
|
|
|
370
370
|
/**
|
|
371
371
|
* Resolves the appropriate file for a migration based on dialect.
|
|
372
372
|
* Priority: dialect-specific file > generic file
|
|
373
|
+
* File extensions: `.mysql.up.sql`, `.sqlite.up.sql`, `.pg.up.sql`.
|
|
373
374
|
*/
|
|
374
375
|
resolveFile(baseName, direction) {
|
|
375
|
-
const dialectExt = this.dialect === "sql" ? "mysql" :
|
|
376
|
+
const dialectExt = this.dialect === "sql" ? "mysql" : this.dialect;
|
|
376
377
|
const dialectFile = import_path.default.join(this.directory, `${baseName}.${dialectExt}.${direction}.sql`);
|
|
377
378
|
if (import_fs.default.existsSync(dialectFile)) return dialectFile;
|
|
378
379
|
const genericFile = import_path.default.join(this.directory, `${baseName}.${direction}.sql`);
|
|
@@ -383,14 +384,14 @@ var MigrationDirectoryReader = class {
|
|
|
383
384
|
* Checks if a file path is dialect-specific (contains .mysql. or .sqlite. in the name)
|
|
384
385
|
*/
|
|
385
386
|
isDialectSpecific(filePath) {
|
|
386
|
-
return /\.(mysql|sqlite)\.(up|down)\.sql$/i.test(filePath);
|
|
387
|
+
return /\.(mysql|sqlite|pg)\.(up|down)\.sql$/i.test(filePath);
|
|
387
388
|
}
|
|
388
389
|
loadMigrations(table, connection) {
|
|
389
390
|
import_fs.default.existsSync(this.directory) || import_fs.default.mkdirSync(this.directory);
|
|
390
391
|
const dir_content = import_fs.default.readdirSync(this.directory, { withFileTypes: true }).filter((file) => file.isFile()).map((file) => file.name);
|
|
391
392
|
const uniqueKeys = /* @__PURE__ */ new Set();
|
|
392
393
|
dir_content.forEach((file) => {
|
|
393
|
-
const key = file.replace(/(?:\.(mysql|sqlite))?\.(up|down)\.(sql|js)/i, "").toLowerCase();
|
|
394
|
+
const key = file.replace(/(?:\.(mysql|sqlite|pg))?\.(up|down)\.(sql|js)/i, "").toLowerCase();
|
|
394
395
|
uniqueKeys.add(key);
|
|
395
396
|
});
|
|
396
397
|
const migration_sorter = {};
|
|
@@ -462,6 +463,12 @@ var MigrationSetup = class {
|
|
|
462
463
|
up TEXT,
|
|
463
464
|
down TEXT,
|
|
464
465
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
466
|
+
)` : this.config.database === "pg" ? `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
467
|
+
id SERIAL PRIMARY KEY,
|
|
468
|
+
migration_key TEXT,
|
|
469
|
+
up TEXT,
|
|
470
|
+
down TEXT,
|
|
471
|
+
created_at TIMESTAMPTZ DEFAULT now()
|
|
465
472
|
)` : `CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
466
473
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
467
474
|
migration_key VARCHAR(255),
|
|
@@ -521,31 +528,35 @@ function MySqlDialectParser(sql) {
|
|
|
521
528
|
}
|
|
522
529
|
return result;
|
|
523
530
|
}
|
|
524
|
-
function
|
|
525
|
-
const
|
|
526
|
-
let result = "";
|
|
527
|
-
let isInSqliteBlock = true;
|
|
528
|
-
const startSqliteRegex = /^\s*--\s*\[\s*sqlite\s*\]\s*$/i;
|
|
531
|
+
function markerDialectParser(names) {
|
|
532
|
+
const startRegex = new RegExp(`^\\s*--\\s*\\[\\s*(${names.join("|")})\\s*\\]\\s*$`, "i");
|
|
529
533
|
const anyDialectRegex = /^\s*--\s*\[\s*\w+\s*\]\s*$/i;
|
|
530
|
-
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
534
|
+
return function(sql) {
|
|
535
|
+
const lines = sql.split("\n");
|
|
536
|
+
let result = "";
|
|
537
|
+
let capturing = true;
|
|
538
|
+
for (const line of lines) {
|
|
539
|
+
const trimmed = line.trim();
|
|
540
|
+
if (startRegex.test(trimmed)) {
|
|
541
|
+
capturing = true;
|
|
542
|
+
result += line + "\n";
|
|
543
|
+
continue;
|
|
544
|
+
} else if (anyDialectRegex.test(trimmed)) {
|
|
545
|
+
capturing = false;
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
if (!capturing && line.toLowerCase().includes("create index")) {
|
|
549
|
+
capturing = true;
|
|
550
|
+
}
|
|
551
|
+
if (capturing) {
|
|
552
|
+
result += line + "\n";
|
|
553
|
+
}
|
|
545
554
|
}
|
|
546
|
-
|
|
547
|
-
|
|
555
|
+
return result;
|
|
556
|
+
};
|
|
548
557
|
}
|
|
558
|
+
var SqliteDialectParser = markerDialectParser(["sqlite"]);
|
|
559
|
+
var PgDialectParser = markerDialectParser(["pg", "postgres", "postgresql"]);
|
|
549
560
|
|
|
550
561
|
// framework/SQLRunner.ts
|
|
551
562
|
var BaseSQLRunner = class {
|
|
@@ -761,12 +772,122 @@ ${sql}
|
|
|
761
772
|
});
|
|
762
773
|
}
|
|
763
774
|
};
|
|
775
|
+
var PgRunner = class _PgRunner extends BaseSQLRunner {
|
|
776
|
+
constructor(connection) {
|
|
777
|
+
super();
|
|
778
|
+
this.connection = connection;
|
|
779
|
+
}
|
|
780
|
+
/** `?` -> `$1`, `$2`, ... outside of string literals / comments. */
|
|
781
|
+
static toPositional(sql) {
|
|
782
|
+
let out = "";
|
|
783
|
+
let n = 0;
|
|
784
|
+
let inSingle = false;
|
|
785
|
+
let inDouble = false;
|
|
786
|
+
let inLineComment = false;
|
|
787
|
+
let inBlockComment = false;
|
|
788
|
+
for (let i = 0; i < sql.length; i++) {
|
|
789
|
+
const ch = sql[i];
|
|
790
|
+
const next = sql[i + 1];
|
|
791
|
+
if (inLineComment) {
|
|
792
|
+
out += ch;
|
|
793
|
+
if (ch === "\n") inLineComment = false;
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if (inBlockComment) {
|
|
797
|
+
out += ch;
|
|
798
|
+
if (ch === "*" && next === "/") {
|
|
799
|
+
out += next;
|
|
800
|
+
i++;
|
|
801
|
+
inBlockComment = false;
|
|
802
|
+
}
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (inSingle) {
|
|
806
|
+
out += ch;
|
|
807
|
+
if (ch === "'") inSingle = false;
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
if (inDouble) {
|
|
811
|
+
out += ch;
|
|
812
|
+
if (ch === '"') inDouble = false;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (ch === "-" && next === "-") {
|
|
816
|
+
out += ch;
|
|
817
|
+
inLineComment = true;
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
if (ch === "/" && next === "*") {
|
|
821
|
+
out += ch + next;
|
|
822
|
+
i++;
|
|
823
|
+
inBlockComment = true;
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
if (ch === "'") {
|
|
827
|
+
out += ch;
|
|
828
|
+
inSingle = true;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (ch === '"') {
|
|
832
|
+
out += ch;
|
|
833
|
+
inDouble = true;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (ch === "?") {
|
|
837
|
+
out += `$${++n}`;
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
out += ch;
|
|
841
|
+
}
|
|
842
|
+
return out;
|
|
843
|
+
}
|
|
844
|
+
run(sql, params) {
|
|
845
|
+
return __async(this, null, function* () {
|
|
846
|
+
if (params.length > 0) {
|
|
847
|
+
return yield this.connection.query(_PgRunner.toPositional(sql), params);
|
|
848
|
+
}
|
|
849
|
+
return yield this.connection.query(sql);
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
_query(_0) {
|
|
853
|
+
return __async(this, arguments, function* (sql, params = []) {
|
|
854
|
+
const result = yield this.run(sql, params);
|
|
855
|
+
return [result.rows, result];
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
_execute(_0) {
|
|
859
|
+
return __async(this, arguments, function* (sql, params = []) {
|
|
860
|
+
var _a;
|
|
861
|
+
const result = yield this.run(sql, params);
|
|
862
|
+
return [{ changes: (_a = result.rowCount) != null ? _a : 0, lastID: void 0 }, result];
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
_end() {
|
|
866
|
+
return __async(this, null, function* () {
|
|
867
|
+
if (this.connection && typeof this.connection.end === "function") {
|
|
868
|
+
yield this.connection.end();
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
};
|
|
764
873
|
|
|
765
874
|
// framework/MigrationRunner.ts
|
|
766
875
|
function toError(error) {
|
|
767
876
|
if (error instanceof Error) return error;
|
|
768
877
|
return new Error(String(error));
|
|
769
878
|
}
|
|
879
|
+
function makeRunner(database, conn) {
|
|
880
|
+
switch (database) {
|
|
881
|
+
case "sql":
|
|
882
|
+
return new SQLRunner(conn);
|
|
883
|
+
case "sqlite":
|
|
884
|
+
return new SQLiteRunner(conn);
|
|
885
|
+
case "pg":
|
|
886
|
+
return new PgRunner(conn);
|
|
887
|
+
default:
|
|
888
|
+
throw ConfigurationError.unknownDatabaseType(database);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
770
891
|
function loadMigrationConfig(configFile) {
|
|
771
892
|
const reader = new FileMigrationConfigReader(configFile);
|
|
772
893
|
return reader.loadFile();
|
|
@@ -787,6 +908,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
787
908
|
}
|
|
788
909
|
static createConnection(config) {
|
|
789
910
|
return __async(this, null, function* () {
|
|
911
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
790
912
|
let conn = null;
|
|
791
913
|
switch (config.database) {
|
|
792
914
|
case "sql":
|
|
@@ -797,7 +919,8 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
797
919
|
password: process.env.SQL_PASSWORD
|
|
798
920
|
}, config.sql);
|
|
799
921
|
try {
|
|
800
|
-
|
|
922
|
+
const mysql = yield import("mysql2/promise");
|
|
923
|
+
conn = yield ((_b = (_a = mysql.default) == null ? void 0 : _a.createConnection) != null ? _b : mysql.createConnection)(settings);
|
|
801
924
|
return conn;
|
|
802
925
|
} catch (error) {
|
|
803
926
|
throw DatabaseConnectionError.connectionFailed("sql", toError(error).message);
|
|
@@ -807,14 +930,32 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
807
930
|
throw ConfigurationError.missingDatabaseConfiguration("sqlite");
|
|
808
931
|
}
|
|
809
932
|
try {
|
|
810
|
-
|
|
933
|
+
const sqlite = yield import("sqlite");
|
|
934
|
+
const sqlite3 = yield import("sqlite3");
|
|
935
|
+
conn = yield ((_d = (_c = sqlite.default) == null ? void 0 : _c.open) != null ? _d : sqlite.open)({
|
|
811
936
|
filename: config.sqlite.database,
|
|
812
|
-
driver: sqlite3.Database
|
|
937
|
+
driver: (_f = (_e = sqlite3.default) == null ? void 0 : _e.Database) != null ? _f : sqlite3.Database
|
|
813
938
|
});
|
|
814
939
|
return conn;
|
|
815
940
|
} catch (error) {
|
|
816
941
|
throw DatabaseConnectionError.connectionFailed("sqlite", toError(error).message);
|
|
817
942
|
}
|
|
943
|
+
case "pg":
|
|
944
|
+
if (!config.pg && !process.env.DATABASE_URL) {
|
|
945
|
+
throw ConfigurationError.missingDatabaseConfiguration("pg");
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
const pgcfg = (_g = config.pg) != null ? _g : {};
|
|
949
|
+
const connectionString = (_h = pgcfg.connectionString) != null ? _h : process.env.DATABASE_URL;
|
|
950
|
+
const settings2 = connectionString ? { connectionString, ssl: pgcfg.ssl } : __spreadProps(__spreadValues({}, pgcfg), { password: (_i = pgcfg.password) != null ? _i : process.env.PG_PASSWORD });
|
|
951
|
+
const pg = yield import("pg");
|
|
952
|
+
const Client = (_k = (_j = pg.default) == null ? void 0 : _j.Client) != null ? _k : pg.Client;
|
|
953
|
+
conn = new Client(settings2);
|
|
954
|
+
yield conn.connect();
|
|
955
|
+
return conn;
|
|
956
|
+
} catch (error) {
|
|
957
|
+
throw DatabaseConnectionError.connectionFailed("pg", toError(error).message);
|
|
958
|
+
}
|
|
818
959
|
default:
|
|
819
960
|
throw ConfigurationError.unknownDatabaseType(config.database);
|
|
820
961
|
}
|
|
@@ -835,16 +976,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
835
976
|
sqlrunner = conn;
|
|
836
977
|
driverConnection = null;
|
|
837
978
|
} else {
|
|
838
|
-
|
|
839
|
-
case "sql":
|
|
840
|
-
sqlrunner = new SQLRunner(conn);
|
|
841
|
-
break;
|
|
842
|
-
case "sqlite":
|
|
843
|
-
sqlrunner = new SQLiteRunner(conn);
|
|
844
|
-
break;
|
|
845
|
-
default:
|
|
846
|
-
throw ConfigurationError.unknownDatabaseType(config.database);
|
|
847
|
-
}
|
|
979
|
+
sqlrunner = makeRunner(config.database, conn);
|
|
848
980
|
}
|
|
849
981
|
const setup = new MigrationSetup(sqlrunner, config);
|
|
850
982
|
const read_strategy = this.getReadStategy(config);
|
|
@@ -856,17 +988,7 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
856
988
|
createEmpty(config) {
|
|
857
989
|
return __async(this, null, function* () {
|
|
858
990
|
const conn = null;
|
|
859
|
-
|
|
860
|
-
switch (config.database) {
|
|
861
|
-
case "sql":
|
|
862
|
-
sqlrunner = new SQLRunner(conn);
|
|
863
|
-
break;
|
|
864
|
-
case "sqlite":
|
|
865
|
-
sqlrunner = new SQLiteRunner(conn);
|
|
866
|
-
break;
|
|
867
|
-
default:
|
|
868
|
-
throw ConfigurationError.unknownDatabaseType(config.database);
|
|
869
|
-
}
|
|
991
|
+
const sqlrunner = makeRunner(config.database, conn);
|
|
870
992
|
const setup = new MigrationSetup(sqlrunner, config);
|
|
871
993
|
const read_strategy = this.getReadStategy(config);
|
|
872
994
|
const migration_files = new MigrationDirectoryReader(config.migration_folder, read_strategy, sqlrunner, config.database);
|
|
@@ -879,6 +1001,8 @@ var MigrationRunnerFactory = class _MigrationRunnerFactory {
|
|
|
879
1001
|
return MySqlDialectParser;
|
|
880
1002
|
case "sqlite":
|
|
881
1003
|
return SqliteDialectParser;
|
|
1004
|
+
case "pg":
|
|
1005
|
+
return PgDialectParser;
|
|
882
1006
|
default:
|
|
883
1007
|
throw ConfigurationError.unknownDatabaseType(config.database);
|
|
884
1008
|
}
|
|
@@ -1065,6 +1189,8 @@ var FileMigrationConfigReader = class {
|
|
|
1065
1189
|
config.database = "sql";
|
|
1066
1190
|
} else if (config.sqlite) {
|
|
1067
1191
|
config.database = "sqlite";
|
|
1192
|
+
} else if (config.pg) {
|
|
1193
|
+
config.database = "pg";
|
|
1068
1194
|
} else {
|
|
1069
1195
|
throw ConfigurationError.missingRequiredProperty("database");
|
|
1070
1196
|
}
|
|
@@ -1118,8 +1244,10 @@ var MigrationCreator = class {
|
|
|
1118
1244
|
// framework/SeedRunner.ts
|
|
1119
1245
|
var import_fs4 = __toESM(require("fs"));
|
|
1120
1246
|
var import_path2 = __toESM(require("path"));
|
|
1247
|
+
var import_url = require("url");
|
|
1121
1248
|
var import_ajv = __toESM(require("ajv"));
|
|
1122
1249
|
var import_ajv_formats = __toESM(require("ajv-formats"));
|
|
1250
|
+
var import_api = require("tsx/esm/api");
|
|
1123
1251
|
function resolveAlias(name, aliasMap) {
|
|
1124
1252
|
var _a;
|
|
1125
1253
|
if (!aliasMap) return name;
|
|
@@ -1256,6 +1384,10 @@ function runSqlSeed(runner, resolved, direction) {
|
|
|
1256
1384
|
function loadSeedModule(modulePath) {
|
|
1257
1385
|
return __async(this, null, function* () {
|
|
1258
1386
|
const resolved = import_path2.default.resolve(modulePath);
|
|
1387
|
+
if (resolved.endsWith(".ts")) {
|
|
1388
|
+
const fileUrl = (0, import_url.pathToFileURL)(resolved).href;
|
|
1389
|
+
return (0, import_api.tsImport)(fileUrl, fileUrl);
|
|
1390
|
+
}
|
|
1259
1391
|
return import(resolved);
|
|
1260
1392
|
});
|
|
1261
1393
|
}
|
|
@@ -1374,6 +1506,9 @@ function createSeedFactory(options) {
|
|
|
1374
1506
|
0 && (module.exports = {
|
|
1375
1507
|
MigrationRunner,
|
|
1376
1508
|
MigrationRunnerFactory,
|
|
1509
|
+
PgRunner,
|
|
1510
|
+
SQLRunner,
|
|
1511
|
+
SQLiteRunner,
|
|
1377
1512
|
createSeedFactory,
|
|
1378
1513
|
loadMigrationConfig,
|
|
1379
1514
|
runSeedsWithRunner
|