metal-orm 1.0.14 → 1.0.15
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 +40 -45
- package/dist/decorators/index.cjs +1600 -27
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -2
- package/dist/decorators/index.d.ts +6 -2
- package/dist/decorators/index.js +1599 -27
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +4608 -3429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +511 -159
- package/dist/index.d.ts +511 -159
- package/dist/index.js +4526 -3415
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-Bkv8g8u_.d.cts} +193 -67
- package/dist/{select-CCp1oz9p.d.ts → select-Bkv8g8u_.d.ts} +193 -67
- package/package.json +1 -1
- package/src/codegen/typescript.ts +38 -35
- package/src/core/ast/adapters.ts +21 -0
- package/src/core/ast/aggregate-functions.ts +13 -13
- package/src/core/ast/builders.ts +56 -43
- package/src/core/ast/expression-builders.ts +34 -34
- package/src/core/ast/expression-nodes.ts +18 -16
- package/src/core/ast/expression-visitor.ts +122 -69
- package/src/core/ast/expression.ts +6 -4
- package/src/core/ast/join-metadata.ts +15 -0
- package/src/core/ast/join-node.ts +22 -20
- package/src/core/ast/join.ts +5 -5
- package/src/core/ast/query.ts +52 -88
- package/src/core/ast/types.ts +20 -0
- package/src/core/ast/window-functions.ts +55 -55
- package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
- package/src/core/ddl/introspect/context.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +26 -0
- package/src/core/ddl/introspect/mssql.ts +149 -149
- package/src/core/ddl/introspect/mysql.ts +99 -99
- package/src/core/ddl/introspect/postgres.ts +245 -154
- package/src/core/ddl/introspect/registry.ts +26 -0
- package/src/core/ddl/introspect/run-select.ts +25 -0
- package/src/core/ddl/introspect/sqlite.ts +7 -7
- package/src/core/ddl/introspect/types.ts +23 -19
- package/src/core/ddl/introspect/utils.ts +1 -1
- package/src/core/ddl/naming-strategy.ts +10 -0
- package/src/core/ddl/schema-dialect.ts +41 -0
- package/src/core/ddl/schema-diff.ts +211 -179
- package/src/core/ddl/schema-generator.ts +16 -90
- package/src/core/ddl/schema-introspect.ts +25 -32
- package/src/core/ddl/schema-plan-executor.ts +17 -0
- package/src/core/ddl/schema-types.ts +46 -39
- package/src/core/ddl/sql-writing.ts +170 -0
- package/src/core/dialect/abstract.ts +144 -126
- package/src/core/dialect/base/cte-compiler.ts +33 -0
- package/src/core/dialect/base/function-table-formatter.ts +132 -0
- package/src/core/dialect/base/groupby-compiler.ts +21 -0
- package/src/core/dialect/base/join-compiler.ts +26 -0
- package/src/core/dialect/base/orderby-compiler.ts +21 -0
- package/src/core/dialect/base/pagination-strategy.ts +32 -0
- package/src/core/dialect/base/returning-strategy.ts +56 -0
- package/src/core/dialect/base/sql-dialect.ts +181 -204
- package/src/core/dialect/dialect-factory.ts +91 -0
- package/src/core/dialect/mssql/functions.ts +101 -0
- package/src/core/dialect/mssql/index.ts +128 -126
- package/src/core/dialect/mysql/functions.ts +101 -0
- package/src/core/dialect/mysql/index.ts +20 -18
- package/src/core/dialect/postgres/functions.ts +95 -0
- package/src/core/dialect/postgres/index.ts +30 -28
- package/src/core/dialect/sqlite/functions.ts +115 -0
- package/src/core/dialect/sqlite/index.ts +30 -28
- package/src/core/driver/database-driver.ts +11 -0
- package/src/core/driver/mssql-driver.ts +20 -0
- package/src/core/driver/mysql-driver.ts +20 -0
- package/src/core/driver/postgres-driver.ts +20 -0
- package/src/core/driver/sqlite-driver.ts +20 -0
- package/src/core/execution/db-executor.ts +63 -0
- package/src/core/execution/executors/mssql-executor.ts +39 -0
- package/src/core/execution/executors/mysql-executor.ts +47 -0
- package/src/core/execution/executors/postgres-executor.ts +32 -0
- package/src/core/execution/executors/sqlite-executor.ts +31 -0
- package/src/core/functions/datetime.ts +132 -0
- package/src/core/functions/numeric.ts +179 -0
- package/src/core/functions/standard-strategy.ts +47 -0
- package/src/core/functions/text.ts +147 -0
- package/src/core/functions/types.ts +18 -0
- package/src/core/hydration/types.ts +57 -0
- package/src/decorators/bootstrap.ts +10 -0
- package/src/decorators/relations.ts +15 -0
- package/src/index.ts +30 -19
- package/src/orm/entity-metadata.ts +7 -0
- package/src/orm/entity.ts +58 -27
- package/src/orm/hydration.ts +25 -17
- package/src/orm/lazy-batch.ts +46 -2
- package/src/orm/orm-context.ts +60 -60
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +43 -2
- package/src/orm/relations/has-one.ts +139 -0
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +60 -60
- package/src/query-builder/delete.ts +22 -5
- package/src/query-builder/hydration-manager.ts +2 -1
- package/src/query-builder/hydration-planner.ts +8 -7
- package/src/query-builder/insert.ts +22 -5
- package/src/query-builder/relation-conditions.ts +9 -8
- package/src/query-builder/relation-service.ts +3 -2
- package/src/query-builder/select.ts +66 -61
- package/src/query-builder/update.ts +22 -5
- package/src/schema/column.ts +246 -246
- package/src/schema/relation.ts +35 -1
- package/src/schema/table.ts +28 -28
- package/src/schema/types.ts +41 -31
- package/src/orm/db-executor.ts +0 -11
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ Full docs live in the `docs/` folder:
|
|
|
63
63
|
- **Fluent query builder** over a real SQL AST
|
|
64
64
|
(`SelectQueryBuilder`, `InsertQueryBuilder`, `UpdateQueryBuilder`, `DeleteQueryBuilder`).
|
|
65
65
|
- **Advanced SQL**: CTEs, aggregates, window functions, subqueries, JSON, CASE, EXISTS.
|
|
66
|
+
- **String helpers**: `lower`, `upper`, `trim`, `ltrim/rtrim`, `concat/concatWs`, `substr/left/right`, `position/instr/locate`, `replace`, `repeat`, `lpad/rpad`, `space`, and more with dialect-aware rendering.
|
|
66
67
|
- **Set operations**: `union`, `unionAll`, `intersect`, `except` across all dialects (ORDER/LIMIT apply to the combined result; hydration is disabled for compound queries so rows are returned as-is without collapsing duplicates).
|
|
67
68
|
- **Expression builders**: `eq`, `and`, `or`, `between`, `inList`, `exists`, `jsonPath`, `caseWhen`, window functions like `rowNumber`, `rank`, `lag`, `lead`, etc., all backed by typed AST nodes.
|
|
68
69
|
- **Relation-aware hydration**: turn flat rows into nested objects (`user.posts`, `user.roles`, etc.) using a hydration plan derived from the AST metadata.
|
|
@@ -102,9 +103,10 @@ If you like explicit model classes, you can add a thin decorator layer on top of
|
|
|
102
103
|
- `@Column(...)` and `@PrimaryKey(...)` on properties; decorators collect column metadata and later build `TableDef`s from it.
|
|
103
104
|
- Relation decorators:
|
|
104
105
|
- `@HasMany({ target, foreignKey, ... })`
|
|
106
|
+
- `@HasOne({ target, foreignKey, ... })`
|
|
105
107
|
- `@BelongsTo({ target, foreignKey, ... })`
|
|
106
108
|
- `@BelongsToMany({ target, pivotTable, ... })`
|
|
107
|
-
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
|
|
109
|
+
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables.
|
|
108
110
|
- `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
|
|
109
111
|
|
|
110
112
|
You don’t have to use decorators, but when you do, you’re still on the same AST + dialect + runtime foundation.
|
|
@@ -171,10 +173,13 @@ import {
|
|
|
171
173
|
|
|
172
174
|
// 1) A very small table
|
|
173
175
|
const todos = defineTable('todos', {
|
|
174
|
-
id: col.
|
|
175
|
-
title: col.varchar(255)
|
|
176
|
-
done: col.boolean()
|
|
176
|
+
id: col.primaryKey(col.int()),
|
|
177
|
+
title: col.varchar(255),
|
|
178
|
+
done: col.boolean(),
|
|
177
179
|
});
|
|
180
|
+
// Add constraints
|
|
181
|
+
todos.columns.title.notNull = true;
|
|
182
|
+
todos.columns.done.default = false;
|
|
178
183
|
|
|
179
184
|
// 2) Build a simple query
|
|
180
185
|
const listOpenTodos = new SelectQueryBuilder(todos)
|
|
@@ -220,20 +225,29 @@ import {
|
|
|
220
225
|
} from 'metal-orm';
|
|
221
226
|
|
|
222
227
|
const posts = defineTable('posts', {
|
|
223
|
-
id: col.
|
|
224
|
-
title: col.varchar(255)
|
|
225
|
-
userId: col.int()
|
|
226
|
-
createdAt: col.timestamp()
|
|
228
|
+
id: col.primaryKey(col.int()),
|
|
229
|
+
title: col.varchar(255),
|
|
230
|
+
userId: col.int(),
|
|
231
|
+
createdAt: col.timestamp(),
|
|
227
232
|
});
|
|
228
233
|
|
|
234
|
+
// Add constraints
|
|
235
|
+
posts.columns.title.notNull = true;
|
|
236
|
+
posts.columns.userId.notNull = true;
|
|
237
|
+
|
|
229
238
|
const users = defineTable('users', {
|
|
230
|
-
id: col.
|
|
231
|
-
name: col.varchar(255)
|
|
232
|
-
email: col.varchar(255)
|
|
233
|
-
}, {
|
|
234
|
-
posts: hasMany(posts, 'userId'),
|
|
239
|
+
id: col.primaryKey(col.int()),
|
|
240
|
+
name: col.varchar(255),
|
|
241
|
+
email: col.varchar(255),
|
|
235
242
|
});
|
|
236
243
|
|
|
244
|
+
// Add relations and constraints
|
|
245
|
+
users.relations = {
|
|
246
|
+
posts: hasMany(posts, 'userId'),
|
|
247
|
+
};
|
|
248
|
+
users.columns.name.notNull = true;
|
|
249
|
+
users.columns.email.unique = true;
|
|
250
|
+
|
|
237
251
|
// Build a query with relation & window function
|
|
238
252
|
const builder = new SelectQueryBuilder(users)
|
|
239
253
|
.select({
|
|
@@ -287,36 +301,23 @@ When you're ready, you can let MetalORM manage entities and relations for you.
|
|
|
287
301
|
Instead of “naked objects”, your queries can return entities attached to an `OrmContext`:
|
|
288
302
|
|
|
289
303
|
```ts
|
|
304
|
+
import mysql from 'mysql2/promise';
|
|
290
305
|
import {
|
|
291
306
|
OrmContext,
|
|
292
307
|
MySqlDialect,
|
|
293
308
|
SelectQueryBuilder,
|
|
294
309
|
eq,
|
|
310
|
+
createMysqlExecutor,
|
|
295
311
|
} from 'metal-orm';
|
|
296
312
|
|
|
297
313
|
// 1) Create an OrmContext for this request
|
|
314
|
+
|
|
315
|
+
const connection = await mysql.createConnection({ /* ... */ });
|
|
316
|
+
const executor = createMysqlExecutor(connection);
|
|
317
|
+
|
|
298
318
|
const ctx = new OrmContext({
|
|
299
319
|
dialect: new MySqlDialect(),
|
|
300
|
-
executor
|
|
301
|
-
async executeSql(sql, params) {
|
|
302
|
-
const [rows] = await connection.execute(sql, params);
|
|
303
|
-
// MetalORM expects columns + values; adapt as needed
|
|
304
|
-
return [{
|
|
305
|
-
columns: Object.keys(rows[0] ?? {}),
|
|
306
|
-
values: rows.map(row => Object.values(row)),
|
|
307
|
-
}];
|
|
308
|
-
},
|
|
309
|
-
// Optional: if you want MetalORM to handle transactions around saveChanges()
|
|
310
|
-
async beginTransaction() {
|
|
311
|
-
await connection.beginTransaction();
|
|
312
|
-
},
|
|
313
|
-
async commitTransaction() {
|
|
314
|
-
await connection.commit();
|
|
315
|
-
},
|
|
316
|
-
async rollbackTransaction() {
|
|
317
|
-
await connection.rollback();
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
+
executor,
|
|
320
321
|
});
|
|
321
322
|
|
|
322
323
|
// 2) Load entities with lazy relations
|
|
@@ -380,7 +381,7 @@ class User {
|
|
|
380
381
|
@PrimaryKey(col.int())
|
|
381
382
|
id!: number;
|
|
382
383
|
|
|
383
|
-
@Column(col.varchar(255)
|
|
384
|
+
@Column(col.varchar(255))
|
|
384
385
|
name!: string;
|
|
385
386
|
|
|
386
387
|
@Column(col.varchar(255))
|
|
@@ -398,10 +399,10 @@ class Post {
|
|
|
398
399
|
@PrimaryKey(col.int())
|
|
399
400
|
id!: number;
|
|
400
401
|
|
|
401
|
-
@Column(col.varchar(255)
|
|
402
|
+
@Column(col.varchar(255))
|
|
402
403
|
title!: string;
|
|
403
404
|
|
|
404
|
-
@Column(col.int()
|
|
405
|
+
@Column(col.int())
|
|
405
406
|
userId!: number;
|
|
406
407
|
|
|
407
408
|
@BelongsTo({
|
|
@@ -417,18 +418,11 @@ const tables = bootstrapEntities();
|
|
|
417
418
|
|
|
418
419
|
// 2) Create OrmContext as before
|
|
419
420
|
const connection = await mysql.createConnection({ /* ... */ });
|
|
421
|
+
const executor = createMysqlExecutor(connection);
|
|
420
422
|
|
|
421
423
|
const ctx = new OrmContext({
|
|
422
424
|
dialect: new MySqlDialect(),
|
|
423
|
-
executor
|
|
424
|
-
async executeSql(sql, params) {
|
|
425
|
-
const [rows] = await connection.execute(sql, params);
|
|
426
|
-
return [{
|
|
427
|
-
columns: Object.keys(rows[0] ?? {}),
|
|
428
|
-
values: rows.map(row => Object.values(row)),
|
|
429
|
-
}];
|
|
430
|
-
},
|
|
431
|
-
},
|
|
425
|
+
executor,
|
|
432
426
|
});
|
|
433
427
|
|
|
434
428
|
// 3) Query starting from the entity class
|
|
@@ -475,6 +469,7 @@ Under the hood, MetalORM leans on well-known patterns:
|
|
|
475
469
|
|
|
476
470
|
- **AST + dialect abstraction**: SQL is modeled as typed AST nodes, compiled by dialects that you can extend.
|
|
477
471
|
- **Separation of concerns**: schema, AST, SQL compilation, execution, and ORM runtime are separate layers.
|
|
472
|
+
- **Executor abstraction**: built-in executor creators (`createMysqlExecutor`, `createPostgresExecutor`, etc.) provide a clean separation between database drivers and ORM operations.
|
|
478
473
|
- **Unit of Work + Identity Map**: `OrmContext` coordinates changes and enforces one entity instance per row, following the [Unit of Work](https://en.wikipedia.org/wiki/Unit_of_work) and [Identity map](https://en.wikipedia.org/wiki/Identity_map_pattern) patterns.
|
|
479
474
|
- **Domain events + interceptors**: decouple side-effects from persistence and let cross-cutting concerns hook into flush points, similar in spirit to domain events in [Domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design).
|
|
480
475
|
|