@ghom/orm 2.1.0 → 2.1.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/dist/app/migration.d.ts +91 -4
- package/dist/app/migration.js +31 -0
- package/dist/app/table.d.ts +19 -10
- package/dist/app/table.js +21 -10
- package/package.json +3 -3
- package/tests/orm.test.ts +118 -9
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +2 -2
package/dist/app/migration.d.ts
CHANGED
|
@@ -17,31 +17,98 @@ export interface TypedMigration<From = {}, To = {}> {
|
|
|
17
17
|
*/
|
|
18
18
|
apply: (builder: Knex.AlterTableBuilder) => void;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Represents a sequence of typed migrations.
|
|
22
|
+
* Used internally by migrate.sequence() to preserve individual migration types.
|
|
23
|
+
*
|
|
24
|
+
* @template Migrations - Tuple of migrations in the sequence
|
|
25
|
+
*/
|
|
26
|
+
export interface TypedMigrationSequence<Migrations extends TypedMigration<any, any>[]> {
|
|
27
|
+
/** @internal The individual migrations in the sequence */
|
|
28
|
+
readonly __migrations__: Migrations;
|
|
29
|
+
/** @internal Type marker for columns being removed (computed from sequence) */
|
|
30
|
+
readonly _from: SequenceFromType<Migrations>;
|
|
31
|
+
/** @internal Type marker for columns being added (computed from sequence) */
|
|
32
|
+
readonly _to: SequenceToType<Migrations>;
|
|
33
|
+
/**
|
|
34
|
+
* Apply all migrations in sequence to the table builder.
|
|
35
|
+
*/
|
|
36
|
+
apply: (builder: Knex.AlterTableBuilder) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Unwrap a migration array to get the individual migration types.
|
|
40
|
+
* If M is an array, extracts the element type; otherwise returns M as-is.
|
|
41
|
+
*/
|
|
42
|
+
type UnwrapMigrationArray<M> = M extends readonly (infer U)[] ? U : M;
|
|
43
|
+
/**
|
|
44
|
+
* Extract "From" keys from a single TypedMigration or TypedMigrationSequence.
|
|
45
|
+
* For sequences, excludes intermediate columns (those that are also in _to).
|
|
46
|
+
*/
|
|
47
|
+
type ExtractFromKeysSingle<M> = M extends {
|
|
48
|
+
__migrations__: infer Migrations;
|
|
49
|
+
} ? Migrations extends TypedMigration<any, any>[] ? Exclude<keyof UnionToIntersection<Migrations[number]["_from"]>, keyof UnionToIntersection<Migrations[number]["_to"]>> : never : M extends TypedMigration<infer From, any> ? keyof From : never;
|
|
20
50
|
/**
|
|
21
51
|
* Extract all "From" keys from a union of TypedMigration.
|
|
22
52
|
* These are the columns that will be removed/renamed.
|
|
53
|
+
* Handles both single migrations and arrays of migrations.
|
|
54
|
+
* Uses distributive conditional types to handle unions correctly.
|
|
23
55
|
*/
|
|
24
|
-
type ExtractFromKeys<M> = M extends
|
|
56
|
+
type ExtractFromKeys<M> = M extends any ? UnwrapMigrationArray<M> extends infer U ? U extends any ? ExtractFromKeysSingle<U> : never : never : never;
|
|
57
|
+
/**
|
|
58
|
+
* Extract "To" type from a single TypedMigration or TypedMigrationSequence.
|
|
59
|
+
* For sequences, excludes intermediate columns (those that are also in _from).
|
|
60
|
+
*/
|
|
61
|
+
type ExtractToTypesSingle<M> = M extends {
|
|
62
|
+
__migrations__: infer Migrations;
|
|
63
|
+
} ? Migrations extends TypedMigration<any, any>[] ? Omit<UnionToIntersection<Migrations[number]["_to"]>, keyof UnionToIntersection<Migrations[number]["_from"]>> : never : M extends TypedMigration<any, infer To> ? To : never;
|
|
25
64
|
/**
|
|
26
65
|
* Extract all "To" types from a union of TypedMigration and intersect them.
|
|
27
66
|
* These are the columns that will be added.
|
|
67
|
+
* Handles both single migrations and arrays of migrations.
|
|
68
|
+
* Uses distributive conditional types to handle unions correctly.
|
|
28
69
|
*/
|
|
29
|
-
type ExtractToTypes<M> = M extends
|
|
70
|
+
type ExtractToTypes<M> = M extends any ? UnwrapMigrationArray<M> extends infer U ? U extends any ? ExtractToTypesSingle<U> : never : never : never;
|
|
30
71
|
/**
|
|
31
72
|
* Convert a union to an intersection.
|
|
32
73
|
* Used to combine all "To" types from migrations.
|
|
33
74
|
*/
|
|
34
75
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
76
|
+
/**
|
|
77
|
+
* Force TypeScript to evaluate a type (expands type aliases).
|
|
78
|
+
*/
|
|
79
|
+
type Simplify<T> = {
|
|
80
|
+
[K in keyof T]: T[K];
|
|
81
|
+
} & {};
|
|
82
|
+
/**
|
|
83
|
+
* Combine all "from" types from a tuple of migrations.
|
|
84
|
+
*/
|
|
85
|
+
type CombineFromTypes<T extends TypedMigration<any, any>[]> = UnionToIntersection<T[number]["_from"]>;
|
|
86
|
+
/**
|
|
87
|
+
* Combine all "to" types from a tuple of migrations.
|
|
88
|
+
*/
|
|
89
|
+
type CombineToTypes<T extends TypedMigration<any, any>[]> = UnionToIntersection<T[number]["_to"]>;
|
|
90
|
+
/**
|
|
91
|
+
* Compute the final "from" type for a sequence of migrations.
|
|
92
|
+
* Excludes columns that are also added (intermediate renames).
|
|
93
|
+
*/
|
|
94
|
+
type SequenceFromType<T extends TypedMigration<any, any>[]> = Simplify<Omit<CombineFromTypes<T>, keyof CombineToTypes<T>>>;
|
|
95
|
+
/**
|
|
96
|
+
* Compute the final "to" type for a sequence of migrations.
|
|
97
|
+
* Excludes columns that are also removed (intermediate renames).
|
|
98
|
+
*/
|
|
99
|
+
type SequenceToType<T extends TypedMigration<any, any>[]> = Simplify<Omit<CombineToTypes<T>, keyof CombineFromTypes<T>>>;
|
|
35
100
|
/**
|
|
36
101
|
* Apply all migrations to compute the final type.
|
|
37
102
|
* 1. Remove all columns specified in migration "From" types
|
|
38
103
|
* 2. Add all columns specified in migration "To" types
|
|
104
|
+
* Handles both single migrations and arrays of migrations.
|
|
39
105
|
*/
|
|
40
|
-
export type ApplyMigrations<Base, Migrations extends Record<string,
|
|
106
|
+
export type ApplyMigrations<Base, Migrations extends Record<string, any>> = Migrations[keyof Migrations] extends infer M ? Omit<Base, ExtractFromKeys<M>> & UnionToIntersection<ExtractToTypes<M>> : Base;
|
|
41
107
|
/**
|
|
42
108
|
* Compute the final table type from base columns and migrations.
|
|
109
|
+
* Supports both single migrations and arrays of migrations.
|
|
43
110
|
*/
|
|
44
|
-
export type FinalTableType<Columns extends Record<string, ColumnDef<any, any>>, Migrations extends Record<string,
|
|
111
|
+
export type FinalTableType<Columns extends Record<string, ColumnDef<any, any>>, Migrations extends Record<string, any> = {}> = ApplyMigrations<InferColumns<Columns>, Migrations>;
|
|
45
112
|
/**
|
|
46
113
|
* Migration helpers for creating typed migrations.
|
|
47
114
|
* Each helper returns a TypedMigration with appropriate type transformations.
|
|
@@ -167,5 +234,25 @@ export declare const migrate: {
|
|
|
167
234
|
* })
|
|
168
235
|
*/
|
|
169
236
|
raw<From = {}, To = {}>(fn: (builder: Knex.AlterTableBuilder) => void): TypedMigration<From, To>;
|
|
237
|
+
/**
|
|
238
|
+
* Combine multiple migrations into a single migration.
|
|
239
|
+
* All migrations are applied sequentially within the same alter table call.
|
|
240
|
+
* Type information from all migrations is preserved and combined.
|
|
241
|
+
*
|
|
242
|
+
* Intermediate columns (added then removed in the sequence) are excluded from the final type.
|
|
243
|
+
* For example: renameColumn("a", "b") + renameColumn("b", "c") results in only "c" being added.
|
|
244
|
+
*
|
|
245
|
+
* @param migrations - The migrations to combine
|
|
246
|
+
* @returns A typed migration sequence combining all type transformations
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* migrate.sequence(
|
|
250
|
+
* migrate.addColumn("phone", col.string()),
|
|
251
|
+
* migrate.addColumn("address", col.string().nullable()),
|
|
252
|
+
* migrate.addIndex(["phone"]),
|
|
253
|
+
* )
|
|
254
|
+
* // Combines: removes nothing, adds { phone: string; address: string | null }
|
|
255
|
+
*/
|
|
256
|
+
sequence<T extends TypedMigration<any, any>[]>(...migrations: T): TypedMigrationSequence<T>;
|
|
170
257
|
};
|
|
171
258
|
export {};
|
package/dist/app/migration.js
CHANGED
|
@@ -195,4 +195,35 @@ export const migrate = {
|
|
|
195
195
|
apply: fn,
|
|
196
196
|
};
|
|
197
197
|
},
|
|
198
|
+
/**
|
|
199
|
+
* Combine multiple migrations into a single migration.
|
|
200
|
+
* All migrations are applied sequentially within the same alter table call.
|
|
201
|
+
* Type information from all migrations is preserved and combined.
|
|
202
|
+
*
|
|
203
|
+
* Intermediate columns (added then removed in the sequence) are excluded from the final type.
|
|
204
|
+
* For example: renameColumn("a", "b") + renameColumn("b", "c") results in only "c" being added.
|
|
205
|
+
*
|
|
206
|
+
* @param migrations - The migrations to combine
|
|
207
|
+
* @returns A typed migration sequence combining all type transformations
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* migrate.sequence(
|
|
211
|
+
* migrate.addColumn("phone", col.string()),
|
|
212
|
+
* migrate.addColumn("address", col.string().nullable()),
|
|
213
|
+
* migrate.addIndex(["phone"]),
|
|
214
|
+
* )
|
|
215
|
+
* // Combines: removes nothing, adds { phone: string; address: string | null }
|
|
216
|
+
*/
|
|
217
|
+
sequence(...migrations) {
|
|
218
|
+
return {
|
|
219
|
+
__migrations__: migrations,
|
|
220
|
+
_from: {},
|
|
221
|
+
_to: {},
|
|
222
|
+
apply: (builder) => {
|
|
223
|
+
for (const migration of migrations) {
|
|
224
|
+
migration.apply(builder);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
},
|
|
198
229
|
};
|
package/dist/app/table.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { CachedQuery } from "@ghom/query";
|
|
2
2
|
import type { Knex } from "knex";
|
|
3
3
|
import { type ColumnDef, type InferColumns } from "./column.js";
|
|
4
|
-
import type { FinalTableType, TypedMigration } from "./migration.js";
|
|
4
|
+
import type { FinalTableType, TypedMigration, TypedMigrationSequence } from "./migration.js";
|
|
5
5
|
import type { ORM } from "./orm.js";
|
|
6
6
|
/**
|
|
7
|
-
* A migration
|
|
7
|
+
* A migration value is a TypedMigration or TypedMigrationSequence.
|
|
8
|
+
* Use migrate.sequence() to combine multiple migrations.
|
|
8
9
|
*/
|
|
9
|
-
export type MigrationValue =
|
|
10
|
+
export type MigrationValue = TypedMigration<any, any> | TypedMigrationSequence<any>;
|
|
10
11
|
export interface MigrationData {
|
|
11
12
|
table: string;
|
|
12
13
|
version: string;
|
|
@@ -28,25 +29,32 @@ export interface TableOptions<Columns extends Record<string, ColumnDef<any, any>
|
|
|
28
29
|
*/
|
|
29
30
|
caching?: number;
|
|
30
31
|
/**
|
|
31
|
-
* Database migrations to apply.
|
|
32
|
+
* Database migrations to apply using typed migrations.
|
|
32
33
|
*
|
|
33
34
|
* Supports three key patterns:
|
|
34
35
|
* - **Numeric keys** (`"1"`, `"2"`): Sorted numerically
|
|
35
36
|
* - **Numeric-prefixed keys** (`"001_init"`, `"002_add"`): Sorted by prefix
|
|
36
37
|
* - **Pure string keys** (`"init"`, `"add"`): Uses insertion order
|
|
37
38
|
*
|
|
38
|
-
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Single migration
|
|
41
|
+
* migrations: {
|
|
42
|
+
* "001_add_email": migrate.addColumn("email", col.string()),
|
|
43
|
+
* }
|
|
39
44
|
*
|
|
40
45
|
* @example
|
|
41
|
-
* //
|
|
46
|
+
* // Multiple migrations in sequence
|
|
42
47
|
* migrations: {
|
|
43
|
-
* "
|
|
48
|
+
* "002_add_fields": migrate.sequence(
|
|
49
|
+
* migrate.addColumn("phone", col.string()),
|
|
50
|
+
* migrate.addColumn("address", col.string().nullable()),
|
|
51
|
+
* ),
|
|
44
52
|
* }
|
|
45
53
|
*
|
|
46
54
|
* @example
|
|
47
|
-
* //
|
|
55
|
+
* // Raw migration for advanced use cases
|
|
48
56
|
* migrations: {
|
|
49
|
-
* "
|
|
57
|
+
* "003_custom": migrate.raw((builder) => builder.dropColumn("oldField")),
|
|
50
58
|
* }
|
|
51
59
|
*/
|
|
52
60
|
migrations?: Migrations;
|
|
@@ -104,8 +112,9 @@ export declare class Table<Columns extends Record<string, ColumnDef<any, any>> =
|
|
|
104
112
|
/**
|
|
105
113
|
* The inferred TypeScript type for rows of this table.
|
|
106
114
|
* Includes base columns and all migration type transforms.
|
|
115
|
+
* Supports both single migrations and arrays of migrations.
|
|
107
116
|
*/
|
|
108
|
-
readonly $type:
|
|
117
|
+
readonly $type: FinalTableType<Columns, Migrations>;
|
|
109
118
|
private requireOrm;
|
|
110
119
|
get client(): Knex;
|
|
111
120
|
get query(): Knex.QueryBuilder<InferColumns<Columns>, {
|
package/dist/app/table.js
CHANGED
|
@@ -151,8 +151,26 @@ export class Table {
|
|
|
151
151
|
const allPureString = keys.every((k) => !/^\d/.test(k));
|
|
152
152
|
// Validate: no mixing allowed
|
|
153
153
|
if (!allPureNumeric && !allNumericPrefix && !allPureString) {
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
// Categorize keys for helpful error message
|
|
155
|
+
const numericKeys = keys.filter((k) => /^\d+$/.test(k));
|
|
156
|
+
const prefixedKeys = keys.filter((k) => /^\d+[_\-a-zA-Z]/.test(k));
|
|
157
|
+
const stringKeys = keys.filter((k) => !/^\d/.test(k));
|
|
158
|
+
const parts = [];
|
|
159
|
+
if (numericKeys.length > 0) {
|
|
160
|
+
parts.push(`numeric keys: ${numericKeys.map((k) => `"${k}"`).join(", ")}`);
|
|
161
|
+
}
|
|
162
|
+
if (prefixedKeys.length > 0) {
|
|
163
|
+
parts.push(`prefixed keys: ${prefixedKeys.map((k) => `"${k}"`).join(", ")}`);
|
|
164
|
+
}
|
|
165
|
+
if (stringKeys.length > 0) {
|
|
166
|
+
parts.push(`string keys: ${stringKeys.map((k) => `"${k}"`).join(", ")}`);
|
|
167
|
+
}
|
|
168
|
+
throw new Error(`Table "${this.options.name}": Migration keys use mixed patterns which prevents reliable ordering.\n\n` +
|
|
169
|
+
`Found: ${parts.join(" AND ")}\n\n` +
|
|
170
|
+
`Choose ONE pattern for all keys:\n` +
|
|
171
|
+
` - Pure numbers: "1", "2", "10" (sorted numerically)\n` +
|
|
172
|
+
` - Prefixed strings: "001_init", "002_add" (sorted by prefix)\n` +
|
|
173
|
+
` - Pure strings: "init", "add_email" (insertion order)`);
|
|
156
174
|
}
|
|
157
175
|
if (allPureNumeric) {
|
|
158
176
|
// Sort purely numeric keys numerically: 1, 2, 10, 20
|
|
@@ -217,14 +235,7 @@ export class Table {
|
|
|
217
235
|
}
|
|
218
236
|
const migration = migrations[key];
|
|
219
237
|
await this.client.schema.alterTable(this.options.name, (builder) => {
|
|
220
|
-
|
|
221
|
-
// Callback function migration
|
|
222
|
-
migration(builder);
|
|
223
|
-
}
|
|
224
|
-
else if (migration && typeof migration === "object" && "apply" in migration) {
|
|
225
|
-
// TypedMigration object
|
|
226
|
-
migration.apply(builder);
|
|
227
|
-
}
|
|
238
|
+
migration.apply(builder);
|
|
228
239
|
});
|
|
229
240
|
data.version = key;
|
|
230
241
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghom/orm",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"homepage": "https://github.com/GhomKrosmonaute/orm",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"format": "biome format --write .",
|
|
12
|
-
"check": "biome check --write .",
|
|
13
|
-
"build": "rimraf dist && tsc",
|
|
12
|
+
"check": "biome check --write . && tsc --noEmit",
|
|
13
|
+
"build": "rimraf dist && tsc -p tsconfig.build.json",
|
|
14
14
|
"test": "bun test",
|
|
15
15
|
"prepublishOnly": "npm run check && npm run build && npm test"
|
|
16
16
|
},
|
package/tests/orm.test.ts
CHANGED
|
@@ -141,6 +141,94 @@ describe("typed migrations", () => {
|
|
|
141
141
|
age: null,
|
|
142
142
|
}
|
|
143
143
|
})
|
|
144
|
+
|
|
145
|
+
test("Table accepts migrate.sequence for multiple typed migrations", () => {
|
|
146
|
+
const userTable = new Table({
|
|
147
|
+
name: "test_sequence_migrations",
|
|
148
|
+
columns: (col) => ({
|
|
149
|
+
id: col.increments(),
|
|
150
|
+
name: col.string(),
|
|
151
|
+
}),
|
|
152
|
+
migrations: {
|
|
153
|
+
"001_multiple_changes": migrate.sequence(
|
|
154
|
+
migrate.addColumn("phone", col.string()),
|
|
155
|
+
migrate.addColumn("address", col.string().nullable()),
|
|
156
|
+
migrate.addIndex(["phone"], "idx_phone"),
|
|
157
|
+
migrate.renameColumn("name", "username"),
|
|
158
|
+
migrate.renameColumn("username", "fullname"),
|
|
159
|
+
),
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
expect(userTable).toBeInstanceOf(Table)
|
|
164
|
+
expect(userTable.options.migrations).toBeDefined()
|
|
165
|
+
expect(Object.keys(userTable.options.migrations!).length).toBe(1)
|
|
166
|
+
|
|
167
|
+
// Type inference check - sequence migrations should infer types correctly
|
|
168
|
+
type ExpectedType = typeof userTable.$type
|
|
169
|
+
const _typeCheck: ExpectedType = {
|
|
170
|
+
id: 1,
|
|
171
|
+
// @ts-expect-error - name is removed by renameColumn
|
|
172
|
+
name: "test",
|
|
173
|
+
// @ts-expect-error - username is removed by renameColumn
|
|
174
|
+
username: "test",
|
|
175
|
+
fullname: "test",
|
|
176
|
+
phone: "123456789",
|
|
177
|
+
address: null,
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test("Table accepts mixed single and sequence migrations", () => {
|
|
182
|
+
const userTable = new Table({
|
|
183
|
+
name: "test_mixed_migrations",
|
|
184
|
+
columns: (col) => ({
|
|
185
|
+
id: col.increments(),
|
|
186
|
+
name: col.string(),
|
|
187
|
+
}),
|
|
188
|
+
migrations: {
|
|
189
|
+
"001_add_email": migrate.addColumn("email", col.string()),
|
|
190
|
+
"002_multiple_changes": migrate.sequence(
|
|
191
|
+
migrate.addColumn("phone", col.string()),
|
|
192
|
+
migrate.addColumn("age", col.integer().nullable()),
|
|
193
|
+
),
|
|
194
|
+
"003_add_active": migrate.addColumn("isActive", col.boolean().defaultTo(true)),
|
|
195
|
+
},
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
expect(userTable).toBeInstanceOf(Table)
|
|
199
|
+
expect(userTable.options.migrations).toBeDefined()
|
|
200
|
+
expect(Object.keys(userTable.options.migrations!).length).toBe(3)
|
|
201
|
+
|
|
202
|
+
// Type inference check - mixed migrations should infer all types
|
|
203
|
+
type ExpectedType = typeof userTable.$type
|
|
204
|
+
const _typeCheck: ExpectedType = {
|
|
205
|
+
id: 1,
|
|
206
|
+
name: "test",
|
|
207
|
+
email: "test@example.com",
|
|
208
|
+
phone: "123456789",
|
|
209
|
+
age: null,
|
|
210
|
+
isActive: true,
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
test("Table accepts migrate.sequence with raw migrations", () => {
|
|
215
|
+
const table = new Table({
|
|
216
|
+
name: "test_sequence_raw_migrations",
|
|
217
|
+
columns: (col) => ({
|
|
218
|
+
id: col.increments(),
|
|
219
|
+
}),
|
|
220
|
+
migrations: {
|
|
221
|
+
"001_multiple_raw": migrate.sequence(
|
|
222
|
+
migrate.raw((builder) => builder.string("field1")),
|
|
223
|
+
migrate.raw((builder) => builder.integer("field2")),
|
|
224
|
+
),
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
expect(table).toBeInstanceOf(Table)
|
|
229
|
+
expect(table.options.migrations).toBeDefined()
|
|
230
|
+
expect(table.options.migrations!["001_multiple_raw"]).toBeDefined()
|
|
231
|
+
})
|
|
144
232
|
})
|
|
145
233
|
|
|
146
234
|
describe("migration key patterns", () => {
|
|
@@ -151,9 +239,9 @@ describe("migration key patterns", () => {
|
|
|
151
239
|
id: col.increments(),
|
|
152
240
|
}),
|
|
153
241
|
migrations: {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
242
|
+
1: migrate.raw(() => {}),
|
|
243
|
+
2: migrate.raw(() => {}),
|
|
244
|
+
10: migrate.raw(() => {}),
|
|
157
245
|
},
|
|
158
246
|
})
|
|
159
247
|
|
|
@@ -168,9 +256,9 @@ describe("migration key patterns", () => {
|
|
|
168
256
|
id: col.increments(),
|
|
169
257
|
}),
|
|
170
258
|
migrations: {
|
|
171
|
-
"001_init": (
|
|
172
|
-
"002_add_column": (
|
|
173
|
-
"010_fix": (
|
|
259
|
+
"001_init": migrate.raw(() => {}),
|
|
260
|
+
"002_add_column": migrate.raw(() => {}),
|
|
261
|
+
"010_fix": migrate.raw(() => {}),
|
|
174
262
|
},
|
|
175
263
|
})
|
|
176
264
|
|
|
@@ -189,15 +277,36 @@ describe("migration key patterns", () => {
|
|
|
189
277
|
id: col.increments(),
|
|
190
278
|
}),
|
|
191
279
|
migrations: {
|
|
192
|
-
init: (
|
|
193
|
-
add_column: (
|
|
194
|
-
fix: (
|
|
280
|
+
init: migrate.raw(() => {}),
|
|
281
|
+
add_column: migrate.raw(() => {}),
|
|
282
|
+
fix: migrate.raw(() => {}),
|
|
195
283
|
},
|
|
196
284
|
})
|
|
197
285
|
|
|
198
286
|
expect(table.options.migrations).toBeDefined()
|
|
199
287
|
expect(Object.keys(table.options.migrations!)).toEqual(["init", "add_column", "fix"])
|
|
200
288
|
})
|
|
289
|
+
|
|
290
|
+
test("Table rejects mixed key patterns at migration time", () => {
|
|
291
|
+
const table = new Table({
|
|
292
|
+
name: "test_mixed_keys",
|
|
293
|
+
columns: (col) => ({
|
|
294
|
+
id: col.increments(),
|
|
295
|
+
}),
|
|
296
|
+
migrations: {
|
|
297
|
+
1: migrate.raw(() => {}),
|
|
298
|
+
"001_init": migrate.raw(() => {}),
|
|
299
|
+
init: migrate.raw(() => {}),
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
// The error is thrown when getMigrationKeys() is called during migration
|
|
304
|
+
// This happens during make(), not during construction
|
|
305
|
+
expect(() => {
|
|
306
|
+
// Access private method to test key validation
|
|
307
|
+
;(table as any).getMigrationKeys()
|
|
308
|
+
}).toThrow(/Migration keys use mixed patterns/)
|
|
309
|
+
})
|
|
201
310
|
})
|
|
202
311
|
|
|
203
312
|
describe("unconnected ORM", () => {
|
package/tsconfig.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"strict": true,
|
|
4
|
-
"rootDir": "src",
|
|
5
4
|
"outDir": "dist",
|
|
6
5
|
"module": "NodeNext",
|
|
7
6
|
"target": "ESNext",
|
|
@@ -12,5 +11,6 @@
|
|
|
12
11
|
"skipLibCheck": true,
|
|
13
12
|
"typeRoots": ["./node_modules/@types", "./dist/typings"]
|
|
14
13
|
},
|
|
15
|
-
"include": ["src/**/*", "dist/typings/**/*"]
|
|
14
|
+
"include": ["src/**/*", "dist/typings/**/*"],
|
|
15
|
+
"exclude": ["tests/**/*"]
|
|
16
16
|
}
|