@ghom/orm 2.0.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/biome.json +7 -0
- package/dist/app/migration.d.ts +258 -0
- package/dist/app/migration.js +229 -0
- package/dist/app/orm.d.ts +21 -1
- package/dist/app/orm.js +51 -1
- package/dist/app/table.d.ts +84 -15
- package/dist/app/table.js +107 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +4 -5
- package/readme.md +91 -19
- package/tests/orm.test.ts +253 -1
- package/tests/tables/d.ts +0 -4
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +2 -2
package/tests/orm.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "fs"
|
|
|
3
3
|
import path from "path"
|
|
4
4
|
import { rimraf } from "rimraf"
|
|
5
5
|
|
|
6
|
-
import { col, ORM, type ORMConfig, Table } from "../src"
|
|
6
|
+
import { col, migrate, ORM, type ORMConfig, Table } from "../src"
|
|
7
7
|
|
|
8
8
|
import a from "./tables/a"
|
|
9
9
|
import b from "./tables/b"
|
|
@@ -57,6 +57,258 @@ describe("typed columns", () => {
|
|
|
57
57
|
})
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
+
describe("typed migrations", () => {
|
|
61
|
+
test("migrate.addColumn creates TypedMigration", () => {
|
|
62
|
+
const migration = migrate.addColumn("email", col.string())
|
|
63
|
+
|
|
64
|
+
expect(migration).toBeDefined()
|
|
65
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
66
|
+
expect("_from" in migration).toBe(true)
|
|
67
|
+
expect("_to" in migration).toBe(true)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test("migrate.dropColumn creates TypedMigration", () => {
|
|
71
|
+
const migration = migrate.dropColumn("oldField")
|
|
72
|
+
|
|
73
|
+
expect(migration).toBeDefined()
|
|
74
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test("migrate.renameColumn creates TypedMigration", () => {
|
|
78
|
+
const migration = migrate.renameColumn("name", "username")
|
|
79
|
+
|
|
80
|
+
expect(migration).toBeDefined()
|
|
81
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("migrate.alterColumn creates TypedMigration", () => {
|
|
85
|
+
const migration = migrate.alterColumn("age", col.integer().nullable())
|
|
86
|
+
|
|
87
|
+
expect(migration).toBeDefined()
|
|
88
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test("migrate.addIndex creates TypedMigration", () => {
|
|
92
|
+
const migration = migrate.addIndex(["email"], "idx_email")
|
|
93
|
+
|
|
94
|
+
expect(migration).toBeDefined()
|
|
95
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test("migrate.addUnique creates TypedMigration", () => {
|
|
99
|
+
const migration = migrate.addUnique(["email"], "uniq_email")
|
|
100
|
+
|
|
101
|
+
expect(migration).toBeDefined()
|
|
102
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("migrate.raw creates TypedMigration", () => {
|
|
106
|
+
const migration = migrate.raw((builder) => {
|
|
107
|
+
builder.dropColumn("temp")
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
expect(migration).toBeDefined()
|
|
111
|
+
expect(migration.apply).toBeInstanceOf(Function)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test("Table with typed migrations has correct options", () => {
|
|
115
|
+
const userTable = new Table({
|
|
116
|
+
name: "test_typed_migrations",
|
|
117
|
+
columns: (col) => ({
|
|
118
|
+
id: col.increments(),
|
|
119
|
+
name: col.string(),
|
|
120
|
+
}),
|
|
121
|
+
migrations: {
|
|
122
|
+
"001_add_email": migrate.addColumn("email", col.string()),
|
|
123
|
+
"002_add_age": migrate.addColumn("age", col.integer().nullable()),
|
|
124
|
+
"003_rename_name": migrate.renameColumn("name", "username"),
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
expect(userTable).toBeInstanceOf(Table)
|
|
129
|
+
expect(userTable.options.migrations).toBeDefined()
|
|
130
|
+
expect(Object.keys(userTable.options.migrations!).length).toBe(3)
|
|
131
|
+
|
|
132
|
+
// Type inference check - final type includes base columns + migrations
|
|
133
|
+
// "name" is removed by renameColumn, "username" is added
|
|
134
|
+
type ExpectedType = typeof userTable.$type
|
|
135
|
+
const _typeCheck: ExpectedType = {
|
|
136
|
+
id: 1,
|
|
137
|
+
username: "test", // renamed from "name"
|
|
138
|
+
// @ts-expect-error - name is removed by renameColumn
|
|
139
|
+
name: "test",
|
|
140
|
+
email: "test@example.com",
|
|
141
|
+
age: null,
|
|
142
|
+
}
|
|
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
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe("migration key patterns", () => {
|
|
235
|
+
test("Table accepts pure numeric keys", () => {
|
|
236
|
+
const table = new Table({
|
|
237
|
+
name: "test_numeric_keys",
|
|
238
|
+
columns: (col) => ({
|
|
239
|
+
id: col.increments(),
|
|
240
|
+
}),
|
|
241
|
+
migrations: {
|
|
242
|
+
1: migrate.raw(() => {}),
|
|
243
|
+
2: migrate.raw(() => {}),
|
|
244
|
+
10: migrate.raw(() => {}),
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
expect(table.options.migrations).toBeDefined()
|
|
249
|
+
expect(Object.keys(table.options.migrations!)).toEqual(["1", "2", "10"])
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
test("Table accepts numeric-prefixed keys", () => {
|
|
253
|
+
const table = new Table({
|
|
254
|
+
name: "test_prefixed_keys",
|
|
255
|
+
columns: (col) => ({
|
|
256
|
+
id: col.increments(),
|
|
257
|
+
}),
|
|
258
|
+
migrations: {
|
|
259
|
+
"001_init": migrate.raw(() => {}),
|
|
260
|
+
"002_add_column": migrate.raw(() => {}),
|
|
261
|
+
"010_fix": migrate.raw(() => {}),
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
expect(table.options.migrations).toBeDefined()
|
|
266
|
+
expect(Object.keys(table.options.migrations!)).toEqual([
|
|
267
|
+
"001_init",
|
|
268
|
+
"002_add_column",
|
|
269
|
+
"010_fix",
|
|
270
|
+
])
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test("Table accepts pure string keys", () => {
|
|
274
|
+
const table = new Table({
|
|
275
|
+
name: "test_string_keys",
|
|
276
|
+
columns: (col) => ({
|
|
277
|
+
id: col.increments(),
|
|
278
|
+
}),
|
|
279
|
+
migrations: {
|
|
280
|
+
init: migrate.raw(() => {}),
|
|
281
|
+
add_column: migrate.raw(() => {}),
|
|
282
|
+
fix: migrate.raw(() => {}),
|
|
283
|
+
},
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
expect(table.options.migrations).toBeDefined()
|
|
287
|
+
expect(Object.keys(table.options.migrations!)).toEqual(["init", "add_column", "fix"])
|
|
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
|
+
})
|
|
310
|
+
})
|
|
311
|
+
|
|
60
312
|
describe("unconnected ORM", () => {
|
|
61
313
|
test("can be initialized with false", () => {
|
|
62
314
|
const unconnectedOrm = new ORM(false)
|
package/tests/tables/d.ts
CHANGED
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
|
}
|