metal-orm 1.0.8 → 1.0.9

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 (153) hide show
  1. package/README.md +12 -1
  2. package/dist/decorators/index.cjs +2564 -0
  3. package/dist/decorators/index.cjs.map +1 -0
  4. package/dist/decorators/index.d.cts +53 -0
  5. package/dist/decorators/index.d.ts +53 -0
  6. package/dist/decorators/index.js +2530 -0
  7. package/dist/decorators/index.js.map +1 -0
  8. package/dist/index.cjs +4227 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +701 -0
  11. package/dist/index.d.ts +701 -0
  12. package/dist/index.js +4131 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/select-654m4qy8.d.cts +1522 -0
  15. package/dist/select-654m4qy8.d.ts +1522 -0
  16. package/package.json +27 -20
  17. package/src/codegen/typescript.ts +405 -393
  18. package/src/core/ast/aggregate-functions.ts +30 -0
  19. package/src/core/ast/builders.ts +43 -0
  20. package/src/core/ast/expression-builders.ts +310 -0
  21. package/src/core/ast/expression-nodes.ts +211 -0
  22. package/src/core/ast/expression-visitor.ts +99 -0
  23. package/src/core/ast/expression.ts +5 -0
  24. package/src/{utils → core/ast}/join-node.ts +20 -20
  25. package/src/{ast → core/ast}/join.ts +18 -18
  26. package/src/{ast → core/ast}/query.ts +113 -113
  27. package/src/core/ast/window-functions.ts +140 -0
  28. package/src/{dialect → core/dialect}/abstract.ts +94 -94
  29. package/src/{dialect → core/dialect}/mssql/index.ts +31 -31
  30. package/src/{dialect → core/dialect}/mysql/index.ts +31 -31
  31. package/src/{dialect → core/dialect}/postgres/index.ts +45 -45
  32. package/src/{dialect → core/dialect}/sqlite/index.ts +45 -45
  33. package/src/{constants → core/sql}/sql-operator-config.ts +39 -39
  34. package/src/decorators/bootstrap.ts +126 -0
  35. package/src/decorators/column.ts +78 -0
  36. package/src/decorators/entity.ts +36 -0
  37. package/src/decorators/index.ts +4 -0
  38. package/src/decorators/relations.ts +107 -0
  39. package/src/global.d.ts +1 -0
  40. package/src/index.ts +22 -22
  41. package/src/orm/db-executor.ts +11 -0
  42. package/src/orm/domain-event-bus.ts +52 -0
  43. package/src/{runtime → orm}/entity-meta.ts +52 -52
  44. package/src/orm/entity-metadata.ts +140 -0
  45. package/src/{runtime → orm}/entity.ts +252 -252
  46. package/src/{runtime → orm}/execute.ts +36 -36
  47. package/src/{runtime → orm}/hydration.ts +103 -103
  48. package/src/orm/identity-map.ts +37 -0
  49. package/src/{runtime → orm}/lazy-batch.ts +205 -205
  50. package/src/orm/orm-context.ts +154 -0
  51. package/src/orm/relation-change-processor.ts +140 -0
  52. package/src/{runtime → orm}/relations/belongs-to.ts +92 -92
  53. package/src/{runtime → orm}/relations/has-many.ts +111 -111
  54. package/src/{runtime → orm}/relations/many-to-many.ts +149 -149
  55. package/src/orm/runtime-types.ts +39 -0
  56. package/src/orm/transaction-runner.ts +17 -0
  57. package/src/orm/unit-of-work.ts +232 -0
  58. package/src/{builder/operations → query-builder}/column-selector.ts +78 -78
  59. package/src/{builder → query-builder}/delete-query-state.ts +38 -42
  60. package/src/{builder → query-builder}/delete.ts +46 -57
  61. package/src/{builder → query-builder}/hydration-manager.ts +87 -87
  62. package/src/{builder → query-builder}/hydration-planner.ts +182 -182
  63. package/src/{builder → query-builder}/insert-query-state.ts +51 -62
  64. package/src/{builder → query-builder}/insert.ts +48 -59
  65. package/src/{builder → query-builder}/query-ast-service.ts +208 -226
  66. package/src/{utils → query-builder}/raw-column-parser.ts +32 -32
  67. package/src/{builder → query-builder}/relation-conditions.ts +112 -112
  68. package/src/{builder/operations → query-builder}/relation-manager.ts +82 -82
  69. package/src/{builder → query-builder}/relation-projection-helper.ts +101 -101
  70. package/src/{builder → query-builder}/relation-service.ts +284 -284
  71. package/src/{builder → query-builder}/relation-types.ts +21 -21
  72. package/src/{builder → query-builder}/relation-utils.ts +12 -12
  73. package/src/{builder → query-builder}/select-query-builder-deps.ts +112 -94
  74. package/src/{builder → query-builder}/select-query-state.ts +179 -179
  75. package/src/{builder → query-builder}/select.ts +78 -69
  76. package/src/{builder → query-builder}/update-query-state.ts +55 -59
  77. package/src/{builder → query-builder}/update.ts +50 -61
  78. package/src/schema/column.ts +25 -25
  79. package/src/schema/relation.ts +116 -116
  80. package/src/schema/table.ts +34 -34
  81. package/src/schema/types.ts +76 -76
  82. package/.github/workflows/publish-metal-orm.yml +0 -38
  83. package/ROADMAP.md +0 -125
  84. package/docs/CHANGES.md +0 -104
  85. package/docs/advanced-features.md +0 -176
  86. package/docs/api-reference.md +0 -31
  87. package/docs/dml-operations.md +0 -156
  88. package/docs/getting-started.md +0 -171
  89. package/docs/hydration.md +0 -115
  90. package/docs/index.md +0 -36
  91. package/docs/multi-dialect-support.md +0 -59
  92. package/docs/query-builder.md +0 -135
  93. package/docs/runtime.md +0 -105
  94. package/docs/schema-definition.md +0 -112
  95. package/metadata.json +0 -5
  96. package/playground/api/playground-api.ts +0 -94
  97. package/playground/index.html +0 -15
  98. package/playground/src/App.css +0 -1
  99. package/playground/src/App.tsx +0 -114
  100. package/playground/src/components/CodeDisplay.tsx +0 -43
  101. package/playground/src/components/QueryExecutor.tsx +0 -189
  102. package/playground/src/components/ResultsTable.tsx +0 -67
  103. package/playground/src/components/ResultsTabs.tsx +0 -105
  104. package/playground/src/components/ScenarioList.tsx +0 -56
  105. package/playground/src/components/logo.svg +0 -45
  106. package/playground/src/data/scenarios.ts +0 -2
  107. package/playground/src/main.tsx +0 -9
  108. package/playground/src/services/PlaygroundApiService.ts +0 -60
  109. package/postcss.config.cjs +0 -5
  110. package/sql_sql-ansi-cheatsheet-2025.md +0 -264
  111. package/src/ast/expression.ts +0 -658
  112. package/src/builder/operations/cte-manager.ts +0 -34
  113. package/src/builder/operations/filter-manager.ts +0 -68
  114. package/src/builder/operations/join-manager.ts +0 -36
  115. package/src/builder/operations/pagination-manager.ts +0 -36
  116. package/src/playground/features/playground/api/types.ts +0 -16
  117. package/src/playground/features/playground/clients/MockClient.ts +0 -17
  118. package/src/playground/features/playground/clients/SqliteClient.ts +0 -57
  119. package/src/playground/features/playground/common/IDatabaseClient.ts +0 -10
  120. package/src/playground/features/playground/data/scenarios/aggregation.ts +0 -36
  121. package/src/playground/features/playground/data/scenarios/basics.ts +0 -25
  122. package/src/playground/features/playground/data/scenarios/edge_cases.ts +0 -57
  123. package/src/playground/features/playground/data/scenarios/filtering.ts +0 -94
  124. package/src/playground/features/playground/data/scenarios/hydration.ts +0 -27
  125. package/src/playground/features/playground/data/scenarios/index.ts +0 -29
  126. package/src/playground/features/playground/data/scenarios/ordering.ts +0 -25
  127. package/src/playground/features/playground/data/scenarios/pagination.ts +0 -16
  128. package/src/playground/features/playground/data/scenarios/relationships.ts +0 -75
  129. package/src/playground/features/playground/data/scenarios/types.ts +0 -70
  130. package/src/playground/features/playground/data/schema.ts +0 -91
  131. package/src/playground/features/playground/data/seed.ts +0 -104
  132. package/src/playground/features/playground/services/QueryExecutionService.ts +0 -121
  133. package/src/runtime/orm-context.ts +0 -539
  134. package/tests/belongs-to-many.test.ts +0 -57
  135. package/tests/between.test.ts +0 -43
  136. package/tests/case-expression.test.ts +0 -58
  137. package/tests/complex-exists.test.ts +0 -230
  138. package/tests/cte.test.ts +0 -118
  139. package/tests/dml.test.ts +0 -206
  140. package/tests/exists.test.ts +0 -127
  141. package/tests/like.test.ts +0 -33
  142. package/tests/orm-runtime.test.ts +0 -254
  143. package/tests/postgres.test.ts +0 -30
  144. package/tests/right-join.test.ts +0 -89
  145. package/tests/subquery-having.test.ts +0 -193
  146. package/tests/window-function.test.ts +0 -151
  147. package/tsconfig.json +0 -30
  148. package/tsup.config.ts +0 -10
  149. package/vite.config.ts +0 -22
  150. package/vitest.config.ts +0 -14
  151. /package/src/{constants → core/sql}/sql.ts +0 -0
  152. /package/src/{runtime → orm}/als.ts +0 -0
  153. /package/src/{utils → query-builder}/relation-alias.ts +0 -0
@@ -0,0 +1,36 @@
1
+ import { TableHooks } from '../schema/table.js';
2
+ import { setEntityTableName } from '../orm/entity-metadata.js';
3
+
4
+ export interface EntityOptions {
5
+ tableName?: string;
6
+ hooks?: TableHooks;
7
+ }
8
+
9
+ const toSnakeCase = (value: string): string => {
10
+ return value
11
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
12
+ .replace(/[^a-z0-9_]+/gi, '_')
13
+ .replace(/__+/g, '_')
14
+ .replace(/^_|_$/g, '')
15
+ .toLowerCase();
16
+ };
17
+
18
+ const deriveTableNameFromConstructor = (ctor: Function): string => {
19
+ const fallback = 'unknown';
20
+ const rawName = ctor.name || fallback;
21
+ const strippedName = rawName.replace(/Entity$/i, '');
22
+ const normalized = toSnakeCase(strippedName || rawName);
23
+ if (!normalized) {
24
+ return fallback;
25
+ }
26
+ return normalized.endsWith('s') ? normalized : `${normalized}s`;
27
+ };
28
+
29
+ export function Entity(options: EntityOptions = {}) {
30
+ const decorator = <T extends new (...args: any[]) => any>(value: T) => {
31
+ const tableName = options.tableName ?? deriveTableNameFromConstructor(value);
32
+ setEntityTableName(value, tableName, options.hooks);
33
+ return value;
34
+ };
35
+ return decorator as unknown as ClassDecorator;
36
+ }
@@ -0,0 +1,4 @@
1
+ export * from './entity.js';
2
+ export * from './column.js';
3
+ export * from './relations.js';
4
+ export * from './bootstrap.js';
@@ -0,0 +1,107 @@
1
+ import { CascadeMode, RelationKinds } from '../schema/relation.js';
2
+ import {
3
+ addRelationMetadata,
4
+ EntityConstructor,
5
+ EntityOrTableTargetResolver,
6
+ RelationMetadata
7
+ } from '../orm/entity-metadata.js';
8
+
9
+ interface BaseRelationOptions {
10
+ target: EntityOrTableTargetResolver;
11
+ cascade?: CascadeMode;
12
+ localKey?: string;
13
+ }
14
+
15
+ export interface HasManyOptions extends BaseRelationOptions {
16
+ foreignKey: string;
17
+ }
18
+
19
+ export interface BelongsToOptions extends BaseRelationOptions {
20
+ foreignKey: string;
21
+ }
22
+
23
+ export interface BelongsToManyOptions {
24
+ target: EntityOrTableTargetResolver;
25
+ pivotTable: EntityOrTableTargetResolver;
26
+ pivotForeignKeyToRoot: string;
27
+ pivotForeignKeyToTarget: string;
28
+ localKey?: string;
29
+ targetKey?: string;
30
+ pivotPrimaryKey?: string;
31
+ defaultPivotColumns?: string[];
32
+ cascade?: CascadeMode;
33
+ }
34
+
35
+ const normalizePropertyName = (name: string | symbol): string => {
36
+ if (typeof name === 'symbol') {
37
+ return name.description ?? name.toString();
38
+ }
39
+ return name;
40
+ };
41
+
42
+ const resolveConstructor = (instanceOrCtor: unknown): EntityConstructor | undefined => {
43
+ if (typeof instanceOrCtor === 'function') {
44
+ return instanceOrCtor as EntityConstructor;
45
+ }
46
+ if (instanceOrCtor && typeof (instanceOrCtor as any).constructor === 'function') {
47
+ return (instanceOrCtor as any).constructor as EntityConstructor;
48
+ }
49
+ return undefined;
50
+ };
51
+
52
+ const registerRelation = (ctor: EntityConstructor, propertyName: string, metadata: RelationMetadata): void => {
53
+ addRelationMetadata(ctor, propertyName, metadata);
54
+ };
55
+
56
+ const createFieldDecorator = (
57
+ metadataFactory: (propertyName: string) => RelationMetadata
58
+ ) => {
59
+ const decorator = (target: object, propertyKey: string | symbol) => {
60
+ const propertyName = normalizePropertyName(propertyKey);
61
+ const ctor = resolveConstructor(target);
62
+ if (!ctor) {
63
+ throw new Error('Unable to resolve constructor when registering relation metadata');
64
+ }
65
+ registerRelation(ctor, propertyName, metadataFactory(propertyName));
66
+ };
67
+
68
+ return decorator as PropertyDecorator;
69
+ };
70
+
71
+ export function HasMany(options: HasManyOptions) {
72
+ return createFieldDecorator(propertyName => ({
73
+ kind: RelationKinds.HasMany,
74
+ propertyKey: propertyName,
75
+ target: options.target,
76
+ foreignKey: options.foreignKey,
77
+ localKey: options.localKey,
78
+ cascade: options.cascade
79
+ }));
80
+ }
81
+
82
+ export function BelongsTo(options: BelongsToOptions) {
83
+ return createFieldDecorator(propertyName => ({
84
+ kind: RelationKinds.BelongsTo,
85
+ propertyKey: propertyName,
86
+ target: options.target,
87
+ foreignKey: options.foreignKey,
88
+ localKey: options.localKey,
89
+ cascade: options.cascade
90
+ }));
91
+ }
92
+
93
+ export function BelongsToMany(options: BelongsToManyOptions) {
94
+ return createFieldDecorator(propertyName => ({
95
+ kind: RelationKinds.BelongsToMany,
96
+ propertyKey: propertyName,
97
+ target: options.target,
98
+ pivotTable: options.pivotTable,
99
+ pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
100
+ pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
101
+ localKey: options.localKey,
102
+ targetKey: options.targetKey,
103
+ pivotPrimaryKey: options.pivotPrimaryKey,
104
+ defaultPivotColumns: options.defaultPivotColumns,
105
+ cascade: options.cascade
106
+ }));
107
+ }
@@ -0,0 +1 @@
1
+ declare module 'tsconfig-paths/register';
package/src/index.ts CHANGED
@@ -1,23 +1,23 @@
1
1
 
2
- export * from './schema/table';
3
- export * from './schema/column';
4
- export * from './schema/relation';
5
- export * from './schema/types';
6
- export * from './builder/select';
7
- export * from './builder/insert';
8
- export * from './builder/update';
9
- export * from './builder/delete';
10
- export * from './ast/expression';
11
- export * from './dialect/mysql';
12
- export * from './dialect/mssql';
13
- export * from './dialect/sqlite';
14
- export * from './runtime/als';
15
- export * from './runtime/hydration';
16
- export * from './codegen/typescript';
17
- export * from './runtime/orm-context';
18
- export * from './runtime/entity';
19
- export * from './runtime/lazy-batch';
20
- export * from './runtime/relations/has-many';
21
- export * from './runtime/relations/belongs-to';
22
- export * from './runtime/relations/many-to-many';
23
- export * from './runtime/execute';
2
+ export * from './schema/table.js';
3
+ export * from './schema/column.js';
4
+ export * from './schema/relation.js';
5
+ export * from './schema/types.js';
6
+ export * from './query-builder/select.js';
7
+ export * from './query-builder/insert.js';
8
+ export * from './query-builder/update.js';
9
+ export * from './query-builder/delete.js';
10
+ export * from './core/ast/expression.js';
11
+ export * from './core/dialect/mysql/index.js';
12
+ export * from './core/dialect/mssql/index.js';
13
+ export * from './core/dialect/sqlite/index.js';
14
+ export * from './orm/als.js';
15
+ export * from './orm/hydration.js';
16
+ export * from './codegen/typescript.js';
17
+ export * from './orm/orm-context.js';
18
+ export * from './orm/entity.js';
19
+ export * from './orm/lazy-batch.js';
20
+ export * from './orm/relations/has-many.js';
21
+ export * from './orm/relations/belongs-to.js';
22
+ export * from './orm/relations/many-to-many.js';
23
+ export * from './orm/execute.js';
@@ -0,0 +1,11 @@
1
+ export type QueryResult = {
2
+ columns: string[];
3
+ values: unknown[][];
4
+ };
5
+
6
+ export interface DbExecutor {
7
+ executeSql(sql: string, params?: unknown[]): Promise<QueryResult[]>;
8
+ beginTransaction?(): Promise<void>;
9
+ commitTransaction?(): Promise<void>;
10
+ rollbackTransaction?(): Promise<void>;
11
+ }
@@ -0,0 +1,52 @@
1
+ import type { HasDomainEvents, TrackedEntity } from './runtime-types.js';
2
+
3
+ export type DomainEventHandler<Context> = (event: any, ctx: Context) => Promise<void> | void;
4
+
5
+ export class DomainEventBus<Context> {
6
+ private readonly handlers = new Map<string, DomainEventHandler<Context>[]>();
7
+
8
+ constructor(initialHandlers?: Record<string, DomainEventHandler<Context>[]>) {
9
+ const handlers = initialHandlers ?? {};
10
+ Object.entries(handlers).forEach(([name, list]) => {
11
+ this.handlers.set(name, [...list]);
12
+ });
13
+ }
14
+
15
+ register(name: string, handler: DomainEventHandler<Context>): void {
16
+ const existing = this.handlers.get(name) ?? [];
17
+ existing.push(handler);
18
+ this.handlers.set(name, existing);
19
+ }
20
+
21
+ async dispatch(trackedEntities: Iterable<TrackedEntity>, ctx: Context): Promise<void> {
22
+ for (const tracked of trackedEntities) {
23
+ const entity = tracked.entity as HasDomainEvents;
24
+ if (!entity.domainEvents || !entity.domainEvents.length) continue;
25
+
26
+ for (const event of entity.domainEvents) {
27
+ const eventName = this.getEventName(event);
28
+ const handlers = this.handlers.get(eventName);
29
+ if (!handlers) continue;
30
+
31
+ for (const handler of handlers) {
32
+ await handler(event, ctx);
33
+ }
34
+ }
35
+
36
+ entity.domainEvents = [];
37
+ }
38
+ }
39
+
40
+ private getEventName(event: any): string {
41
+ if (!event) return 'Unknown';
42
+ if (typeof event === 'string') return event;
43
+ return event.constructor?.name ?? 'Unknown';
44
+ }
45
+ }
46
+
47
+ export const addDomainEvent = (entity: HasDomainEvents, event: any): void => {
48
+ if (!entity.domainEvents) {
49
+ entity.domainEvents = [];
50
+ }
51
+ entity.domainEvents.push(event);
52
+ };
@@ -1,52 +1,52 @@
1
- import { TableDef } from '../schema/table';
2
- import { OrmContext } from './orm-context';
3
- import { RelationMap } from '../schema/types';
4
-
5
- export const ENTITY_META = Symbol('EntityMeta');
6
-
7
- const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
8
-
9
- export interface EntityMeta<TTable extends TableDef> {
10
- ctx: OrmContext;
11
- table: TTable;
12
- lazyRelations: (keyof RelationMap<TTable>)[];
13
- relationCache: Map<string, Promise<any>>;
14
- relationHydration: Map<string, Map<string, any>>;
15
- relationWrappers: Map<string, unknown>;
16
- }
17
-
18
- export const getHydrationRows = <TTable extends TableDef>(
19
- meta: EntityMeta<TTable>,
20
- relationName: string,
21
- key: unknown
22
- ): Record<string, any>[] | undefined => {
23
- const map = meta.relationHydration.get(relationName);
24
- if (!map) return undefined;
25
- const rows = map.get(toKey(key));
26
- if (!rows) return undefined;
27
- return Array.isArray(rows) ? rows : undefined;
28
- };
29
-
30
- export const getHydrationRecord = <TTable extends TableDef>(
31
- meta: EntityMeta<TTable>,
32
- relationName: string,
33
- key: unknown
34
- ): Record<string, any> | undefined => {
35
- const map = meta.relationHydration.get(relationName);
36
- if (!map) return undefined;
37
- const value = map.get(toKey(key));
38
- if (!value) return undefined;
39
- if (Array.isArray(value)) {
40
- return value[0];
41
- }
42
- return value;
43
- };
44
-
45
- export const getEntityMeta = <TTable extends TableDef>(entity: any): EntityMeta<TTable> | undefined => {
46
- if (!entity || typeof entity !== 'object') return undefined;
47
- return (entity as any)[ENTITY_META];
48
- };
49
-
50
- export const hasEntityMeta = (entity: any): entity is { [ENTITY_META]: EntityMeta<TableDef> } => {
51
- return Boolean(getEntityMeta(entity));
52
- };
1
+ import { TableDef } from '../schema/table.js';
2
+ import { OrmContext } from './orm-context.js';
3
+ import { RelationMap } from '../schema/types.js';
4
+
5
+ export const ENTITY_META = Symbol('EntityMeta');
6
+
7
+ const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
8
+
9
+ export interface EntityMeta<TTable extends TableDef> {
10
+ ctx: OrmContext;
11
+ table: TTable;
12
+ lazyRelations: (keyof RelationMap<TTable>)[];
13
+ relationCache: Map<string, Promise<any>>;
14
+ relationHydration: Map<string, Map<string, any>>;
15
+ relationWrappers: Map<string, unknown>;
16
+ }
17
+
18
+ export const getHydrationRows = <TTable extends TableDef>(
19
+ meta: EntityMeta<TTable>,
20
+ relationName: string,
21
+ key: unknown
22
+ ): Record<string, any>[] | undefined => {
23
+ const map = meta.relationHydration.get(relationName);
24
+ if (!map) return undefined;
25
+ const rows = map.get(toKey(key));
26
+ if (!rows) return undefined;
27
+ return Array.isArray(rows) ? rows : undefined;
28
+ };
29
+
30
+ export const getHydrationRecord = <TTable extends TableDef>(
31
+ meta: EntityMeta<TTable>,
32
+ relationName: string,
33
+ key: unknown
34
+ ): Record<string, any> | undefined => {
35
+ const map = meta.relationHydration.get(relationName);
36
+ if (!map) return undefined;
37
+ const value = map.get(toKey(key));
38
+ if (!value) return undefined;
39
+ if (Array.isArray(value)) {
40
+ return value[0];
41
+ }
42
+ return value;
43
+ };
44
+
45
+ export const getEntityMeta = <TTable extends TableDef>(entity: any): EntityMeta<TTable> | undefined => {
46
+ if (!entity || typeof entity !== 'object') return undefined;
47
+ return (entity as any)[ENTITY_META];
48
+ };
49
+
50
+ export const hasEntityMeta = (entity: any): entity is { [ENTITY_META]: EntityMeta<TableDef> } => {
51
+ return Boolean(getEntityMeta(entity));
52
+ };
@@ -0,0 +1,140 @@
1
+ import { ColumnType, ColumnDef } from '../schema/column.js';
2
+ import { defineTable, TableDef, TableHooks } from '../schema/table.js';
3
+ import { CascadeMode, RelationKinds } from '../schema/relation.js';
4
+
5
+ export type EntityConstructor = new (...args: any[]) => any;
6
+ export type EntityOrTableTarget = EntityConstructor | TableDef;
7
+ export type EntityOrTableTargetResolver = EntityOrTableTarget | (() => EntityOrTableTarget);
8
+
9
+ export interface ColumnDefLike {
10
+ type: ColumnType;
11
+ args?: ColumnDef['args'];
12
+ primary?: boolean;
13
+ notNull?: boolean;
14
+ }
15
+
16
+ interface BaseRelationMetadata {
17
+ propertyKey: string;
18
+ target: EntityOrTableTargetResolver;
19
+ cascade?: CascadeMode;
20
+ }
21
+
22
+ export interface HasManyRelationMetadata extends BaseRelationMetadata {
23
+ kind: typeof RelationKinds.HasMany;
24
+ foreignKey: string;
25
+ localKey?: string;
26
+ }
27
+
28
+ export interface BelongsToRelationMetadata extends BaseRelationMetadata {
29
+ kind: typeof RelationKinds.BelongsTo;
30
+ foreignKey: string;
31
+ localKey?: string;
32
+ }
33
+
34
+ export interface BelongsToManyRelationMetadata extends BaseRelationMetadata {
35
+ kind: typeof RelationKinds.BelongsToMany;
36
+ pivotTable: EntityOrTableTargetResolver;
37
+ pivotForeignKeyToRoot: string;
38
+ pivotForeignKeyToTarget: string;
39
+ localKey?: string;
40
+ targetKey?: string;
41
+ pivotPrimaryKey?: string;
42
+ defaultPivotColumns?: string[];
43
+ }
44
+
45
+ export type RelationMetadata =
46
+ | HasManyRelationMetadata
47
+ | BelongsToRelationMetadata
48
+ | BelongsToManyRelationMetadata;
49
+
50
+ export interface EntityMetadata {
51
+ target: EntityConstructor;
52
+ tableName: string;
53
+ columns: Record<string, ColumnDefLike>;
54
+ relations: Record<string, RelationMetadata>;
55
+ hooks?: TableHooks;
56
+ table?: TableDef;
57
+ }
58
+
59
+ const metadataMap = new Map<EntityConstructor, EntityMetadata>();
60
+
61
+ export const registerEntityMetadata = (meta: EntityMetadata): void => {
62
+ metadataMap.set(meta.target, meta);
63
+ };
64
+
65
+ export const ensureEntityMetadata = (target: EntityConstructor): EntityMetadata => {
66
+ let meta = metadataMap.get(target);
67
+ if (!meta) {
68
+ meta = {
69
+ target,
70
+ tableName: target.name || 'unknown',
71
+ columns: {},
72
+ relations: {}
73
+ };
74
+ metadataMap.set(target, meta);
75
+ }
76
+ return meta;
77
+ };
78
+
79
+ export const getEntityMetadata = (target: EntityConstructor): EntityMetadata | undefined => {
80
+ return metadataMap.get(target);
81
+ };
82
+
83
+ export const getAllEntityMetadata = (): EntityMetadata[] => {
84
+ return Array.from(metadataMap.values());
85
+ };
86
+
87
+ export const clearEntityMetadata = (): void => {
88
+ metadataMap.clear();
89
+ };
90
+
91
+ export const addColumnMetadata = (
92
+ target: EntityConstructor,
93
+ propertyKey: string,
94
+ column: ColumnDefLike
95
+ ): void => {
96
+ const meta = ensureEntityMetadata(target);
97
+ meta.columns[propertyKey] = { ...column };
98
+ };
99
+
100
+ export const addRelationMetadata = (
101
+ target: EntityConstructor,
102
+ propertyKey: string,
103
+ relation: RelationMetadata
104
+ ): void => {
105
+ const meta = ensureEntityMetadata(target);
106
+ meta.relations[propertyKey] = relation;
107
+ };
108
+
109
+ export const setEntityTableName = (
110
+ target: EntityConstructor,
111
+ tableName: string,
112
+ hooks?: TableHooks
113
+ ): void => {
114
+ const meta = ensureEntityMetadata(target);
115
+ if (tableName && tableName.length > 0) {
116
+ meta.tableName = tableName;
117
+ }
118
+ if (hooks) {
119
+ meta.hooks = hooks;
120
+ }
121
+ };
122
+
123
+ export const buildTableDef = (meta: EntityMetadata): TableDef => {
124
+ if (meta.table) {
125
+ return meta.table;
126
+ }
127
+
128
+ const columns = Object.entries(meta.columns).reduce<Record<string, ColumnDef>>((acc, [key, def]) => {
129
+ acc[key] = {
130
+ ...def,
131
+ name: key,
132
+ table: meta.tableName
133
+ };
134
+ return acc;
135
+ }, {});
136
+
137
+ const table = defineTable(meta.tableName, columns, {}, meta.hooks);
138
+ meta.table = table;
139
+ return table;
140
+ };