metal-orm 1.0.43 β 1.0.44
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 +173 -30
- package/dist/index.cjs +896 -476
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1146 -275
- package/dist/index.d.ts +1146 -275
- package/dist/index.js +896 -474
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ast/adapters.ts +8 -2
- package/src/core/ast/builders.ts +105 -81
- package/src/core/ast/expression-builders.ts +430 -390
- package/src/core/ast/expression-visitor.ts +47 -8
- package/src/core/ast/helpers.ts +23 -0
- package/src/core/ast/join-node.ts +17 -1
- package/src/core/ddl/dialects/base-schema-dialect.ts +7 -1
- package/src/core/ddl/dialects/index.ts +1 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -0
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
- package/src/core/ddl/introspect/context.ts +6 -0
- package/src/core/ddl/introspect/functions/postgres.ts +13 -0
- package/src/core/ddl/introspect/mssql.ts +11 -0
- package/src/core/ddl/introspect/mysql.ts +2 -0
- package/src/core/ddl/introspect/postgres.ts +14 -0
- package/src/core/ddl/introspect/registry.ts +14 -0
- package/src/core/ddl/introspect/run-select.ts +13 -0
- package/src/core/ddl/introspect/sqlite.ts +22 -0
- package/src/core/ddl/introspect/utils.ts +18 -0
- package/src/core/ddl/naming-strategy.ts +6 -0
- package/src/core/ddl/schema-dialect.ts +19 -6
- package/src/core/ddl/schema-diff.ts +22 -0
- package/src/core/ddl/schema-generator.ts +22 -0
- package/src/core/ddl/schema-plan-executor.ts +6 -0
- package/src/core/ddl/schema-types.ts +6 -0
- package/src/core/dialect/abstract.ts +2 -2
- package/src/core/execution/pooling/pool.ts +12 -7
- package/src/core/functions/datetime.ts +57 -33
- package/src/core/functions/numeric.ts +95 -30
- package/src/core/functions/standard-strategy.ts +35 -0
- package/src/core/functions/text.ts +83 -22
- package/src/core/functions/types.ts +23 -8
- package/src/decorators/bootstrap.ts +16 -4
- package/src/decorators/column.ts +17 -0
- package/src/decorators/decorator-metadata.ts +27 -0
- package/src/decorators/entity.ts +8 -0
- package/src/decorators/index.ts +3 -0
- package/src/decorators/relations.ts +32 -0
- package/src/orm/als.ts +34 -9
- package/src/orm/entity-context.ts +54 -0
- package/src/orm/entity-metadata.ts +122 -9
- package/src/orm/execute.ts +15 -0
- package/src/orm/lazy-batch.ts +68 -98
- package/src/orm/relations/has-many.ts +44 -0
- package/src/query/index.ts +74 -0
- package/src/query/target.ts +46 -0
- package/src/query-builder/delete-query-state.ts +30 -0
- package/src/query-builder/delete.ts +64 -19
- package/src/query-builder/hydration-manager.ts +46 -0
- package/src/query-builder/insert-query-state.ts +30 -0
- package/src/query-builder/insert.ts +46 -2
- package/src/query-builder/query-ast-service.ts +5 -0
- package/src/query-builder/query-resolution.ts +78 -0
- package/src/query-builder/raw-column-parser.ts +5 -0
- package/src/query-builder/relation-alias.ts +7 -0
- package/src/query-builder/relation-conditions.ts +61 -48
- package/src/query-builder/relation-service.ts +68 -63
- package/src/query-builder/relation-utils.ts +3 -0
- package/src/query-builder/select/cte-facet.ts +40 -0
- package/src/query-builder/select/from-facet.ts +80 -0
- package/src/query-builder/select/join-facet.ts +62 -0
- package/src/query-builder/select/predicate-facet.ts +103 -0
- package/src/query-builder/select/projection-facet.ts +69 -0
- package/src/query-builder/select/relation-facet.ts +81 -0
- package/src/query-builder/select/setop-facet.ts +36 -0
- package/src/query-builder/select-helpers.ts +13 -0
- package/src/query-builder/select-query-builder-deps.ts +19 -1
- package/src/query-builder/select-query-state.ts +2 -1
- package/src/query-builder/select.ts +795 -1163
- package/src/query-builder/update-query-state.ts +52 -0
- package/src/query-builder/update.ts +69 -19
- package/src/schema/table-guards.ts +31 -0
package/README.md
CHANGED
|
@@ -1,19 +1,53 @@
|
|
|
1
|
-
# MetalORM βοΈ
|
|
1
|
+
# MetalORM βοΈ
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/metal-orm)
|
|
4
4
|
[](https://github.com/celsowm/metal-orm/blob/main/LICENSE)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> **TypeScript-first ORM that adapts to your needs**: use it as a type-safe query builder, a full-featured ORM runtime, or anything in between.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Why MetalORM? π‘
|
|
10
|
+
|
|
11
|
+
- π― **Gradual adoption**: Start with just SQL building, add ORM features when you need them
|
|
12
|
+
- π **Exceptionally strongly typed**: Built with TypeScript generics and type inferenceβ**zero** `any` types in the entire codebase
|
|
13
|
+
- ποΈ **Well-architected**: Implements proven design patterns (Strategy, Visitor, Builder, Unit of Work, Identity Map, Interceptor, and more)
|
|
14
|
+
- π¨ **One AST, multiple levels**: All features share the same SQL AST foundationβno magic, just composable layers
|
|
15
|
+
- π **Multi-dialect from the start**: MySQL, PostgreSQL, SQLite, SQL Server support built-in
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## β‘ 30-Second Quick Start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { defineTable, col, selectFrom, MySqlDialect } from 'metal-orm';
|
|
23
|
+
|
|
24
|
+
const users = defineTable('users', {
|
|
25
|
+
id: col.primaryKey(col.int()),
|
|
26
|
+
name: col.varchar(255),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const query = selectFrom(users).select('id', 'name').limit(10);
|
|
30
|
+
const { sql, params } = query.compile(new MySqlDialect());
|
|
31
|
+
// That's it! Use sql + params with any driver.
|
|
32
|
+
// β Fully typedβno casting, no 'any', just strong types all the way down
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Three Levels of Abstraction
|
|
38
|
+
|
|
39
|
+
MetalORM is a TypeScript-first, AST-driven SQL toolkit you can dial up or down depending on how "ORM-y" you want to be:
|
|
40
|
+
|
|
41
|
+
- **Level 1 β Query builder & hydration π§©**
|
|
10
42
|
Define tables with `defineTable` / `col.*`, build strongly-typed queries on a real SQL AST, and hydrate flat result sets into nested objects β no ORM runtime involved.
|
|
11
|
-
|
|
43
|
+
|
|
44
|
+
- **Level 2 β ORM runtime (entities + Unit of Work π§ )**
|
|
12
45
|
Let `OrmSession` (created from `Orm`) turn rows into tracked entities with lazy relations, cascades, and a [Unit of Work](https://en.wikipedia.org/wiki/Unit_of_work) that flushes changes with `session.commit()`.
|
|
13
|
-
|
|
46
|
+
|
|
47
|
+
- **Level 3 β Decorator entities (classes + metadata β¨)**
|
|
14
48
|
Use `@Entity`, `@Column`, `@PrimaryKey`, relation decorators, `bootstrapEntities()` (or the lazy bootstrapping in `getTableDefFromEntity` / `selectFromEntity`) to describe your model classes. MetalORM bootstraps schema & relations from metadata and plugs them into the same runtime and query builder.
|
|
15
49
|
|
|
16
|
-
Use only the layer you need in each part of your codebase
|
|
50
|
+
**Use only the layer you need in each part of your codebase.**
|
|
17
51
|
|
|
18
52
|
---
|
|
19
53
|
|
|
@@ -28,7 +62,10 @@ Use only the layer you need in each part of your codebase.
|
|
|
28
62
|
- [Level 2 β Entities + Unit of Work](#level-2)
|
|
29
63
|
- [Level 3 β Decorator entities](#level-3)
|
|
30
64
|
- [When to use which level?](#when-to-use-which-level)
|
|
31
|
-
- [Design
|
|
65
|
+
- [Design & Architecture](#design-notes)
|
|
66
|
+
- [FAQ](#frequently-asked-questions-)
|
|
67
|
+
- [Performance & Production](#performance--production-)
|
|
68
|
+
- [Community & Support](#community--support-)
|
|
32
69
|
- [Contributing](#contributing)
|
|
33
70
|
- [License](#license)
|
|
34
71
|
|
|
@@ -173,7 +210,7 @@ import {
|
|
|
173
210
|
defineTable,
|
|
174
211
|
tableRef,
|
|
175
212
|
col,
|
|
176
|
-
|
|
213
|
+
selectFrom,
|
|
177
214
|
eq,
|
|
178
215
|
MySqlDialect,
|
|
179
216
|
} from 'metal-orm';
|
|
@@ -192,8 +229,8 @@ todos.columns.done.default = false;
|
|
|
192
229
|
const t = tableRef(todos);
|
|
193
230
|
|
|
194
231
|
// 2) Build a simple query
|
|
195
|
-
const listOpenTodos =
|
|
196
|
-
.
|
|
232
|
+
const listOpenTodos = selectFrom(todos)
|
|
233
|
+
.select('id', 'title', 'done')
|
|
197
234
|
.where(eq(t.done, false))
|
|
198
235
|
.orderBy(t.id, 'ASC');
|
|
199
236
|
|
|
@@ -221,8 +258,8 @@ Thatβs it: schema, query, SQL, done.
|
|
|
221
258
|
```ts
|
|
222
259
|
const t = tableRef(todos);
|
|
223
260
|
|
|
224
|
-
const listOpenTodos =
|
|
225
|
-
.
|
|
261
|
+
const listOpenTodos = selectFrom(todos)
|
|
262
|
+
.select('id', 'title', 'done') // typed shorthand for the same fields
|
|
226
263
|
.where(eq(t.done, false))
|
|
227
264
|
.orderBy(t.id, 'ASC');
|
|
228
265
|
```
|
|
@@ -234,19 +271,19 @@ const listOpenTodos = new SelectQueryBuilder(todos)
|
|
|
234
271
|
If you still want the convenience of accessing columns without spelling `.columns`, you can opt-in with `tableRef()`:
|
|
235
272
|
|
|
236
273
|
```ts
|
|
237
|
-
import { tableRef, eq } from 'metal-orm';
|
|
274
|
+
import { tableRef, eq, selectFrom } from 'metal-orm';
|
|
238
275
|
|
|
239
276
|
// Existing style (always works)
|
|
240
|
-
const listOpenTodos =
|
|
241
|
-
.
|
|
277
|
+
const listOpenTodos = selectFrom(todos)
|
|
278
|
+
.select('id', 'title', 'done')
|
|
242
279
|
.where(eq(todos.columns.done, false))
|
|
243
280
|
.orderBy(todos.columns.id, 'ASC');
|
|
244
281
|
|
|
245
282
|
// Opt-in ergonomic style
|
|
246
283
|
const t = tableRef(todos);
|
|
247
284
|
|
|
248
|
-
const listOpenTodos2 =
|
|
249
|
-
.
|
|
285
|
+
const listOpenTodos2 = selectFrom(todos)
|
|
286
|
+
.select('id', 'title', 'done')
|
|
250
287
|
.where(eq(t.done, false))
|
|
251
288
|
.orderBy(t.id, 'ASC');
|
|
252
289
|
```
|
|
@@ -265,7 +302,7 @@ import {
|
|
|
265
302
|
defineTable,
|
|
266
303
|
col,
|
|
267
304
|
hasMany,
|
|
268
|
-
|
|
305
|
+
selectFrom,
|
|
269
306
|
eq,
|
|
270
307
|
count,
|
|
271
308
|
rowNumber,
|
|
@@ -302,7 +339,7 @@ users.columns.email.unique = true;
|
|
|
302
339
|
const u = sel(users, 'id', 'name', 'email');
|
|
303
340
|
const p = sel(posts, 'id', 'userId');
|
|
304
341
|
|
|
305
|
-
const builder =
|
|
342
|
+
const builder = selectFrom(users)
|
|
306
343
|
.select({
|
|
307
344
|
...u,
|
|
308
345
|
postCount: count(p.id),
|
|
@@ -358,7 +395,7 @@ import {
|
|
|
358
395
|
Orm,
|
|
359
396
|
OrmSession,
|
|
360
397
|
MySqlDialect,
|
|
361
|
-
|
|
398
|
+
selectFrom,
|
|
362
399
|
eq,
|
|
363
400
|
tableRef,
|
|
364
401
|
createMysqlExecutor,
|
|
@@ -381,8 +418,8 @@ const session = new OrmSession({ orm, executor });
|
|
|
381
418
|
const u = tableRef(users);
|
|
382
419
|
|
|
383
420
|
// 2) Load entities with lazy relations
|
|
384
|
-
const [user] = await
|
|
385
|
-
.
|
|
421
|
+
const [user] = await selectFrom(users)
|
|
422
|
+
.select('id', 'name', 'email')
|
|
386
423
|
.includeLazy('posts') // HasMany as a lazy collection
|
|
387
424
|
.includeLazy('roles') // BelongsToMany as a lazy collection
|
|
388
425
|
.where(eq(u.id, 1))
|
|
@@ -528,17 +565,123 @@ All three levels share the same schema, AST, and dialects, so you can mix them a
|
|
|
528
565
|
---
|
|
529
566
|
|
|
530
567
|
<a id="design-notes"></a>
|
|
531
|
-
## Design
|
|
568
|
+
## Design & Architecture ποΈ
|
|
569
|
+
|
|
570
|
+
MetalORM is built on solid software engineering principles and proven design patterns.
|
|
571
|
+
|
|
572
|
+
### Architecture Layers
|
|
573
|
+
|
|
574
|
+
```
|
|
575
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
576
|
+
β Your Application β
|
|
577
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
578
|
+
β
|
|
579
|
+
ββββββββββββββββββββΌβββββββββββββββββββ
|
|
580
|
+
β β β
|
|
581
|
+
βΌ βΌ βΌ
|
|
582
|
+
βββββββββββ ββββββββββββ ββββββββββββ
|
|
583
|
+
β Level 1 β β Level 2 β β Level 3 β
|
|
584
|
+
β Query ββββββββ€ ORM ββββββββ€Decoratorsβ
|
|
585
|
+
β Builder β β Runtime β β β
|
|
586
|
+
βββββββββββ ββββββββββββ ββββββββββββ
|
|
587
|
+
β β β
|
|
588
|
+
ββββββββββββββββββββΌβββββββββββββββββββ
|
|
589
|
+
βΌ
|
|
590
|
+
ββββββββββββββββββ
|
|
591
|
+
β SQL AST β
|
|
592
|
+
β (Typed Nodes) β
|
|
593
|
+
ββββββββββββββββββ
|
|
594
|
+
βΌ
|
|
595
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
596
|
+
β Strategy Pattern: Dialects β
|
|
597
|
+
β MySQL | PostgreSQL | SQLite | SQL Server β
|
|
598
|
+
ββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
599
|
+
βΌ
|
|
600
|
+
ββββββββββββββββββ
|
|
601
|
+
β Database β
|
|
602
|
+
ββββββββββββββββββ
|
|
603
|
+
```
|
|
532
604
|
|
|
533
|
-
|
|
605
|
+
### Design Patterns
|
|
606
|
+
|
|
607
|
+
- **Strategy Pattern**: Pluggable dialects (MySQL, PostgreSQL, SQLite, SQL Server) and function renderers allow the same query to target different databases
|
|
608
|
+
- **Visitor Pattern**: AST traversal for SQL compilation and expression processing
|
|
609
|
+
- **Builder Pattern**: Fluent query builders (Select, Insert, Update, Delete) for constructing queries step-by-step
|
|
610
|
+
- **Factory Pattern**: Dialect factory and executor creation abstract instantiation logic
|
|
611
|
+
- **Unit of Work**: Change tracking and batch persistence in `OrmSession` coordinate all modifications
|
|
612
|
+
- **Identity Map**: One entity instance per row within a session prevents duplicate object issues
|
|
613
|
+
- **Interceptor/Pipeline**: Query interceptors and flush lifecycle hooks enable cross-cutting concerns
|
|
614
|
+
- **Adapter Pattern**: Connection pooling adapters allow different pool implementations
|
|
615
|
+
|
|
616
|
+
### Type Safety
|
|
617
|
+
|
|
618
|
+
- **Zero `any` types**: The entire src codebase contains zero `any` typesβevery value is properly typed
|
|
619
|
+
- **100% typed public API**: Every public method, parameter, and return value is fully typed
|
|
620
|
+
- **Full type inference**: From schema definition through query building to result hydration
|
|
621
|
+
- **Compile-time safety**: Catch SQL errors at TypeScript compile time, not runtime
|
|
622
|
+
- **Generic-driven**: Leverages TypeScript generics extensively for type propagation
|
|
623
|
+
|
|
624
|
+
### Separation of Concerns
|
|
625
|
+
|
|
626
|
+
Each layer has a clear, focused responsibility:
|
|
627
|
+
|
|
628
|
+
- **Core AST layer**: SQL representation independent of any specific dialect
|
|
629
|
+
- **Dialect layer**: Vendor-specific SQL compilation (MySQL, PostgreSQL, etc.)
|
|
630
|
+
- **Schema layer**: Table and column definitions with relations
|
|
631
|
+
- **Query builder layer**: Fluent API for building type-safe queries
|
|
632
|
+
- **Hydration layer**: Transforms flat result sets into nested object graphs
|
|
633
|
+
- **ORM runtime layer**: Entity management, change tracking, lazy relations, transactions
|
|
634
|
+
|
|
635
|
+
You can use just the layers you need and stay at the low level (AST + dialects) or adopt higher levels when beneficial.
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## Frequently Asked Questions β
|
|
640
|
+
|
|
641
|
+
**Q: How does MetalORM differ from other ORMs?**
|
|
642
|
+
A: MetalORM's unique three-level architecture lets you choose your abstraction levelβuse just the query builder, add the ORM runtime when needed, or go full decorator-based entities. This gradual adoption path is uncommon in the TypeScript ecosystem. You're not locked into an all-or-nothing ORM approach.
|
|
643
|
+
|
|
644
|
+
**Q: Can I use this in production?**
|
|
645
|
+
A: Yes! MetalORM is designed for production use with robust patterns like Unit of Work, Identity Map, and connection pooling support. The type-safe query builder ensures SQL correctness at compile time.
|
|
646
|
+
|
|
647
|
+
**Q: Do I need to use all three levels?**
|
|
648
|
+
A: No! Use only what you need. Many projects stay at Level 1 (query builder) for its type-safe SQL building without any ORM overhead. Add runtime features (Level 2) or decorators (Level 3) only where they provide value.
|
|
649
|
+
|
|
650
|
+
**Q: What about migrations?**
|
|
651
|
+
A: MetalORM provides schema generation via DDL builders. See the [Schema Generation docs](./docs/schema-generation.md) for details on generating CREATE TABLE statements from your table definitions.
|
|
652
|
+
|
|
653
|
+
**Q: How type-safe is it really?**
|
|
654
|
+
A: Exceptionally. The entire codebase contains **zero** `any` typesβevery value is properly typed with TypeScript generics and inference. All public APIs are fully typed, and your queries, entities, and results get full TypeScript checking at compile time.
|
|
655
|
+
|
|
656
|
+
**Q: What design patterns are used?**
|
|
657
|
+
A: MetalORM implements several well-known patterns: Strategy (dialects & functions), Visitor (AST traversal), Builder (query construction), Factory (dialect & executor creation), Unit of Work (change tracking), Identity Map (entity caching), Interceptor (query hooks), and Adapter (pooling). This makes the codebase maintainable and extensible.
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
## Performance & Production π
|
|
662
|
+
|
|
663
|
+
- **Zero runtime overhead for Level 1** (query builder) - it's just SQL compilation and hydration
|
|
664
|
+
- **Efficient batching** for Level 2 lazy relations minimizes database round-trips
|
|
665
|
+
- **Identity Map** prevents duplicate entity instances and unnecessary queries
|
|
666
|
+
- **Connection pooling** supported via executor factory pattern (see [pooling docs](./docs/pooling.md))
|
|
667
|
+
- **Prepared statements** with parameterized queries protect against SQL injection
|
|
668
|
+
|
|
669
|
+
**Production checklist:**
|
|
670
|
+
- β
Use connection pooling for better resource management
|
|
671
|
+
- β
Enable query logging in development for debugging
|
|
672
|
+
- β
Set up proper error handling and retries
|
|
673
|
+
- β
Use transactions for multi-statement operations
|
|
674
|
+
- β
Monitor query performance with interceptors
|
|
675
|
+
|
|
676
|
+
---
|
|
534
677
|
|
|
535
|
-
|
|
536
|
-
- **Separation of concerns**: schema, AST, SQL compilation, execution, and ORM runtime are separate layers.
|
|
537
|
-
- **Executor abstraction**: built-in executor creators (`createMysqlExecutor`, `createPostgresExecutor`, etc.) provide a clean separation between database drivers and ORM operations.
|
|
538
|
-
- **Unit of Work + Identity Map**: `OrmSession` 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.
|
|
539
|
-
- **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).
|
|
678
|
+
## Community & Support π¬
|
|
540
679
|
|
|
541
|
-
|
|
680
|
+
- π **Issues:** [GitHub Issues](https://github.com/celsowm/metal-orm/issues)
|
|
681
|
+
- π‘ **Discussions:** [GitHub Discussions](https://github.com/celsowm/metal-orm/discussions)
|
|
682
|
+
- π **Documentation:** [Full docs](./docs/index.md)
|
|
683
|
+
- πΊοΈ **Roadmap:** [See what's planned](./ROADMAP.md)
|
|
684
|
+
- π¦ **Changelog:** [View releases](https://github.com/celsowm/metal-orm/releases)
|
|
542
685
|
|
|
543
686
|
---
|
|
544
687
|
|