metal-orm 1.0.41 → 1.0.43

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.
Files changed (87) hide show
  1. package/README.md +74 -20
  2. package/dist/index.cjs +180 -74
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +142 -96
  5. package/dist/index.d.ts +142 -96
  6. package/dist/index.js +177 -74
  7. package/dist/index.js.map +1 -1
  8. package/package.json +8 -2
  9. package/scripts/run-eslint.mjs +34 -0
  10. package/src/codegen/typescript.ts +32 -15
  11. package/src/core/ast/builders.ts +7 -2
  12. package/src/core/ast/expression-builders.ts +0 -2
  13. package/src/core/ast/expression-nodes.ts +14 -5
  14. package/src/core/ast/expression-visitor.ts +11 -8
  15. package/src/core/ast/expression.ts +2 -2
  16. package/src/core/ast/join-node.ts +1 -1
  17. package/src/core/ast/query.ts +6 -6
  18. package/src/core/ast/window-functions.ts +10 -2
  19. package/src/core/ddl/dialects/base-schema-dialect.ts +30 -3
  20. package/src/core/ddl/dialects/mssql-schema-dialect.ts +4 -0
  21. package/src/core/ddl/dialects/mysql-schema-dialect.ts +2 -0
  22. package/src/core/ddl/dialects/postgres-schema-dialect.ts +13 -1
  23. package/src/core/ddl/dialects/render-reference.test.ts +69 -0
  24. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  25. package/src/core/ddl/introspect/mssql.ts +42 -8
  26. package/src/core/ddl/introspect/mysql.ts +30 -6
  27. package/src/core/ddl/introspect/postgres.ts +88 -34
  28. package/src/core/ddl/introspect/run-select.ts +6 -4
  29. package/src/core/ddl/introspect/sqlite.ts +56 -11
  30. package/src/core/ddl/introspect/types.ts +0 -1
  31. package/src/core/ddl/introspect/utils.ts +3 -3
  32. package/src/core/ddl/schema-dialect.ts +1 -0
  33. package/src/core/ddl/schema-generator.ts +4 -12
  34. package/src/core/ddl/sql-writing.ts +4 -4
  35. package/src/core/dialect/abstract.ts +18 -6
  36. package/src/core/dialect/base/function-table-formatter.ts +3 -2
  37. package/src/core/dialect/base/join-compiler.ts +5 -3
  38. package/src/core/dialect/base/returning-strategy.ts +1 -0
  39. package/src/core/dialect/base/sql-dialect.ts +3 -3
  40. package/src/core/dialect/mssql/functions.ts +24 -25
  41. package/src/core/dialect/mssql/index.ts +1 -4
  42. package/src/core/dialect/mysql/functions.ts +0 -1
  43. package/src/core/dialect/postgres/functions.ts +33 -34
  44. package/src/core/dialect/postgres/index.ts +1 -0
  45. package/src/core/dialect/sqlite/functions.ts +18 -19
  46. package/src/core/dialect/sqlite/index.ts +2 -0
  47. package/src/core/execution/db-executor.ts +1 -1
  48. package/src/core/execution/executors/mysql-executor.ts +2 -2
  49. package/src/core/execution/executors/postgres-executor.ts +1 -1
  50. package/src/core/execution/pooling/pool.ts +2 -0
  51. package/src/core/functions/datetime.ts +1 -1
  52. package/src/core/functions/numeric.ts +1 -1
  53. package/src/core/functions/text.ts +1 -1
  54. package/src/decorators/bootstrap.ts +27 -8
  55. package/src/decorators/column.ts +3 -11
  56. package/src/decorators/decorator-metadata.ts +3 -9
  57. package/src/decorators/entity.ts +21 -5
  58. package/src/decorators/relations.ts +2 -11
  59. package/src/orm/entity-context.ts +8 -8
  60. package/src/orm/entity-meta.ts +8 -8
  61. package/src/orm/entity-metadata.ts +11 -9
  62. package/src/orm/entity.ts +28 -29
  63. package/src/orm/execute.ts +4 -4
  64. package/src/orm/hydration.ts +42 -39
  65. package/src/orm/identity-map.ts +1 -1
  66. package/src/orm/lazy-batch.ts +9 -9
  67. package/src/orm/orm-session.ts +24 -23
  68. package/src/orm/orm.ts +2 -5
  69. package/src/orm/relation-change-processor.ts +12 -11
  70. package/src/orm/relations/belongs-to.ts +11 -11
  71. package/src/orm/relations/has-many.ts +10 -10
  72. package/src/orm/relations/has-one.ts +8 -7
  73. package/src/orm/relations/many-to-many.ts +13 -13
  74. package/src/orm/runtime-types.ts +4 -4
  75. package/src/orm/save-graph.ts +31 -25
  76. package/src/orm/unit-of-work.ts +17 -17
  77. package/src/query-builder/delete.ts +4 -3
  78. package/src/query-builder/hydration-manager.ts +6 -5
  79. package/src/query-builder/insert.ts +12 -8
  80. package/src/query-builder/query-ast-service.ts +2 -2
  81. package/src/query-builder/raw-column-parser.ts +2 -1
  82. package/src/query-builder/select-helpers.ts +2 -2
  83. package/src/query-builder/select.ts +31 -31
  84. package/src/query-builder/update.ts +4 -3
  85. package/src/schema/column.ts +26 -26
  86. package/src/schema/table.ts +239 -115
  87. package/src/schema/types.ts +22 -22
package/README.md CHANGED
@@ -171,6 +171,7 @@ MetalORM can be just a straightforward query builder.
171
171
  import mysql from 'mysql2/promise';
172
172
  import {
173
173
  defineTable,
174
+ tableRef,
174
175
  col,
175
176
  SelectQueryBuilder,
176
177
  eq,
@@ -187,15 +188,14 @@ const todos = defineTable('todos', {
187
188
  todos.columns.title.notNull = true;
188
189
  todos.columns.done.default = false;
189
190
 
191
+ // Optional: opt-in ergonomic column access
192
+ const t = tableRef(todos);
193
+
190
194
  // 2) Build a simple query
191
195
  const listOpenTodos = new SelectQueryBuilder(todos)
192
- .select({
193
- id: todos.columns.id,
194
- title: todos.columns.title,
195
- done: todos.columns.done,
196
- })
197
- .where(eq(todos.columns.done, false))
198
- .orderBy(todos.columns.id, 'ASC');
196
+ .selectColumns('id', 'title', 'done')
197
+ .where(eq(t.done, false))
198
+ .orderBy(t.id, 'ASC');
199
199
 
200
200
  // 3) Compile to SQL + params
201
201
  const dialect = new MySqlDialect();
@@ -214,6 +214,48 @@ console.log(rows);
214
214
 
215
215
  That’s it: schema, query, SQL, done.
216
216
 
217
+ #### Column pickers (preferred selection helpers)
218
+
219
+ `defineTable` still exposes the full `table.columns` map for schema metadata and constraint tweaks, but modern queries usually benefit from higher-level helpers instead of spelling `todo.columns.*` everywhere.
220
+
221
+ ```ts
222
+ const t = tableRef(todos);
223
+
224
+ const listOpenTodos = new SelectQueryBuilder(todos)
225
+ .selectColumns('id', 'title', 'done') // typed shorthand for the same fields
226
+ .where(eq(t.done, false))
227
+ .orderBy(t.id, 'ASC');
228
+ ```
229
+
230
+ `selectColumns`, `selectRelationColumns`, `includePick`, `selectColumnsDeep`, the `sel()` helpers for tables, and `esel()` for entities all build typed selection maps without repeating `table.columns.*`. Use those helpers when building query selections and reserve `table.columns.*` for schema definition, relations, or rare cases where you need a column reference outside of a picker. See the [Query Builder docs](./docs/query-builder.md#selection-helpers) for the reference, examples, and best practices for these helpers.
231
+
232
+ #### Ergonomic column access (opt-in) with `tableRef`
233
+
234
+ If you still want the convenience of accessing columns without spelling `.columns`, you can opt-in with `tableRef()`:
235
+
236
+ ```ts
237
+ import { tableRef, eq } from 'metal-orm';
238
+
239
+ // Existing style (always works)
240
+ const listOpenTodos = new SelectQueryBuilder(todos)
241
+ .selectColumns('id', 'title', 'done')
242
+ .where(eq(todos.columns.done, false))
243
+ .orderBy(todos.columns.id, 'ASC');
244
+
245
+ // Opt-in ergonomic style
246
+ const t = tableRef(todos);
247
+
248
+ const listOpenTodos2 = new SelectQueryBuilder(todos)
249
+ .selectColumns('id', 'title', 'done')
250
+ .where(eq(t.done, false))
251
+ .orderBy(t.id, 'ASC');
252
+ ```
253
+
254
+ Collision rule: real table fields win.
255
+
256
+ - `t.name` is the table name (string)
257
+ - `t.$.name` is the column definition for a colliding column name (escape hatch)
258
+
217
259
  #### 2. Relations & hydration (still no ORM)
218
260
 
219
261
  Now add relations and get nested objects, still without committing to a runtime.
@@ -227,6 +269,8 @@ import {
227
269
  eq,
228
270
  count,
229
271
  rowNumber,
272
+ MySqlDialect,
273
+ sel,
230
274
  hydrateRows,
231
275
  } from 'metal-orm';
232
276
 
@@ -255,22 +299,24 @@ users.columns.name.notNull = true;
255
299
  users.columns.email.unique = true;
256
300
 
257
301
  // Build a query with relation & window function
302
+ const u = sel(users, 'id', 'name', 'email');
303
+ const p = sel(posts, 'id', 'userId');
304
+
258
305
  const builder = new SelectQueryBuilder(users)
259
306
  .select({
260
- id: users.columns.id,
261
- name: users.columns.name,
262
- email: users.columns.email,
263
- postCount: count(posts.columns.id),
264
- rank: rowNumber(), // window function helper
307
+ ...u,
308
+ postCount: count(p.id),
309
+ rank: rowNumber(), // window function helper
265
310
  })
266
- .leftJoin(posts, eq(posts.columns.userId, users.columns.id))
267
- .groupBy(users.columns.id, users.columns.name, users.columns.email)
268
- .orderBy(count(posts.columns.id), 'DESC')
311
+ .leftJoin(posts, eq(p.userId, u.id))
312
+ .groupBy(u.id)
313
+ .groupBy(u.name)
314
+ .groupBy(u.email)
315
+ .orderBy(count(p.id), 'DESC')
269
316
  .limit(10)
270
- .include('posts', {
271
- columns: [posts.columns.id, posts.columns.title, posts.columns.createdAt],
272
- }); // eager relation for hydration
317
+ .includePick('posts', ['id', 'title', 'createdAt']); // eager relation for hydration
273
318
 
319
+ const dialect = new MySqlDialect();
274
320
  const { sql, params } = builder.compile(dialect);
275
321
  const [rows] = await connection.execute(sql, params);
276
322
 
@@ -314,6 +360,7 @@ import {
314
360
  MySqlDialect,
315
361
  SelectQueryBuilder,
316
362
  eq,
363
+ tableRef,
317
364
  createMysqlExecutor,
318
365
  } from 'metal-orm';
319
366
 
@@ -326,16 +373,19 @@ const orm = new Orm({
326
373
  executorFactory: {
327
374
  createExecutor: () => executor,
328
375
  createTransactionalExecutor: () => executor,
376
+ dispose: async () => {},
329
377
  },
330
378
  });
331
379
  const session = new OrmSession({ orm, executor });
332
380
 
381
+ const u = tableRef(users);
382
+
333
383
  // 2) Load entities with lazy relations
334
384
  const [user] = await new SelectQueryBuilder(users)
335
385
  .selectColumns('id', 'name', 'email')
336
386
  .includeLazy('posts') // HasMany as a lazy collection
337
387
  .includeLazy('roles') // BelongsToMany as a lazy collection
338
- .where(eq(users.columns.id, 1))
388
+ .where(eq(u.id, 1))
339
389
  .execute(session);
340
390
 
341
391
  // user is an EntityInstance<typeof users>
@@ -383,6 +433,8 @@ import {
383
433
  BelongsTo,
384
434
  bootstrapEntities,
385
435
  selectFromEntity,
436
+ entityRef,
437
+ eq,
386
438
  } from 'metal-orm';
387
439
 
388
440
  @Entity()
@@ -433,15 +485,17 @@ const orm = new Orm({
433
485
  executorFactory: {
434
486
  createExecutor: () => executor,
435
487
  createTransactionalExecutor: () => executor,
488
+ dispose: async () => {},
436
489
  },
437
490
  });
438
491
  const session = new OrmSession({ orm, executor });
439
492
 
440
493
  // 3) Query starting from the entity class
494
+ const U = entityRef(User);
441
495
  const [user] = await selectFromEntity(User)
442
496
  .selectColumns('id', 'name')
443
497
  .includeLazy('posts')
444
- .where(/* same eq()/and() API as before */)
498
+ .where(eq(U.id, 1))
445
499
  .execute(session);
446
500
 
447
501
  user.posts.add({ title: 'From decorators' });