metal-orm 1.0.15 → 1.0.17

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 (63) hide show
  1. package/README.md +64 -61
  2. package/dist/decorators/index.cjs +490 -175
  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 +490 -175
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +1044 -483
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +67 -15
  11. package/dist/index.d.ts +67 -15
  12. package/dist/index.js +1033 -482
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-Bkv8g8u_.d.cts → select-BPCn6MOH.d.cts} +486 -32
  15. package/dist/{select-Bkv8g8u_.d.ts → select-BPCn6MOH.d.ts} +486 -32
  16. package/package.json +2 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +48 -53
  19. package/src/core/ast/aggregate-functions.ts +50 -4
  20. package/src/core/ast/expression-builders.ts +22 -15
  21. package/src/core/ast/expression-nodes.ts +6 -0
  22. package/src/core/ddl/introspect/functions/postgres.ts +2 -6
  23. package/src/core/ddl/schema-generator.ts +3 -2
  24. package/src/core/ddl/schema-introspect.ts +1 -1
  25. package/src/core/dialect/abstract.ts +40 -8
  26. package/src/core/dialect/mssql/functions.ts +24 -15
  27. package/src/core/dialect/postgres/functions.ts +33 -24
  28. package/src/core/dialect/sqlite/functions.ts +19 -12
  29. package/src/core/functions/datetime.ts +2 -1
  30. package/src/core/functions/numeric.ts +2 -1
  31. package/src/core/functions/standard-strategy.ts +52 -12
  32. package/src/core/functions/text.ts +2 -1
  33. package/src/core/functions/types.ts +8 -8
  34. package/src/decorators/column.ts +13 -4
  35. package/src/index.ts +13 -5
  36. package/src/orm/domain-event-bus.ts +43 -25
  37. package/src/orm/entity-context.ts +30 -0
  38. package/src/orm/entity-meta.ts +42 -2
  39. package/src/orm/entity-metadata.ts +1 -6
  40. package/src/orm/entity.ts +88 -88
  41. package/src/orm/execute.ts +42 -25
  42. package/src/orm/execution-context.ts +18 -0
  43. package/src/orm/hydration-context.ts +16 -0
  44. package/src/orm/identity-map.ts +4 -0
  45. package/src/orm/interceptor-pipeline.ts +29 -0
  46. package/src/orm/lazy-batch.ts +6 -6
  47. package/src/orm/orm-session.ts +245 -0
  48. package/src/orm/orm.ts +58 -0
  49. package/src/orm/query-logger.ts +15 -0
  50. package/src/orm/relation-change-processor.ts +5 -1
  51. package/src/orm/relations/belongs-to.ts +45 -44
  52. package/src/orm/relations/has-many.ts +44 -43
  53. package/src/orm/relations/has-one.ts +140 -139
  54. package/src/orm/relations/many-to-many.ts +46 -45
  55. package/src/orm/runtime-types.ts +60 -2
  56. package/src/orm/transaction-runner.ts +7 -0
  57. package/src/orm/unit-of-work.ts +7 -1
  58. package/src/query-builder/insert-query-state.ts +13 -3
  59. package/src/query-builder/select-helpers.ts +50 -0
  60. package/src/query-builder/select.ts +616 -18
  61. package/src/query-builder/update-query-state.ts +31 -9
  62. package/src/schema/types.ts +16 -6
  63. package/src/orm/orm-context.ts +0 -159
@@ -1,7 +1,21 @@
1
1
  import { TableDef } from '../schema/table.js';
2
- import { ColumnNode, ExpressionNode, valueToOperand } from '../core/ast/expression.js';
2
+ import { ColumnNode, ExpressionNode, OperandNode, isOperandNode, valueToOperand } from '../core/ast/expression.js';
3
3
  import { TableNode, UpdateQueryNode, UpdateAssignmentNode } from '../core/ast/query.js';
4
4
  import { createTableNode } from '../core/ast/builders.js';
5
+ type LiteralValue = string | number | boolean | null;
6
+ type UpdateValue = OperandNode | LiteralValue;
7
+
8
+ const isUpdateValue = (value: unknown): value is UpdateValue => {
9
+ if (value === null) return true;
10
+ switch (typeof value) {
11
+ case 'string':
12
+ case 'number':
13
+ case 'boolean':
14
+ return true;
15
+ default:
16
+ return isOperandNode(value);
17
+ }
18
+ };
5
19
 
6
20
  /**
7
21
  * Immutable state for UPDATE queries
@@ -24,14 +38,22 @@ export class UpdateQueryState {
24
38
  }
25
39
 
26
40
  withSet(values: Record<string, unknown>): UpdateQueryState {
27
- const assignments: UpdateAssignmentNode[] = Object.entries(values).map(([column, value]) => ({
28
- column: {
29
- type: 'Column',
30
- table: this.table.name,
31
- name: column
32
- },
33
- value: valueToOperand(value)
34
- }));
41
+ const assignments: UpdateAssignmentNode[] = Object.entries(values).map(([column, rawValue]) => {
42
+ if (!isUpdateValue(rawValue)) {
43
+ throw new Error(
44
+ `Invalid update value for column "${column}": only primitives, null, or OperandNodes are allowed`
45
+ );
46
+ }
47
+
48
+ return {
49
+ column: {
50
+ type: 'Column',
51
+ table: this.table.name,
52
+ name: column
53
+ },
54
+ value: valueToOperand(rawValue)
55
+ };
56
+ });
35
57
 
36
58
  return this.clone({
37
59
  ...this.ast,
@@ -1,5 +1,5 @@
1
- import { ColumnDef } from './column.js';
2
- import { TableDef } from './table.js';
1
+ import { ColumnDef } from './column.js';
2
+ import { TableDef } from './table.js';
3
3
  import {
4
4
  RelationDef,
5
5
  HasManyRelation,
@@ -7,10 +7,20 @@ import {
7
7
  BelongsToRelation,
8
8
  BelongsToManyRelation
9
9
  } from './relation.js';
10
-
11
- /**
12
- * Maps a ColumnDef to its TypeScript type representation
13
- */
10
+
11
+ /**
12
+ * Resolves a relation definition to its target table type.
13
+ */
14
+ export type RelationTargetTable<TRel extends RelationDef> =
15
+ TRel extends HasManyRelation<infer TTarget> ? TTarget :
16
+ TRel extends HasOneRelation<infer TTarget> ? TTarget :
17
+ TRel extends BelongsToRelation<infer TTarget> ? TTarget :
18
+ TRel extends BelongsToManyRelation<infer TTarget> ? TTarget :
19
+ never;
20
+
21
+ /**
22
+ * Maps a ColumnDef to its TypeScript type representation
23
+ */
14
24
  export type ColumnToTs<T extends ColumnDef> =
15
25
  T['type'] extends 'INT' | 'INTEGER' | 'int' | 'integer' ? number :
16
26
  T['type'] extends 'BIGINT' | 'bigint' ? number | bigint :
@@ -1,159 +0,0 @@
1
- import type { Dialect } from '../core/dialect/abstract.js';
2
- import type { RelationDef } from '../schema/relation.js';
3
- import type { TableDef } from '../schema/table.js';
4
- import type { DbExecutor, QueryResult } from '../core/execution/db-executor.js';
5
- import { DomainEventBus, DomainEventHandler as DomainEventHandlerFn, addDomainEvent } from './domain-event-bus.js';
6
- import { IdentityMap } from './identity-map.js';
7
- import { RelationChangeProcessor } from './relation-change-processor.js';
8
- import { runInTransaction } from './transaction-runner.js';
9
- import { UnitOfWork } from './unit-of-work.js';
10
- import {
11
- EntityStatus,
12
- HasDomainEvents,
13
- RelationChange,
14
- RelationChangeEntry,
15
- RelationKey,
16
- TrackedEntity
17
- } from './runtime-types.js';
18
- import { createQueryLoggingExecutor, QueryLogger } from './query-logger.js';
19
-
20
- export interface OrmInterceptor {
21
- beforeFlush?(ctx: OrmContext): Promise<void> | void;
22
- afterFlush?(ctx: OrmContext): Promise<void> | void;
23
- }
24
-
25
- export type DomainEventHandler = DomainEventHandlerFn<OrmContext>;
26
-
27
- export interface OrmContextOptions {
28
- dialect: Dialect;
29
- executor: DbExecutor;
30
- interceptors?: OrmInterceptor[];
31
- domainEventHandlers?: Record<string, DomainEventHandler[]>;
32
- queryLogger?: QueryLogger;
33
- }
34
-
35
- export class OrmContext {
36
- private readonly identityMap = new IdentityMap();
37
- private readonly executorWithLogging: DbExecutor;
38
- private readonly unitOfWork: UnitOfWork;
39
- private readonly relationChanges: RelationChangeProcessor;
40
- private readonly interceptors: OrmInterceptor[];
41
- private readonly domainEvents: DomainEventBus<OrmContext>;
42
-
43
- constructor(private readonly options: OrmContextOptions) {
44
- this.interceptors = [...(options.interceptors ?? [])];
45
- this.executorWithLogging = createQueryLoggingExecutor(options.executor, options.queryLogger);
46
- this.unitOfWork = new UnitOfWork(
47
- options.dialect,
48
- this.executorWithLogging,
49
- this.identityMap,
50
- () => this
51
- );
52
- this.relationChanges = new RelationChangeProcessor(
53
- this.unitOfWork,
54
- options.dialect,
55
- this.executorWithLogging
56
- );
57
- this.domainEvents = new DomainEventBus<OrmContext>(options.domainEventHandlers);
58
- }
59
-
60
- get dialect(): Dialect {
61
- return this.options.dialect;
62
- }
63
-
64
- get executor(): DbExecutor {
65
- return this.executorWithLogging;
66
- }
67
-
68
- get identityBuckets(): Map<string, Map<string, TrackedEntity>> {
69
- return this.unitOfWork.identityBuckets;
70
- }
71
-
72
- get tracked(): TrackedEntity[] {
73
- return this.unitOfWork.getTracked();
74
- }
75
-
76
- getEntity(table: TableDef, pk: string | number): any | undefined {
77
- return this.unitOfWork.getEntity(table, pk);
78
- }
79
-
80
- setEntity(table: TableDef, pk: string | number, entity: any): void {
81
- this.unitOfWork.setEntity(table, pk, entity);
82
- }
83
-
84
- trackNew(table: TableDef, entity: any, pk?: string | number): void {
85
- this.unitOfWork.trackNew(table, entity, pk);
86
- }
87
-
88
- trackManaged(table: TableDef, pk: string | number, entity: any): void {
89
- this.unitOfWork.trackManaged(table, pk, entity);
90
- }
91
-
92
- markDirty(entity: any): void {
93
- this.unitOfWork.markDirty(entity);
94
- }
95
-
96
- markRemoved(entity: any): void {
97
- this.unitOfWork.markRemoved(entity);
98
- }
99
-
100
- registerRelationChange(
101
- root: any,
102
- relationKey: RelationKey,
103
- rootTable: TableDef,
104
- relationName: string,
105
- relation: RelationDef,
106
- change: RelationChange<any>
107
- ): void {
108
- const entry: RelationChangeEntry = {
109
- root,
110
- relationKey,
111
- rootTable,
112
- relationName,
113
- relation,
114
- change
115
- };
116
- this.relationChanges.registerChange(entry);
117
- }
118
-
119
- registerInterceptor(interceptor: OrmInterceptor): void {
120
- this.interceptors.push(interceptor);
121
- }
122
-
123
- registerDomainEventHandler(name: string, handler: DomainEventHandler): void {
124
- this.domainEvents.register(name, handler);
125
- }
126
-
127
- async saveChanges(): Promise<void> {
128
- await runInTransaction(this.executor, async () => {
129
- for (const interceptor of this.interceptors) {
130
- await interceptor.beforeFlush?.(this);
131
- }
132
-
133
- await this.unitOfWork.flush();
134
- await this.relationChanges.process();
135
- await this.unitOfWork.flush();
136
-
137
- for (const interceptor of this.interceptors) {
138
- await interceptor.afterFlush?.(this);
139
- }
140
- });
141
-
142
- await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
143
- }
144
-
145
- getEntitiesForTable(table: TableDef): TrackedEntity[] {
146
- return this.unitOfWork.getEntitiesForTable(table);
147
- }
148
- }
149
-
150
- export { addDomainEvent };
151
- export { EntityStatus };
152
- export type {
153
- QueryResult,
154
- DbExecutor,
155
- RelationKey,
156
- RelationChange,
157
- HasDomainEvents
158
- };
159
- export type { QueryLogEntry, QueryLogger } from './query-logger.js';