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.
- package/README.md +74 -20
- package/dist/index.cjs +180 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -96
- package/dist/index.d.ts +142 -96
- package/dist/index.js +177 -74
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/scripts/run-eslint.mjs +34 -0
- package/src/codegen/typescript.ts +32 -15
- package/src/core/ast/builders.ts +7 -2
- package/src/core/ast/expression-builders.ts +0 -2
- package/src/core/ast/expression-nodes.ts +14 -5
- package/src/core/ast/expression-visitor.ts +11 -8
- package/src/core/ast/expression.ts +2 -2
- package/src/core/ast/join-node.ts +1 -1
- package/src/core/ast/query.ts +6 -6
- package/src/core/ast/window-functions.ts +10 -2
- package/src/core/ddl/dialects/base-schema-dialect.ts +30 -3
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +4 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +2 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +13 -1
- package/src/core/ddl/dialects/render-reference.test.ts +69 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
- package/src/core/ddl/introspect/mssql.ts +42 -8
- package/src/core/ddl/introspect/mysql.ts +30 -6
- package/src/core/ddl/introspect/postgres.ts +88 -34
- package/src/core/ddl/introspect/run-select.ts +6 -4
- package/src/core/ddl/introspect/sqlite.ts +56 -11
- package/src/core/ddl/introspect/types.ts +0 -1
- package/src/core/ddl/introspect/utils.ts +3 -3
- package/src/core/ddl/schema-dialect.ts +1 -0
- package/src/core/ddl/schema-generator.ts +4 -12
- package/src/core/ddl/sql-writing.ts +4 -4
- package/src/core/dialect/abstract.ts +18 -6
- package/src/core/dialect/base/function-table-formatter.ts +3 -2
- package/src/core/dialect/base/join-compiler.ts +5 -3
- package/src/core/dialect/base/returning-strategy.ts +1 -0
- package/src/core/dialect/base/sql-dialect.ts +3 -3
- package/src/core/dialect/mssql/functions.ts +24 -25
- package/src/core/dialect/mssql/index.ts +1 -4
- package/src/core/dialect/mysql/functions.ts +0 -1
- package/src/core/dialect/postgres/functions.ts +33 -34
- package/src/core/dialect/postgres/index.ts +1 -0
- package/src/core/dialect/sqlite/functions.ts +18 -19
- package/src/core/dialect/sqlite/index.ts +2 -0
- package/src/core/execution/db-executor.ts +1 -1
- package/src/core/execution/executors/mysql-executor.ts +2 -2
- package/src/core/execution/executors/postgres-executor.ts +1 -1
- package/src/core/execution/pooling/pool.ts +2 -0
- package/src/core/functions/datetime.ts +1 -1
- package/src/core/functions/numeric.ts +1 -1
- package/src/core/functions/text.ts +1 -1
- package/src/decorators/bootstrap.ts +27 -8
- package/src/decorators/column.ts +3 -11
- package/src/decorators/decorator-metadata.ts +3 -9
- package/src/decorators/entity.ts +21 -5
- package/src/decorators/relations.ts +2 -11
- package/src/orm/entity-context.ts +8 -8
- package/src/orm/entity-meta.ts +8 -8
- package/src/orm/entity-metadata.ts +11 -9
- package/src/orm/entity.ts +28 -29
- package/src/orm/execute.ts +4 -4
- package/src/orm/hydration.ts +42 -39
- package/src/orm/identity-map.ts +1 -1
- package/src/orm/lazy-batch.ts +9 -9
- package/src/orm/orm-session.ts +24 -23
- package/src/orm/orm.ts +2 -5
- package/src/orm/relation-change-processor.ts +12 -11
- package/src/orm/relations/belongs-to.ts +11 -11
- package/src/orm/relations/has-many.ts +10 -10
- package/src/orm/relations/has-one.ts +8 -7
- package/src/orm/relations/many-to-many.ts +13 -13
- package/src/orm/runtime-types.ts +4 -4
- package/src/orm/save-graph.ts +31 -25
- package/src/orm/unit-of-work.ts +17 -17
- package/src/query-builder/delete.ts +4 -3
- package/src/query-builder/hydration-manager.ts +6 -5
- package/src/query-builder/insert.ts +12 -8
- package/src/query-builder/query-ast-service.ts +2 -2
- package/src/query-builder/raw-column-parser.ts +2 -1
- package/src/query-builder/select-helpers.ts +2 -2
- package/src/query-builder/select.ts +31 -31
- package/src/query-builder/update.ts +4 -3
- package/src/schema/column.ts +26 -26
- package/src/schema/table.ts +239 -115
- 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
|
-
.
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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(
|
|
267
|
-
.groupBy(
|
|
268
|
-
.
|
|
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
|
-
.
|
|
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(
|
|
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(
|
|
498
|
+
.where(eq(U.id, 1))
|
|
445
499
|
.execute(session);
|
|
446
500
|
|
|
447
501
|
user.posts.add({ title: 'From decorators' });
|