metal-orm 1.0.15 → 1.0.16

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 (43) hide show
  1. package/README.md +34 -27
  2. package/dist/decorators/index.cjs +339 -153
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -5
  5. package/dist/decorators/index.d.ts +1 -5
  6. package/dist/decorators/index.js +339 -153
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +838 -484
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +17 -14
  11. package/dist/index.d.ts +17 -14
  12. package/dist/index.js +833 -483
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-Bkv8g8u_.d.cts → select-BKZrMRCQ.d.cts} +363 -28
  15. package/dist/{select-Bkv8g8u_.d.ts → select-BKZrMRCQ.d.ts} +363 -28
  16. package/package.json +1 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +48 -53
  19. package/src/core/ddl/schema-generator.ts +3 -2
  20. package/src/core/ddl/schema-introspect.ts +1 -1
  21. package/src/core/dialect/abstract.ts +28 -0
  22. package/src/decorators/column.ts +13 -4
  23. package/src/index.ts +8 -1
  24. package/src/orm/entity-context.ts +30 -0
  25. package/src/orm/entity-meta.ts +2 -2
  26. package/src/orm/entity-metadata.ts +1 -6
  27. package/src/orm/entity.ts +88 -88
  28. package/src/orm/execute.ts +42 -25
  29. package/src/orm/execution-context.ts +12 -0
  30. package/src/orm/hydration-context.ts +14 -0
  31. package/src/orm/identity-map.ts +4 -0
  32. package/src/orm/interceptor-pipeline.ts +29 -0
  33. package/src/orm/lazy-batch.ts +6 -6
  34. package/src/orm/orm-session.ts +234 -0
  35. package/src/orm/orm.ts +58 -0
  36. package/src/orm/relation-change-processor.ts +5 -1
  37. package/src/orm/relations/belongs-to.ts +45 -44
  38. package/src/orm/relations/has-many.ts +44 -43
  39. package/src/orm/relations/has-one.ts +140 -139
  40. package/src/orm/relations/many-to-many.ts +46 -45
  41. package/src/orm/unit-of-work.ts +6 -1
  42. package/src/query-builder/select.ts +509 -3
  43. package/src/orm/orm-context.ts +0 -159
package/README.md CHANGED
@@ -6,11 +6,11 @@
6
6
 
7
7
  MetalORM is a TypeScript-first, AST-driven SQL toolkit you can dial up or down depending on how “ORM-y” you want to be:
8
8
 
9
- - **Level 1 – Query builder & hydration 🧩**
9
+ - **Level 1 – Query builder & hydration 🧩**
10
10
  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
- - **Level 2 – ORM runtime (entities + Unit of Work 🧠)**
12
- Let `OrmContext` 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 `saveChanges()`.
13
- - **Level 3 – Decorator entities (classes + metadata ✨)**
11
+ - **Level 2 – ORM runtime (entities + Unit of Work 🧠)**
12
+ 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
+ - **Level 3 – Decorator entities (classes + metadata ✨)**
14
14
  Use `@Entity`, `@Column`, `@PrimaryKey`, relation decorators, `bootstrapEntities()` and `selectFromEntity()` to describe your model classes. MetalORM bootstraps schema & relations from metadata and plugs them into the same runtime and query builder.
15
15
 
16
16
  Use only the layer you need in each part of your codebase.
@@ -76,18 +76,18 @@ Level 1 is ideal when you:
76
76
  - Want deterministic SQL (no magical query generation).
77
77
  - Need to share the same AST across tooling (e.g. codegen, diagnostics, logging).
78
78
 
79
- ### Level 2 – ORM runtime (`OrmContext`)
79
+ ### Level 2 – ORM runtime (`OrmSession`)
80
80
 
81
- On top of the query builder, MetalORM ships a focused runtime:
81
+ On top of the query builder, MetalORM ships a focused runtime managed by `Orm` and its request-scoped `OrmSession`s:
82
82
 
83
83
  - **Entities inferred from your `TableDef`s** (no separate mapping file).
84
84
  - **Lazy, batched relations**: `user.posts.load()`, `user.roles.syncByIds([...])`, etc.
85
- - **Identity map**: the same row becomes the same entity instance within a context (see the [Identity map pattern](https://en.wikipedia.org/wiki/Identity_map_pattern)).
86
- - **Unit of Work (`OrmContext`)** tracking New/Dirty/Removed entities and relation changes, inspired by the classic [Unit of Work pattern](https://en.wikipedia.org/wiki/Unit_of_work).
87
- - **Graph persistence**: mutate a whole object graph and flush once with `ctx.saveChanges()`.
85
+ - **Identity map**: the same row becomes the same entity instance within a session (see the [Identity map pattern](https://en.wikipedia.org/wiki/Identity_map_pattern)).
86
+ - **Unit of Work (`OrmSession`)** tracking New/Dirty/Removed entities and relation changes, inspired by the classic [Unit of Work pattern](https://en.wikipedia.org/wiki/Unit_of_work).
87
+ - **Graph persistence**: mutate a whole object graph and flush once with `session.commit()`.
88
88
  - **Relation change processor** that knows how to deal with has-many and many-to-many pivot tables.
89
89
  - **Interceptors**: `beforeFlush` / `afterFlush` hooks for cross-cutting concerns (auditing, multi-tenant filters, soft delete filters, etc.).
90
- - **Domain events**: `addDomainEvent` and a DomainEventBus integrated into `saveChanges()`, aligned with domain events from [Domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design).
90
+ - **Domain events**: `addDomainEvent` and a DomainEventBus integrated into `session.commit()`, aligned with domain events from [Domain-driven design](https://en.wikipedia.org/wiki/Domain-driven_design).
91
91
  - **JSON-safe entities**: relation wrappers hide internal references and implement `toJSON`, so `JSON.stringify` of hydrated entities works without circular reference errors.
92
92
 
93
93
  Use this layer where:
@@ -298,27 +298,31 @@ Use this mode anywhere you want powerful SQL + nice nested results, without chan
298
298
 
299
299
  When you're ready, you can let MetalORM manage entities and relations for you.
300
300
 
301
- Instead of “naked objects”, your queries can return entities attached to an `OrmContext`:
301
+ Instead of “naked objects”, your queries can return entities attached to an `OrmSession`:
302
302
 
303
303
  ```ts
304
304
  import mysql from 'mysql2/promise';
305
305
  import {
306
- OrmContext,
306
+ Orm,
307
+ OrmSession,
307
308
  MySqlDialect,
308
309
  SelectQueryBuilder,
309
310
  eq,
310
311
  createMysqlExecutor,
311
312
  } from 'metal-orm';
312
313
 
313
- // 1) Create an OrmContext for this request
314
+ // 1) Create an Orm + session for this request
314
315
 
315
316
  const connection = await mysql.createConnection({ /* ... */ });
316
317
  const executor = createMysqlExecutor(connection);
317
-
318
- const ctx = new OrmContext({
318
+ const orm = new Orm({
319
319
  dialect: new MySqlDialect(),
320
- executor,
320
+ executorFactory: {
321
+ createExecutor: () => executor,
322
+ createTransactionalExecutor: () => executor,
323
+ },
321
324
  });
325
+ const session = new OrmSession({ orm, executor });
322
326
 
323
327
  // 2) Load entities with lazy relations
324
328
  const [user] = await new SelectQueryBuilder(users)
@@ -330,7 +334,7 @@ const [user] = await new SelectQueryBuilder(users)
330
334
  .includeLazy('posts') // HasMany as a lazy collection
331
335
  .includeLazy('roles') // BelongsToMany as a lazy collection
332
336
  .where(eq(users.columns.id, 1))
333
- .execute(ctx);
337
+ .execute(session);
334
338
 
335
339
  // user is an Entity<typeof users>
336
340
  // scalar props are normal:
@@ -344,7 +348,7 @@ const newPost = user.posts.add({ title: 'Hello from ORM mode' });
344
348
  await user.roles.syncByIds([1, 2, 3]);
345
349
 
346
350
  // 3) Persist the entire graph
347
- await ctx.saveChanges();
351
+ await session.commit();
348
352
  // INSERT/UPDATE/DELETE + pivot updates happen in a single Unit of Work.
349
353
  ```
350
354
 
@@ -354,7 +358,7 @@ What the runtime gives you:
354
358
  - [Unit of Work](https://en.wikipedia.org/wiki/Unit_of_work) style change tracking on scalar properties.
355
359
  - Relation tracking (add/remove/sync on collections).
356
360
  - Cascades on relations: `'all' | 'persist' | 'remove' | 'link'`.
357
- - Single flush: `ctx.saveChanges()` figures out inserts, updates, deletes, and pivot changes.
361
+ - Single flush: `session.commit()` figures out inserts, updates, deletes, and pivot changes.
358
362
 
359
363
  <a id="level-3"></a>
360
364
  ### Level 3: Decorator entities ✨
@@ -365,7 +369,7 @@ Finally, you can describe your models with decorators and still use the same run
365
369
 
366
370
  ```ts
367
371
  import mysql from 'mysql2/promise';
368
- import { OrmContext, MySqlDialect, col } from 'metal-orm';
372
+ import { Orm, OrmSession, MySqlDialect, col, createMysqlExecutor } from 'metal-orm';
369
373
  import {
370
374
  Entity,
371
375
  Column,
@@ -416,14 +420,17 @@ class Post {
416
420
  const tables = bootstrapEntities();
417
421
  // tables: TableDef[] – compatible with the rest of MetalORM
418
422
 
419
- // 2) Create OrmContext as before
423
+ // 2) Create an Orm + session
420
424
  const connection = await mysql.createConnection({ /* ... */ });
421
425
  const executor = createMysqlExecutor(connection);
422
-
423
- const ctx = new OrmContext({
426
+ const orm = new Orm({
424
427
  dialect: new MySqlDialect(),
425
- executor,
428
+ executorFactory: {
429
+ createExecutor: () => executor,
430
+ createTransactionalExecutor: () => executor,
431
+ },
426
432
  });
433
+ const session = new OrmSession({ orm, executor });
427
434
 
428
435
  // 3) Query starting from the entity class
429
436
  const [user] = await selectFromEntity(User)
@@ -433,10 +440,10 @@ const [user] = await selectFromEntity(User)
433
440
  })
434
441
  .includeLazy('posts')
435
442
  .where(/* same eq()/and() API as before */)
436
- .execute(ctx);
443
+ .execute(session);
437
444
 
438
445
  user.posts.add({ title: 'From decorators' });
439
- await ctx.saveChanges();
446
+ await session.commit();
440
447
  ```
441
448
 
442
449
  This level is nice when:
@@ -470,7 +477,7 @@ Under the hood, MetalORM leans on well-known patterns:
470
477
  - **AST + dialect abstraction**: SQL is modeled as typed AST nodes, compiled by dialects that you can extend.
471
478
  - **Separation of concerns**: schema, AST, SQL compilation, execution, and ORM runtime are separate layers.
472
479
  - **Executor abstraction**: built-in executor creators (`createMysqlExecutor`, `createPostgresExecutor`, etc.) provide a clean separation between database drivers and ORM operations.
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.
480
+ - **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.
474
481
  - **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).
475
482
 
476
483
  You can stay at the low level (just AST + dialects) or adopt the higher levels when it makes your code simpler.