@kurdel/migrations 0.1.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/lib/blueprint.d.ts +29 -0
- package/lib/blueprint.js +75 -0
- package/lib/blueprint.js.map +1 -0
- package/lib/column.d.ts +17 -0
- package/lib/column.js +56 -0
- package/lib/column.js.map +1 -0
- package/lib/consts.d.ts +1 -0
- package/lib/consts.js +2 -0
- package/lib/consts.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -0
- package/lib/migration-loader.d.ts +5 -0
- package/lib/migration-loader.js +21 -0
- package/lib/migration-loader.js.map +1 -0
- package/lib/migration-manager.d.ts +18 -0
- package/lib/migration-manager.js +104 -0
- package/lib/migration-manager.js.map +1 -0
- package/lib/migration-registry.d.ts +18 -0
- package/lib/migration-registry.js +78 -0
- package/lib/migration-registry.js.map +1 -0
- package/lib/migration.d.ts +8 -0
- package/lib/migration.js +8 -0
- package/lib/migration.js.map +1 -0
- package/lib/schema.d.ts +12 -0
- package/lib/schema.js +36 -0
- package/lib/schema.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Andrii Sorokin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @kurdel/migrations
|
|
2
|
+
|
|
3
|
+
Database schema migration primitives for Kurdel. The package provides the
|
|
4
|
+
`Migration`, `MigrationManager`, `Schema`, and `Blueprint` APIs used to define,
|
|
5
|
+
apply, roll back, and refresh migrations.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @kurdel/migrations@beta @kurdel/db@beta
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Migration example
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Migration } from '@kurdel/migrations';
|
|
17
|
+
|
|
18
|
+
export default class CreateUsers extends Migration {
|
|
19
|
+
async up() {
|
|
20
|
+
await this.schema.create('users', table => {
|
|
21
|
+
table.integer('id').primaryKey();
|
|
22
|
+
table.string('email').unique();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async down() {
|
|
27
|
+
await this.schema.dropIfExists('users');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use `@kurdel/pirx` to run migrations from the command line.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
Node.js 20.19+, 22.12+, or 24+.
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
MIT © Andrii Sorokin
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Column } from './column.js';
|
|
2
|
+
export interface IndexDefinition {
|
|
3
|
+
columns: string[];
|
|
4
|
+
name?: string;
|
|
5
|
+
unique: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare class Blueprint {
|
|
8
|
+
private readonly columns;
|
|
9
|
+
private readonly constraints;
|
|
10
|
+
private readonly indexes;
|
|
11
|
+
integer(name: string): Column;
|
|
12
|
+
string(name: string, length?: number): Column;
|
|
13
|
+
boolean(name: string): Column;
|
|
14
|
+
datetime(name: string): Column;
|
|
15
|
+
/** Supports both the legacy single-column form and composite keys. */
|
|
16
|
+
primaryKey(columns: string | string[]): void;
|
|
17
|
+
unique(columns: string[], name?: string): void;
|
|
18
|
+
foreign(column: string, referencesTable: string, referencesColumn: string, options?: {
|
|
19
|
+
name?: string;
|
|
20
|
+
onDelete?: string;
|
|
21
|
+
}): void;
|
|
22
|
+
index(columns: string[], name?: string): void;
|
|
23
|
+
uniqueIndex(columns: string[], name?: string): void;
|
|
24
|
+
getColumnDefinitions(): string;
|
|
25
|
+
getIndexes(): readonly IndexDefinition[];
|
|
26
|
+
private addColumn;
|
|
27
|
+
private requireColumn;
|
|
28
|
+
private requireColumns;
|
|
29
|
+
}
|
package/lib/blueprint.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Column } from './column.js';
|
|
2
|
+
export class Blueprint {
|
|
3
|
+
columns = [];
|
|
4
|
+
constraints = [];
|
|
5
|
+
indexes = [];
|
|
6
|
+
integer(name) {
|
|
7
|
+
return this.addColumn(name, 'INTEGER');
|
|
8
|
+
}
|
|
9
|
+
string(name, length = 255) {
|
|
10
|
+
return this.addColumn(name, `VARCHAR(${length})`);
|
|
11
|
+
}
|
|
12
|
+
boolean(name) {
|
|
13
|
+
return this.addColumn(name, 'BOOLEAN');
|
|
14
|
+
}
|
|
15
|
+
datetime(name) {
|
|
16
|
+
return this.addColumn(name, 'DATETIME');
|
|
17
|
+
}
|
|
18
|
+
/** Supports both the legacy single-column form and composite keys. */
|
|
19
|
+
primaryKey(columns) {
|
|
20
|
+
if (typeof columns === 'string') {
|
|
21
|
+
this.requireColumn(columns).primaryKey();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.requireColumns(columns);
|
|
25
|
+
this.constraints.push(`PRIMARY KEY (${columns.join(', ')})`);
|
|
26
|
+
}
|
|
27
|
+
unique(columns, name) {
|
|
28
|
+
this.requireColumns(columns);
|
|
29
|
+
const prefix = name ? `CONSTRAINT ${name} ` : '';
|
|
30
|
+
this.constraints.push(`${prefix}UNIQUE (${columns.join(', ')})`);
|
|
31
|
+
}
|
|
32
|
+
foreign(column, referencesTable, referencesColumn, options = {}) {
|
|
33
|
+
this.requireColumn(column);
|
|
34
|
+
const prefix = options.name ? `CONSTRAINT ${options.name} ` : '';
|
|
35
|
+
const onDelete = options.onDelete ? ` ON DELETE ${options.onDelete}` : '';
|
|
36
|
+
this.constraints.push(`${prefix}FOREIGN KEY (${column}) REFERENCES ${referencesTable} (${referencesColumn})${onDelete}`);
|
|
37
|
+
}
|
|
38
|
+
index(columns, name) {
|
|
39
|
+
this.requireColumns(columns);
|
|
40
|
+
this.indexes.push({ columns: [...columns], name, unique: false });
|
|
41
|
+
}
|
|
42
|
+
uniqueIndex(columns, name) {
|
|
43
|
+
this.requireColumns(columns);
|
|
44
|
+
this.indexes.push({ columns: [...columns], name, unique: true });
|
|
45
|
+
}
|
|
46
|
+
getColumnDefinitions() {
|
|
47
|
+
return [
|
|
48
|
+
...this.columns.map(column => column.toString()),
|
|
49
|
+
...this.constraints,
|
|
50
|
+
].join(', ');
|
|
51
|
+
}
|
|
52
|
+
getIndexes() {
|
|
53
|
+
return this.indexes;
|
|
54
|
+
}
|
|
55
|
+
addColumn(name, type) {
|
|
56
|
+
if (this.columns.some(column => column.name === name)) {
|
|
57
|
+
throw new Error(`Column '${name}' is already defined`);
|
|
58
|
+
}
|
|
59
|
+
const column = new Column(name, type);
|
|
60
|
+
this.columns.push(column);
|
|
61
|
+
return column;
|
|
62
|
+
}
|
|
63
|
+
requireColumn(name) {
|
|
64
|
+
const column = this.columns.find(candidate => candidate.name === name);
|
|
65
|
+
if (!column)
|
|
66
|
+
throw new Error(`Unknown column '${name}'`);
|
|
67
|
+
return column;
|
|
68
|
+
}
|
|
69
|
+
requireColumns(columns) {
|
|
70
|
+
if (columns.length === 0)
|
|
71
|
+
throw new Error('At least one column is required');
|
|
72
|
+
columns.forEach(column => this.requireColumn(column));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=blueprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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"}
|
package/lib/column.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ColumnDefault = string | number | boolean | null;
|
|
2
|
+
export declare class Column {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly type: string;
|
|
5
|
+
private readonly modifiers;
|
|
6
|
+
constructor(name: string, type: string);
|
|
7
|
+
primaryKey(): this;
|
|
8
|
+
notNull(): this;
|
|
9
|
+
nullable(): this;
|
|
10
|
+
unique(): this;
|
|
11
|
+
default(value: ColumnDefault): this;
|
|
12
|
+
defaultRaw(expression: string): this;
|
|
13
|
+
references(table: string, column: string, onDelete?: string): this;
|
|
14
|
+
toString(): string;
|
|
15
|
+
private addModifier;
|
|
16
|
+
private serializeDefault;
|
|
17
|
+
}
|
package/lib/column.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export class Column {
|
|
2
|
+
name;
|
|
3
|
+
type;
|
|
4
|
+
modifiers = [];
|
|
5
|
+
constructor(name, type) {
|
|
6
|
+
this.name = name;
|
|
7
|
+
this.type = type;
|
|
8
|
+
}
|
|
9
|
+
primaryKey() {
|
|
10
|
+
return this.addModifier('PRIMARY KEY');
|
|
11
|
+
}
|
|
12
|
+
notNull() {
|
|
13
|
+
return this.addModifier('NOT NULL');
|
|
14
|
+
}
|
|
15
|
+
nullable() {
|
|
16
|
+
const index = this.modifiers.indexOf('NOT NULL');
|
|
17
|
+
if (index >= 0)
|
|
18
|
+
this.modifiers.splice(index, 1);
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
unique() {
|
|
22
|
+
return this.addModifier('UNIQUE');
|
|
23
|
+
}
|
|
24
|
+
default(value) {
|
|
25
|
+
return this.addModifier(`DEFAULT ${this.serializeDefault(value)}`);
|
|
26
|
+
}
|
|
27
|
+
defaultRaw(expression) {
|
|
28
|
+
if (!expression.trim())
|
|
29
|
+
throw new Error('Default expression cannot be empty');
|
|
30
|
+
return this.addModifier(`DEFAULT ${expression}`);
|
|
31
|
+
}
|
|
32
|
+
references(table, column, onDelete) {
|
|
33
|
+
let constraint = `REFERENCES ${table} (${column})`;
|
|
34
|
+
if (onDelete)
|
|
35
|
+
constraint += ` ON DELETE ${onDelete}`;
|
|
36
|
+
return this.addModifier(constraint);
|
|
37
|
+
}
|
|
38
|
+
toString() {
|
|
39
|
+
return `${this.name} ${this.type} ${this.modifiers.join(' ')}`.trim();
|
|
40
|
+
}
|
|
41
|
+
addModifier(modifier) {
|
|
42
|
+
if (!this.modifiers.includes(modifier))
|
|
43
|
+
this.modifiers.push(modifier);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
serializeDefault(value) {
|
|
47
|
+
if (value === null)
|
|
48
|
+
return 'NULL';
|
|
49
|
+
if (typeof value === 'boolean')
|
|
50
|
+
return value ? '1' : '0';
|
|
51
|
+
if (typeof value === 'number')
|
|
52
|
+
return String(value);
|
|
53
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=column.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column.js","sourceRoot":"","sources":["../src/column.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,MAAM;IAIC;IACA;IAJD,SAAS,GAAa,EAAE,CAAC;IAE1C,YACkB,IAAY,EACZ,IAAY;QADZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;IAC3B,CAAC;IAEJ,UAAU;QACR,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IACzC,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC;IAED,QAAQ;QACN,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,KAAK,IAAI,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,CAAC,KAAoB;QAC1B,OAAO,IAAI,CAAC,WAAW,CAAC,WAAW,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,UAAU,CAAC,UAAkB;QAC3B,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,WAAW,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,MAAc,EAAE,QAAiB;QACzD,IAAI,UAAU,GAAG,cAAc,KAAK,KAAK,MAAM,GAAG,CAAC;QACnD,IAAI,QAAQ;YAAE,UAAU,IAAI,cAAc,QAAQ,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC;IAED,QAAQ;QACN,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;IACxE,CAAC;IAEO,WAAW,CAAC,QAAgB;QAClC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,gBAAgB,CAAC,KAAoB;QAC3C,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC;QAClC,IAAI,OAAO,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;QACpD,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;IAC5C,CAAC;CACF"}
|
package/lib/consts.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MIGRATIONS_DIR = "migrations";
|
package/lib/consts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC"}
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import { MIGRATIONS_DIR } from './consts.js';
|
|
5
|
+
const migrationsDirectory = path.resolve(process.cwd(), MIGRATIONS_DIR);
|
|
6
|
+
const migrationsDirUrl = pathToFileURL(migrationsDirectory + path.sep);
|
|
7
|
+
export class MigrationLoader {
|
|
8
|
+
async load() {
|
|
9
|
+
const entries = await fs.readdir(migrationsDirectory, { withFileTypes: true });
|
|
10
|
+
const files = entries
|
|
11
|
+
.filter(e => e.isFile())
|
|
12
|
+
.map(e => e.name)
|
|
13
|
+
.filter(name => /\.(m?js|cjs)$/i.test(name));
|
|
14
|
+
return Promise.all(files.map(async (file) => {
|
|
15
|
+
const url = new URL(file, migrationsDirUrl.toString());
|
|
16
|
+
const mod = await import(url.href);
|
|
17
|
+
return mod.default;
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=migration-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-loader.js","sourceRoot":"","sources":["../src/migration-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,CAAC;AACxE,MAAM,gBAAgB,GAAG,aAAa,CAAC,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;AAEvE,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAC,IAAI;QACR,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAE/E,MAAM,KAAK,GAAG,OAAO;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACvB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAChB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAE/C,OAAO,OAAO,CAAC,GAAG,CAChB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAC,IAAI,EAAC,EAAE;YACrB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnC,OAAO,GAAG,CAAC,OAA6B,CAAC;QAC3C,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import type { IDatabase } from '@kurdel/db';
|
|
3
|
+
import { MigrationRegistry } from './migration-registry.js';
|
|
4
|
+
export declare class MigrationManager extends EventEmitter {
|
|
5
|
+
private connection;
|
|
6
|
+
private loader;
|
|
7
|
+
private registry;
|
|
8
|
+
constructor(connection: IDatabase, registry: MigrationRegistry);
|
|
9
|
+
static create(): Promise<MigrationManager>;
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
rollback(): Promise<void>;
|
|
12
|
+
refresh(): Promise<void>;
|
|
13
|
+
close(): Promise<void>;
|
|
14
|
+
private runMigrations;
|
|
15
|
+
private rollbackMigrations;
|
|
16
|
+
private findMigrationsToRun;
|
|
17
|
+
private findMigrationsToRollback;
|
|
18
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import { DBConnector } from '@kurdel/db';
|
|
3
|
+
import { MigrationLoader } from './migration-loader.js';
|
|
4
|
+
import { MigrationRegistry } from './migration-registry.js';
|
|
5
|
+
export class MigrationManager extends EventEmitter {
|
|
6
|
+
connection;
|
|
7
|
+
loader;
|
|
8
|
+
registry;
|
|
9
|
+
constructor(connection, registry) {
|
|
10
|
+
super();
|
|
11
|
+
this.connection = connection;
|
|
12
|
+
this.registry = registry;
|
|
13
|
+
this.loader = new MigrationLoader();
|
|
14
|
+
}
|
|
15
|
+
static async create() {
|
|
16
|
+
const connection = await new DBConnector().run();
|
|
17
|
+
const registry = await MigrationRegistry.create(connection);
|
|
18
|
+
return new MigrationManager(connection, registry);
|
|
19
|
+
}
|
|
20
|
+
async run() {
|
|
21
|
+
const migrations = await this.findMigrationsToRun();
|
|
22
|
+
if (migrations.length === 0) {
|
|
23
|
+
this.emit('up:nothing');
|
|
24
|
+
}
|
|
25
|
+
await this.runMigrations(migrations, await this.registry.next);
|
|
26
|
+
}
|
|
27
|
+
async rollback() {
|
|
28
|
+
const migrations = await this.findMigrationsToRollback(await this.registry.last);
|
|
29
|
+
if (migrations.length === 0) {
|
|
30
|
+
this.emit('down:nothing');
|
|
31
|
+
}
|
|
32
|
+
await this.rollbackMigrations(migrations);
|
|
33
|
+
}
|
|
34
|
+
async refresh() {
|
|
35
|
+
const migrationsToRollback = await this.findMigrationsToRollback();
|
|
36
|
+
if (migrationsToRollback.length === 0) {
|
|
37
|
+
this.emit('down:nothing');
|
|
38
|
+
}
|
|
39
|
+
const result = await this.rollbackMigrations(migrationsToRollback);
|
|
40
|
+
if (!result) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const migrationsToRun = await this.findMigrationsToRun();
|
|
44
|
+
if (migrationsToRun.length === 0) {
|
|
45
|
+
this.emit('up:nothing');
|
|
46
|
+
}
|
|
47
|
+
await this.runMigrations(migrationsToRun);
|
|
48
|
+
}
|
|
49
|
+
async close() {
|
|
50
|
+
this.connection.close();
|
|
51
|
+
}
|
|
52
|
+
async runMigrations(migrations, batch = 1) {
|
|
53
|
+
for (const migration of migrations) {
|
|
54
|
+
const { name } = migration.constructor;
|
|
55
|
+
try {
|
|
56
|
+
await migration.up();
|
|
57
|
+
await this.registry.add(name, batch);
|
|
58
|
+
this.emit('up:success', name);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
this.emit('up:failure', name, error);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
async rollbackMigrations(migrations) {
|
|
69
|
+
for (const migration of migrations) {
|
|
70
|
+
const { name } = migration.constructor;
|
|
71
|
+
try {
|
|
72
|
+
await migration.down();
|
|
73
|
+
await this.registry.remove(name);
|
|
74
|
+
this.emit('down:success', name);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
this.emit('down:failure', name, error);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
async findMigrationsToRun() {
|
|
85
|
+
const [appliedMigrations, importedMigrations] = await Promise.all([
|
|
86
|
+
this.registry.all,
|
|
87
|
+
this.loader.load(),
|
|
88
|
+
]);
|
|
89
|
+
return importedMigrations
|
|
90
|
+
.filter(MigrationClass => !appliedMigrations.includes(MigrationClass.name))
|
|
91
|
+
.map(MigrationClass => new MigrationClass(this.connection));
|
|
92
|
+
}
|
|
93
|
+
async findMigrationsToRollback(batch) {
|
|
94
|
+
const [appliedMigrations, importedMigrations] = await Promise.all([
|
|
95
|
+
batch ? this.registry.getBatch(batch) : this.registry.all,
|
|
96
|
+
this.loader.load(),
|
|
97
|
+
]);
|
|
98
|
+
importedMigrations.reverse();
|
|
99
|
+
return importedMigrations
|
|
100
|
+
.filter(MigrationClass => appliedMigrations.includes(MigrationClass.name))
|
|
101
|
+
.map(MigrationClass => new MigrationClass(this.connection));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=migration-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-manager.js","sourceRoot":"","sources":["../src/migration-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IDatabase } from '@kurdel/db';
|
|
2
|
+
export declare class MigrationRegistry {
|
|
3
|
+
private connection;
|
|
4
|
+
private builder;
|
|
5
|
+
constructor(connection: IDatabase);
|
|
6
|
+
static create(connection: IDatabase): Promise<MigrationRegistry>;
|
|
7
|
+
get all(): Promise<string[]>;
|
|
8
|
+
getBatch(batch: number): Promise<string[]>;
|
|
9
|
+
get last(): Promise<number>;
|
|
10
|
+
get next(): Promise<number>;
|
|
11
|
+
add(name: string, batch: number): Promise<void>;
|
|
12
|
+
remove(name: string): Promise<void>;
|
|
13
|
+
private get;
|
|
14
|
+
private getLastBatch;
|
|
15
|
+
private getNextBatch;
|
|
16
|
+
private existsMigrtionsTable;
|
|
17
|
+
private createMigrationsTable;
|
|
18
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { QueryBuilder } from '@kurdel/db';
|
|
2
|
+
import { Schema } from './schema.js';
|
|
3
|
+
export class MigrationRegistry {
|
|
4
|
+
connection;
|
|
5
|
+
builder;
|
|
6
|
+
constructor(connection) {
|
|
7
|
+
this.connection = connection;
|
|
8
|
+
this.builder = new QueryBuilder();
|
|
9
|
+
}
|
|
10
|
+
static async create(connection) {
|
|
11
|
+
const registry = new MigrationRegistry(connection);
|
|
12
|
+
const exists = await registry.existsMigrtionsTable();
|
|
13
|
+
if (!exists) {
|
|
14
|
+
await registry.createMigrationsTable();
|
|
15
|
+
}
|
|
16
|
+
return registry;
|
|
17
|
+
}
|
|
18
|
+
get all() {
|
|
19
|
+
const query = this.builder.select('*').from('migrations').build();
|
|
20
|
+
return this.get(query);
|
|
21
|
+
}
|
|
22
|
+
getBatch(batch) {
|
|
23
|
+
const query = this.builder.select('*').from('migrations').where('batch = ?', [batch]).build();
|
|
24
|
+
return this.get(query);
|
|
25
|
+
}
|
|
26
|
+
get last() {
|
|
27
|
+
return this.getLastBatch();
|
|
28
|
+
}
|
|
29
|
+
get next() {
|
|
30
|
+
return this.getNextBatch();
|
|
31
|
+
}
|
|
32
|
+
async add(name, batch) {
|
|
33
|
+
const query = this.builder.insert('migrations', { name, batch }).build();
|
|
34
|
+
await this.connection.run(query);
|
|
35
|
+
}
|
|
36
|
+
async remove(name) {
|
|
37
|
+
const query = this.builder.delete().from('migrations').where('name = ?', [name]).build();
|
|
38
|
+
await this.connection.run(query);
|
|
39
|
+
}
|
|
40
|
+
async get(query) {
|
|
41
|
+
const migrations = (await this.connection.all(query));
|
|
42
|
+
return migrations.map(migration => migration.name);
|
|
43
|
+
}
|
|
44
|
+
async getLastBatch() {
|
|
45
|
+
const maxBatchQuery = this.builder
|
|
46
|
+
.select('batch', { fn: 'MAX', as: 'maxBatch' })
|
|
47
|
+
.from('migrations')
|
|
48
|
+
.build();
|
|
49
|
+
const { maxBatch } = await this.connection.get(maxBatchQuery);
|
|
50
|
+
return maxBatch ?? 0;
|
|
51
|
+
}
|
|
52
|
+
async getNextBatch() {
|
|
53
|
+
return (await this.getLastBatch()) + 1;
|
|
54
|
+
}
|
|
55
|
+
async existsMigrtionsTable() {
|
|
56
|
+
const selectCountQuery = this.builder
|
|
57
|
+
.select('batch', { fn: 'COUNT' })
|
|
58
|
+
.from('migrations')
|
|
59
|
+
.build();
|
|
60
|
+
try {
|
|
61
|
+
await this.connection.get(selectCountQuery);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async createMigrationsTable() {
|
|
69
|
+
const schema = new Schema(this.connection);
|
|
70
|
+
await schema.create('migrations', table => {
|
|
71
|
+
table.integer('id');
|
|
72
|
+
table.primaryKey('id');
|
|
73
|
+
table.string('name');
|
|
74
|
+
table.integer('batch');
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=migration-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-registry.js","sourceRoot":"","sources":["../src/migration-registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,MAAM,OAAO,iBAAiB;IACpB,UAAU,CAAY;IACtB,OAAO,CAAe;IAE9B,YAAY,UAAqB;QAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IACpC,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAqB;QAC9C,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,oBAAoB,EAAE,CAAC;QACrD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,QAAQ,CAAC,qBAAqB,EAAE,CAAC;QACzC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAW,GAAG;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAEM,QAAQ,CAAC,KAAa;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAC9F,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAC7B,CAAC;IAED,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,KAAa;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACzE,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,IAAY;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QACzF,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAEO,KAAK,CAAC,GAAG,CAAC,KAAoB;QACpC,MAAM,UAAU,GAAG,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAsB,CAAC;QAC3E,OAAO,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO;aAC/B,MAAM,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC;aAC9C,IAAI,CAAC,YAAY,CAAC;aAClB,KAAK,EAAE,CAAC;QACX,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC9D,OAAO,QAAQ,IAAI,CAAC,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,oBAAoB;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO;aAClC,MAAM,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;aAChC,IAAI,CAAC,YAAY,CAAC;aAClB,KAAK,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3C,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE;YACxC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACpB,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACvB,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACrB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
package/lib/migration.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,OAAgB,SAAS;IACnB,MAAM,CAAS;IAEzB,YAAY,UAAqB;QAC/B,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;CAIF"}
|
package/lib/schema.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IDatabase } from '@kurdel/db';
|
|
2
|
+
import { Blueprint } from './blueprint.js';
|
|
3
|
+
type Configure = (table: Blueprint) => void;
|
|
4
|
+
export declare class Schema {
|
|
5
|
+
private readonly connection;
|
|
6
|
+
constructor(connection: IDatabase);
|
|
7
|
+
create(tableName: string, configure: Configure): Promise<void>;
|
|
8
|
+
drop(tableName: string): Promise<void>;
|
|
9
|
+
dropIfExists(tableName: string): Promise<void>;
|
|
10
|
+
private createIndex;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
package/lib/schema.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Blueprint } from './blueprint.js';
|
|
2
|
+
export class Schema {
|
|
3
|
+
connection;
|
|
4
|
+
constructor(connection) {
|
|
5
|
+
this.connection = connection;
|
|
6
|
+
}
|
|
7
|
+
async create(tableName, configure) {
|
|
8
|
+
const blueprint = new Blueprint();
|
|
9
|
+
configure(blueprint);
|
|
10
|
+
const definitions = blueprint.getColumnDefinitions();
|
|
11
|
+
if (!definitions)
|
|
12
|
+
throw new Error(`Table '${tableName}' must define at least one column`);
|
|
13
|
+
await this.connection.run({
|
|
14
|
+
sql: `CREATE TABLE ${tableName} (${definitions});`,
|
|
15
|
+
params: [],
|
|
16
|
+
});
|
|
17
|
+
for (const index of blueprint.getIndexes()) {
|
|
18
|
+
await this.createIndex(tableName, index);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async drop(tableName) {
|
|
22
|
+
await this.connection.run({ sql: `DROP TABLE ${tableName};`, params: [] });
|
|
23
|
+
}
|
|
24
|
+
async dropIfExists(tableName) {
|
|
25
|
+
await this.connection.run({ sql: `DROP TABLE IF EXISTS ${tableName};`, params: [] });
|
|
26
|
+
}
|
|
27
|
+
async createIndex(tableName, index) {
|
|
28
|
+
const name = index.name ?? `${tableName}_${index.columns.join('_')}_index`;
|
|
29
|
+
const unique = index.unique ? 'UNIQUE ' : '';
|
|
30
|
+
await this.connection.run({
|
|
31
|
+
sql: `CREATE ${unique}INDEX ${name} ON ${tableName} (${index.columns.join(', ')});`,
|
|
32
|
+
params: [],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAwB,MAAM,gBAAgB,CAAC;AAIjE,MAAM,OAAO,MAAM;IACY;IAA7B,YAA6B,UAAqB;QAArB,eAAU,GAAV,UAAU,CAAW;IAAG,CAAC;IAEtD,KAAK,CAAC,MAAM,CAAC,SAAiB,EAAE,SAAoB;QAClD,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC;QAClC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrB,MAAM,WAAW,GAAG,SAAS,CAAC,oBAAoB,EAAE,CAAC;QACrD,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,SAAS,mCAAmC,CAAC,CAAC;QAE1F,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACxB,GAAG,EAAE,gBAAgB,SAAS,KAAK,WAAW,IAAI;YAClD,MAAM,EAAE,EAAE;SACX,CAAC,CAAC;QAEH,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,UAAU,EAAE,EAAE,CAAC;YAC3C,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,SAAiB;QAC1B,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,cAAc,SAAS,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,wBAAwB,SAAS,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;IACvF,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,KAAsB;QACjE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACxB,GAAG,EAAE,UAAU,MAAM,SAAS,IAAI,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YACnF,MAAM,EAAE,EAAE;SACX,CAAC,CAAC;IACL,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kurdel/migrations",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "Database schema migration tools for Kurdel",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./lib/index.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "./lib/index.js",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"files": [ "lib" ],
|
|
11
|
+
"types": "./lib/index.d.ts",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"prepack": "npm run build",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"clean": "rimraf lib ../../.cache/tsconfig.migrations.build.tsbuildinfo",
|
|
17
|
+
"build": "tsc -p tsconfig.build.json",
|
|
18
|
+
"build:force": "npm run clean && tsc -p tsconfig.build.json --force"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"kurdel",
|
|
22
|
+
"database",
|
|
23
|
+
"migrations",
|
|
24
|
+
"schema"
|
|
25
|
+
],
|
|
26
|
+
"author": "Andrii Sorokin",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/ignorantic/kurdel.git",
|
|
34
|
+
"directory": "packages/migrations"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/ignorantic/kurdel/tree/main/packages/migrations#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/ignorantic/kurdel/issues"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"tag": "beta"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^20.9.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@kurdel/db": "0.1.0-beta.1"
|
|
49
|
+
}
|
|
50
|
+
}
|