millas 0.2.20 → 0.2.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "millas",
3
- "version": "0.2.20",
3
+ "version": "0.2.23",
4
4
  "description": "A modern batteries-included backend framework for Node.js — built on Express, inspired by Laravel, Django, and FastAPI",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -145,7 +145,7 @@ class QueryEngine {
145
145
  // ── Execute data + count in parallel ──────────────────────────────────
146
146
  const [rows, countResult] = await Promise.all([
147
147
  q.clone().limit(limit).offset(offset),
148
- q.clone().count('* as count').first(),
148
+ q.clone().clearOrder().clearSelect().count('* as count').first(),
149
149
  ]);
150
150
 
151
151
  const total = Number(countResult?.count ?? 0);
@@ -231,12 +231,12 @@ class QueryEngine {
231
231
  case 'in': return q.whereIn(col, Array.isArray(value) ? value : [value]);
232
232
  case 'notin': return q.whereNotIn(col, Array.isArray(value) ? value : [value]);
233
233
  case 'between': return q.whereBetween(col, Array.isArray(value) ? value : [value, value]);
234
- case 'contains':
235
- case 'icontains': return q.where(col, 'like', `%${value}%`);
236
- case 'startswith':
237
- case 'istartswith': return q.where(col, 'like', `${value}%`);
238
- case 'endswith':
239
- case 'iendswith': return q.where(col, 'like', `%${value}`);
234
+ case 'contains': return q.where(col, 'like', `%${value}%`);
235
+ case 'icontains': return q.where(q.client?.config?.client?.includes('pg') ? col : col, q.client?.config?.client?.includes('pg') ? 'ilike' : 'like', `%${value}%`);
236
+ case 'startswith': return q.where(col, 'like', `${value}%`);
237
+ case 'istartswith': return q.where(col, q.client?.config?.client?.includes('pg') ? 'ilike' : 'like', `${value}%`);
238
+ case 'endswith': return q.where(col, 'like', `%${value}`);
239
+ case 'iendswith': return q.where(col, q.client?.config?.client?.includes('pg') ? 'ilike' : 'like', `%${value}`);
240
240
  default: return q.where(key, value);
241
241
  }
242
242
  }
@@ -246,15 +246,19 @@ class QueryEngine {
246
246
  * Uses strftime for SQLite/MySQL; falls back gracefully on PostgreSQL.
247
247
  */
248
248
  _applyDateHierarchy(q, col, year, month) {
249
+ const client = q.client?.config?.client || 'sqlite3';
250
+ const isPg = client.includes('pg') || client.includes('postgres');
251
+ const isMy = client.includes('mysql') || client.includes('maria');
252
+
249
253
  if (year) {
250
- try {
251
- q = q.whereRaw(`strftime('%Y', \`${col}\`) = ?`, [String(year)]);
252
- } catch { /* PG fallback — skip */ }
254
+ if (isPg) q = q.whereRaw(`EXTRACT(YEAR FROM "${col}") = ?`, [Number(year)]);
255
+ else if (isMy) q = q.whereRaw(`YEAR(\`${col}\`) = ?`, [Number(year)]);
256
+ else q = q.whereRaw(`strftime('%Y', \`${col}\`) = ?`, [String(year)]);
253
257
  }
254
258
  if (month) {
255
- try {
256
- q = q.whereRaw(`strftime('%m', \`${col}\`) = ?`, [String(month).padStart(2, '0')]);
257
- } catch { /* PG fallback — skip */ }
259
+ if (isPg) q = q.whereRaw(`EXTRACT(MONTH FROM "${col}") = ?`, [Number(month)]);
260
+ else if (isMy) q = q.whereRaw(`MONTH(\`${col}\`) = ?`, [Number(month)]);
261
+ else q = q.whereRaw(`strftime('%m', \`${col}\`) = ?`, [String(month).padStart(2, '0')]);
258
262
  }
259
263
  return q;
260
264
  }
package/src/cli.js CHANGED
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ // Load .env before anything else so all commands have access to env vars
4
+ require('dotenv').config();
5
+
3
6
  const { Command } = require('commander');
4
7
  const chalk = require('chalk');
5
8
  const program = new Command();
package/src/core/db.js CHANGED
@@ -1,9 +1,10 @@
1
- const {
2
- Model,
3
- fields
4
- } = require("../orm");
5
- const { migrations } = require("../orm/migration/operations");
1
+ const { Model, fields } = require('../orm');
2
+ const { migrations } = require('../orm/migration/operations');
3
+ const F = require('../orm/query/F');
4
+ const Q = require('../orm/query/Q');
5
+ const HasMany = require('../orm/relations/HasMany');
6
+ const BelongsTo = require('../orm/relations/BelongsTo');
7
+ const HasOne = require('../orm/relations/HasOne');
8
+ const BelongsToMany = require('../orm/relations/BelongsToMany');
6
9
 
7
- module.exports = {
8
- Model, fields,migrations
9
- }
10
+ module.exports = { Model, fields, migrations, F, Q, HasMany, BelongsTo, HasOne, BelongsToMany };
@@ -81,7 +81,18 @@ class EventEmitter {
81
81
  async _invoke(handler, event) {
82
82
  // Listener class (has handle() on prototype)
83
83
  if (typeof handler === 'function' && typeof handler.prototype?.handle === 'function') {
84
- const inst = new handler();
84
+ // Resolve static inject dependencies from the container
85
+ let inst;
86
+ if (handler.inject && Array.isArray(handler.inject) && handler.inject.length) {
87
+ const Facade = require('../facades/Facade');
88
+ const container = Facade._container;
89
+ const deps = handler.inject.map(key => {
90
+ try { return container ? container.make(key) : undefined; } catch { return undefined; }
91
+ });
92
+ inst = new handler(...deps);
93
+ } else {
94
+ inst = new handler();
95
+ }
85
96
  if (handler.queue && this._queue) {
86
97
  const Job = require('../queue/Job');
87
98
  const q = this._queue;
@@ -1,43 +1,64 @@
1
1
  'use strict';
2
2
 
3
+ const Facade = require('./Facade');
4
+
3
5
  /**
4
- * millas/facades/Database
6
+ * Database facade — direct access to the knex connection.
7
+ *
8
+ * Usage:
9
+ * const Database = require('millas/facades/Database');
5
10
  *
6
- * ORM models, field definitions, and query builder.
11
+ * // Raw SQL
12
+ * const result = await Database.raw('SELECT NOW()');
7
13
  *
8
- * const { Model, fields } = require('millas/facades/Database');
14
+ * // Knex query builder
15
+ * const rows = await Database.table('posts').where('published', true).select('*');
9
16
  *
10
- * class Post extends Model {
11
- * static table = 'posts';
12
- * static fields = {
13
- * id: fields.id(),
14
- * title: fields.string({ max: 255 }),
15
- * author: fields.ForeignKey('User', { relatedName: 'posts' }),
16
- * published: fields.boolean({ default: false }),
17
- * created_at: fields.timestamp(),
18
- * updated_at: fields.timestamp(),
19
- * };
20
- * }
17
+ * // Named connection
18
+ * const rows = await Database.connection('replica').raw('SELECT 1');
21
19
  */
20
+ class Database {
21
+ static _resolveInstance() {
22
+ const DatabaseManager = require('../orm/drivers/DatabaseManager');
23
+ return DatabaseManager.connection();
24
+ }
25
+ }
26
+
27
+ // Proxy every static call to the knex connection
28
+ module.exports = new Proxy(Database, {
29
+ get(target, prop) {
30
+ // Let real static members through
31
+ if (prop in target || prop === 'then' || prop === 'catch') {
32
+ return target[prop];
33
+ }
34
+ if (typeof prop === 'symbol') return target[prop];
35
+
36
+ // Special case: raw() — normalize result across dialects
37
+ if (prop === 'raw') {
38
+ return async (sql, bindings) => {
39
+ const db = Database._resolveInstance();
40
+ const result = await db.raw(sql, bindings);
41
+ // Postgres returns { rows: [...], command, rowCount, ... }
42
+ // SQLite/MySQL return [rows, fields] or just rows
43
+ if (result && result.rows) return result.rows;
44
+ if (Array.isArray(result)) return result[0] ?? result;
45
+ return result;
46
+ };
47
+ }
22
48
 
23
- const {
24
- Model,
25
- fields,
26
- QueryBuilder,
27
- DatabaseManager,
28
- SchemaBuilder,
29
- MigrationRunner,
30
- ModelInspector,
31
- DatabaseServiceProvider,
32
- } = require('../core');
49
+ // Special case: connection(name) returns a named knex instance
50
+ if (prop === 'connection') {
51
+ return (name) => {
52
+ const DatabaseManager = require('../orm/drivers/DatabaseManager');
53
+ return DatabaseManager.connection(name || null);
54
+ };
55
+ }
33
56
 
34
- module.exports = {
35
- Model,
36
- fields,
37
- QueryBuilder,
38
- DatabaseManager,
39
- SchemaBuilder,
40
- MigrationRunner,
41
- ModelInspector,
42
- DatabaseServiceProvider,
43
- };
57
+ // Proxy everything else to the default knex connection
58
+ return (...args) => {
59
+ const db = Database._resolveInstance();
60
+ if (typeof db[prop] !== 'function') return db[prop];
61
+ return db[prop](...args);
62
+ };
63
+ },
64
+ });
@@ -99,6 +99,12 @@ class DatabaseManager {
99
99
  });
100
100
 
101
101
  case 'mysql':
102
+ try { require('mysql2'); } catch {
103
+ throw new Error(
104
+ 'MySQL driver not installed.\n' +
105
+ 'Run: npm install mysql2'
106
+ );
107
+ }
102
108
  return knex({
103
109
  client: 'mysql2',
104
110
  connection: {
@@ -112,6 +118,12 @@ class DatabaseManager {
112
118
  });
113
119
 
114
120
  case 'postgres':
121
+ try { require('pg'); } catch {
122
+ throw new Error(
123
+ 'PostgreSQL driver not installed.\n' +
124
+ 'Run: npm install pg'
125
+ );
126
+ }
115
127
  return knex({
116
128
  client: 'pg',
117
129
  connection: {
@@ -101,6 +101,24 @@ const fields = {
101
101
  return new FieldDefinition('json', options);
102
102
  },
103
103
 
104
+ /**
105
+ * Array field — stores an ordered list of values.
106
+ *
107
+ * On PostgreSQL: uses native ARRAY type (text[], integer[], etc.)
108
+ * On SQLite/MySQL: falls back to JSON column (same as fields.json())
109
+ *
110
+ * @param {string} [of='text'] — item type: 'text' | 'integer' | 'float' | 'boolean'
111
+ *
112
+ * @example
113
+ * tags: fields.array() // text[] on PG, json on SQLite
114
+ * scores: fields.array('integer') // integer[] on PG
115
+ * media_ids: fields.array('integer', { nullable: true, default: [] })
116
+ */
117
+ array(of = 'text', options = {}) {
118
+ if (typeof of === 'object') { options = of; of = 'text'; }
119
+ return new FieldDefinition('array', { arrayOf: of, nullable: true, default: [], ...options });
120
+ },
121
+
104
122
  date(options = {}) {
105
123
  return new FieldDefinition('date', options);
106
124
  },
@@ -408,6 +408,12 @@ ${opsCode},
408
408
  case 'id':
409
409
  return 'fields.id()';
410
410
 
411
+ case 'array': {
412
+ const of_ = def.arrayOf || 'text';
413
+ const optsStr = Object.keys(opts).length ? `, ${this._renderOpts(opts)}` : '';
414
+ return `fields.array('${of_}'${optsStr})`;
415
+ }
416
+
411
417
  case 'enum': {
412
418
  const vals = JSON.stringify(def.enumValues || []);
413
419
  const optsStr = Object.keys(opts).length
@@ -523,6 +523,10 @@ ${this._renderColumn(' ', diff.column, diff.previous, '.alter()')}
523
523
  line = `t.json('${name}')`;
524
524
  break;
525
525
 
526
+ case 'array':
527
+ line = `t.specificType('${name}', '${field.arrayOf || 'text'}[]')`;
528
+ break;
529
+
526
530
  case 'date':
527
531
  line = `t.date('${name}')`;
528
532
  break;
@@ -1,81 +1,49 @@
1
1
  'use strict';
2
2
 
3
- /**
4
- * column.js
5
- *
6
- * Knex column builder helpers shared across all field-level operations.
7
- *
8
- * Having these in one place means:
9
- * - The type → knex method mapping is never duplicated
10
- * - AlterField reuses the same logic as AddField, with `.alter()` appended
11
- * - FK constraint attachment is explicit and separated from column creation
12
- *
13
- * Exports:
14
- * applyColumn(t, name, def) — add a new column to a table builder
15
- * alterColumn(t, name, def) — modify an existing column (.alter())
16
- * attachFKConstraints(db, table, fields) — attach FK constraints via ALTER TABLE
17
- * after all tables in a migration exist
18
- */
19
-
20
3
  // ─── Core column builder ──────────────────────────────────────────────────────
21
4
 
22
- /**
23
- * Add a single column to a knex table builder.
24
- *
25
- * Handles all supported field types, nullability, uniqueness, defaults,
26
- * and inline FK constraints (references).
27
- *
28
- * Pass `{ ...def, references: null }` to suppress FK constraint creation
29
- * when deferring constraints to a later ALTER TABLE pass.
30
- *
31
- * @param {object} t — knex table builder (from createTable / table callback)
32
- * @param {string} name — column name
33
- * @param {object} def — normalised field definition from ProjectState.normaliseField()
34
- */
35
- function applyColumn(t, name, def) {
5
+ function applyColumn(t, name, def, tableName) {
36
6
  const col = _buildColumn(t, name, def);
37
- if (!col) return; // 'id' handled internally by _buildColumn
7
+ if (!col) return;
38
8
 
39
9
  _applyModifiers(col, def);
10
+
11
+ // Postgres enum: stored as text + separate CHECK constraint
12
+ if (def.type === 'enum' && def.enumValues?.length) {
13
+ const client = t.client?.config?.client || '';
14
+ if (client.includes('pg') || client.includes('postgres')) {
15
+ const values = def.enumValues.map(v => `'${v}'`).join(', ');
16
+ const constraintName = `${tableName || 'tbl'}_${name}_check`;
17
+ t.check(`"${name}" in (${values})`, [], constraintName);
18
+ }
19
+ }
40
20
  }
41
21
 
42
- /**
43
- * Modify an existing column in a knex alterTable builder.
44
- * Identical to applyColumn but appends `.alter()` — required by knex to
45
- * signal that this is a column modification, not a new column addition.
46
- *
47
- * Note: FK constraints are NOT altered here use attachFKConstraints()
48
- * to manage them separately. Most DBs require DROP CONSTRAINT + re-add
49
- * for FK changes, which is safer to do explicitly.
50
- *
51
- * @param {object} t knex table builder (from alterTable callback)
52
- * @param {string} name — column name
53
- * @param {object} def normalised field definition
54
- */
55
- function alterColumn(t, name, def) {
22
+ function alterColumn(t, name, def, tableName) {
23
+ const client = t.client?.config?.client || '';
24
+ const isPg = client.includes('pg') || client.includes('postgres');
25
+
26
+ if (isPg && def.type === 'enum' && def.enumValues?.length) {
27
+ // Postgres: ALTER COLUMN TYPE with inline CHECK is invalid.
28
+ // Drop old CHECK constraint, add new one.
29
+ const constraintName = `${tableName || 'tbl'}_${name}_check`;
30
+ const values = def.enumValues.map(v => `'${v}'`).join(', ');
31
+ try { t.dropChecks(constraintName); } catch {}
32
+ t.check(`"${name}" in (${values})`, [], constraintName);
33
+ if (def.nullable) t.setNullable(name);
34
+ else t.dropNullable(name);
35
+ return;
36
+ }
37
+
56
38
  const col = _buildColumn(t, name, def, { forAlter: true });
57
39
  if (!col) return;
58
40
 
59
- _applyModifiers(col, def, { skipFK: true }); // FKs not altered inline
41
+ _applyModifiers(col, def, { skipFK: true });
60
42
  col.alter();
61
43
  }
62
44
 
63
- /**
64
- * Attach FK constraints for a set of fields on a table.
65
- *
66
- * Called by MigrationRunner AFTER all tables in a migration have been
67
- * created — this guarantees all referenced tables exist.
68
- *
69
- * All FK columns for a given table are batched into a single ALTER TABLE
70
- * statement, not one per column.
71
- *
72
- * @param {import('knex').Knex} db
73
- * @param {string} table — table name
74
- * @param {object} fields — { columnName: normalisedDef, ... }
75
- */
76
45
  async function attachFKConstraints(db, table, fields) {
77
46
  const fkEntries = Object.entries(fields).filter(([, def]) => def.references);
78
-
79
47
  if (fkEntries.length === 0) return;
80
48
 
81
49
  await db.schema.alterTable(table, (t) => {
@@ -91,22 +59,11 @@ async function attachFKConstraints(db, table, fields) {
91
59
 
92
60
  // ─── Internal helpers ─────────────────────────────────────────────────────────
93
61
 
94
- /**
95
- * Build a knex column builder for a given field type.
96
- * Returns null for 'id' fields (handled by t.increments which returns void).
97
- *
98
- * @param {object} t
99
- * @param {string} name
100
- * @param {object} def
101
- * @param {object} [opts]
102
- * @param {boolean} [opts.forAlter] — if true, skip t.increments (can't alter PK)
103
- * @returns {object|null} knex column builder
104
- */
105
62
  function _buildColumn(t, name, def, opts = {}) {
106
63
  switch (def.type) {
107
64
  case 'id':
108
65
  if (!opts.forAlter) t.increments(name).primary();
109
- return null; // increments() doesn't return a chainable column builder
66
+ return null;
110
67
 
111
68
  case 'string':
112
69
  case 'email':
@@ -119,14 +76,10 @@ function _buildColumn(t, name, def, opts = {}) {
119
76
  return t.text(name);
120
77
 
121
78
  case 'integer':
122
- return def.unsigned
123
- ? t.integer(name).unsigned()
124
- : t.integer(name);
79
+ return def.unsigned ? t.integer(name).unsigned() : t.integer(name);
125
80
 
126
81
  case 'bigInteger':
127
- return def.unsigned
128
- ? t.bigInteger(name).unsigned()
129
- : t.bigInteger(name);
82
+ return def.unsigned ? t.bigInteger(name).unsigned() : t.bigInteger(name);
130
83
 
131
84
  case 'float':
132
85
  return t.float(name);
@@ -140,49 +93,60 @@ function _buildColumn(t, name, def, opts = {}) {
140
93
  case 'json':
141
94
  return t.json(name);
142
95
 
96
+ case 'array': {
97
+ const client = t.client?.config?.client || '';
98
+ if (client.includes('pg') || client.includes('postgres')) {
99
+ // Native Postgres ARRAY type
100
+ const pgTypeMap = {
101
+ text: 'text', string: 'text',
102
+ integer: 'integer', int: 'integer',
103
+ float: 'float', decimal: 'decimal',
104
+ boolean: 'boolean',
105
+ uuid: 'uuid',
106
+ };
107
+ const pgType = pgTypeMap[def.arrayOf || 'text'] || 'text';
108
+ return t.specificType(name, `${pgType}[]`);
109
+ }
110
+ // SQLite / MySQL — fall back to JSON
111
+ return t.json(name);
112
+ }
113
+
143
114
  case 'date':
144
115
  return t.date(name);
145
116
 
146
117
  case 'timestamp':
147
118
  return t.timestamp(name, { useTz: false });
148
119
 
149
- case 'enum':
120
+ case 'enum': {
121
+ const client = t.client?.config?.client || '';
122
+ if (client.includes('pg') || client.includes('postgres')) {
123
+ // Store as text — CHECK constraint added separately in applyColumn
124
+ return t.text(name);
125
+ }
150
126
  return t.enu(name, def.enumValues || []);
127
+ }
151
128
 
152
129
  case 'uuid':
153
130
  return t.uuid(name);
154
131
 
155
132
  default:
156
- return t.string(name); // safe fallback
133
+ return t.string(name);
157
134
  }
158
135
  }
159
136
 
160
- /**
161
- * Apply nullability, uniqueness, default, and FK constraint modifiers
162
- * to an already-built knex column builder.
163
- *
164
- * @param {object} col — knex column builder
165
- * @param {object} def — normalised field def
166
- * @param {object} [opts]
167
- * @param {boolean} [opts.skipFK] — skip FK constraint (used by alterColumn)
168
- */
169
137
  function _applyModifiers(col, def, opts = {}) {
170
- // Nullability
171
138
  if (def.nullable) {
172
139
  col.nullable();
173
140
  } else if (def.type !== 'id') {
174
141
  col.notNullable();
175
142
  }
176
143
 
177
- // Uniqueness
178
144
  if (def.unique) col.unique();
179
145
 
180
- // Default value
181
146
  if (def.default !== null && def.default !== undefined) {
182
147
  col.defaultTo(def.default);
183
148
  }
184
149
 
185
- // Inline FK constraint — skipped when deferring to attachFKConstraints()
186
150
  if (!opts.skipFK && def.references) {
187
151
  const ref = def.references;
188
152
  col
@@ -192,4 +156,4 @@ function _applyModifiers(col, def, opts = {}) {
192
156
  }
193
157
  }
194
158
 
195
- module.exports = { applyColumn, alterColumn, attachFKConstraints };
159
+ module.exports = { applyColumn, alterColumn, attachFKConstraints };
@@ -53,7 +53,7 @@ class AddField extends BaseOperation {
53
53
  await this._safeBackfill(db, def);
54
54
  } else {
55
55
  await db.schema.table(this.table, (t) => {
56
- applyColumn(t, this.column, def);
56
+ applyColumn(t, this.column, def, this.table);
57
57
  });
58
58
  }
59
59
  }
@@ -94,7 +94,7 @@ class AddField extends BaseOperation {
94
94
 
95
95
  // Step 1: add as nullable
96
96
  await db.schema.table(this.table, (t) => {
97
- applyColumn(t, this.column, { ...def, nullable: true, default: null });
97
+ applyColumn(t, this.column, { ...def, nullable: true, default: null }, this.table);
98
98
  });
99
99
 
100
100
  // Step 2: backfill
@@ -115,7 +115,7 @@ class AddField extends BaseOperation {
115
115
 
116
116
  // Step 3: tighten to NOT NULL
117
117
  await db.schema.alterTable(this.table, (t) => {
118
- alterColumn(t, this.column, { ...def, nullable: false });
118
+ alterColumn(t, this.column, { ...def, nullable: false }, this.table);
119
119
  });
120
120
  }
121
121
  }
@@ -148,7 +148,7 @@ class RemoveField extends BaseOperation {
148
148
 
149
149
  async down(db) {
150
150
  await db.schema.table(this.table, (t) => {
151
- applyColumn(t, this.column, normaliseField(this.field));
151
+ applyColumn(t, this.column, normaliseField(this.field), this.table);
152
152
  });
153
153
  }
154
154
 
@@ -186,13 +186,13 @@ class AlterField extends BaseOperation {
186
186
 
187
187
  async up(db) {
188
188
  await db.schema.alterTable(this.table, (t) => {
189
- alterColumn(t, this.column, normaliseField(this.field));
189
+ alterColumn(t, this.column, normaliseField(this.field), this.table);
190
190
  });
191
191
  }
192
192
 
193
193
  async down(db) {
194
194
  await db.schema.alterTable(this.table, (t) => {
195
- alterColumn(t, this.column, normaliseField(this.previousField));
195
+ alterColumn(t, this.column, normaliseField(this.previousField), this.table);
196
196
  });
197
197
  }
198
198
 
@@ -48,7 +48,7 @@ class CreateModel extends BaseOperation {
48
48
  async up(db) {
49
49
  await db.schema.createTable(this.table, (t) => {
50
50
  for (const [name, def] of Object.entries(this.fields)) {
51
- applyColumn(t, name, normaliseField(def));
51
+ applyColumn(t, name, normaliseField(def), this.table);
52
52
  }
53
53
  });
54
54
  await this._applyIndexes(db);
@@ -61,7 +61,7 @@ class CreateModel extends BaseOperation {
61
61
  async upWithoutFKs(db) {
62
62
  await db.schema.createTable(this.table, (t) => {
63
63
  for (const [name, def] of Object.entries(this.fields)) {
64
- applyColumn(t, name, { ...normaliseField(def), references: null });
64
+ applyColumn(t, name, { ...normaliseField(def), references: null }, this.table);
65
65
  }
66
66
  });
67
67
  await this._applyIndexes(db);
@@ -134,7 +134,7 @@ class DeleteModel extends BaseOperation {
134
134
  async down(db) {
135
135
  await db.schema.createTable(this.table, (t) => {
136
136
  for (const [name, def] of Object.entries(this.fields)) {
137
- applyColumn(t, name, normaliseField(def));
137
+ applyColumn(t, name, normaliseField(def), this.table);
138
138
  }
139
139
  });
140
140
  }