@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/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
@@ -1,9 +1,5 @@
1
1
  import { Table } from "../../src"
2
2
 
3
- /**
4
- * Table using the new typed columns system.
5
- * Type is automatically inferred from the column definitions.
6
- */
7
3
  export default new Table({
8
4
  name: "d",
9
5
  priority: 0,
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src"
5
+ },
6
+ "include": ["src/**/*"],
7
+ "exclude": ["tests/**/*"]
8
+ }
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
  }