metal-orm 1.1.11 → 1.1.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 (182) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/package.json +100 -96
  5. package/scripts/generate-entities/schema.mjs +122 -122
  6. package/scripts/generate-entities/tree-detection.mjs +92 -92
  7. package/scripts/inflection/pt-br.mjs +468 -468
  8. package/scripts/run-eslint.mjs +33 -34
  9. package/scripts/show-sql-seed.sql +102 -0
  10. package/scripts/show-sql.mjs +321 -325
  11. package/src/codegen/naming-strategy.ts +54 -54
  12. package/src/codegen/typescript.ts +482 -482
  13. package/src/core/ast/adapters.ts +1 -1
  14. package/src/core/ast/builders.ts +123 -123
  15. package/src/core/ast/expression-builders.ts +745 -745
  16. package/src/core/ast/expression-nodes.ts +304 -304
  17. package/src/core/ast/expression-visitor.ts +212 -212
  18. package/src/core/ast/expression.ts +20 -20
  19. package/src/core/ast/helpers.ts +14 -14
  20. package/src/core/ast/join.ts +18 -18
  21. package/src/core/ast/query.ts +200 -200
  22. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  23. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  24. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  25. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  26. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  28. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  29. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  30. package/src/core/ddl/introspect/context.ts +15 -15
  31. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  32. package/src/core/ddl/introspect/registry.ts +40 -40
  33. package/src/core/ddl/introspect/run-select.ts +35 -35
  34. package/src/core/ddl/introspect/utils.ts +56 -56
  35. package/src/core/ddl/naming-strategy.ts +16 -16
  36. package/src/core/ddl/schema-dialect.ts +56 -56
  37. package/src/core/ddl/schema-diff.ts +234 -234
  38. package/src/core/ddl/schema-generator.ts +1 -1
  39. package/src/core/ddl/schema-introspect.ts +25 -25
  40. package/src/core/ddl/sql-writing.ts +171 -171
  41. package/src/core/dialect/abstract.ts +541 -541
  42. package/src/core/dialect/base/cte-compiler.ts +33 -33
  43. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  44. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  45. package/src/core/dialect/base/join-compiler.ts +25 -25
  46. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  47. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  48. package/src/core/dialect/base/returning-strategy.ts +57 -57
  49. package/src/core/dialect/base/sql-dialect.ts +294 -294
  50. package/src/core/dialect/dialect-factory.ts +91 -91
  51. package/src/core/dialect/mssql/functions.ts +120 -120
  52. package/src/core/dialect/mssql/index.ts +317 -317
  53. package/src/core/dialect/mysql/functions.ts +103 -103
  54. package/src/core/dialect/mysql/index.ts +105 -105
  55. package/src/core/dialect/postgres/functions.ts +108 -108
  56. package/src/core/dialect/postgres/index.ts +91 -91
  57. package/src/core/dialect/sqlite/functions.ts +129 -129
  58. package/src/core/dialect/sqlite/index.ts +67 -67
  59. package/src/core/driver/database-driver.ts +19 -19
  60. package/src/core/driver/mssql-driver.ts +23 -23
  61. package/src/core/driver/mysql-driver.ts +23 -23
  62. package/src/core/driver/postgres-driver.ts +23 -23
  63. package/src/core/driver/sqlite-driver.ts +23 -23
  64. package/src/core/execution/db-executor.ts +92 -92
  65. package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
  66. package/src/core/execution/executors/mssql-executor.ts +184 -184
  67. package/src/core/execution/executors/mysql-executor.ts +88 -88
  68. package/src/core/execution/executors/postgres-executor.ts +26 -26
  69. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  70. package/src/core/execution/pooling/pool-types.ts +30 -30
  71. package/src/core/execution/pooling/pool.ts +275 -275
  72. package/src/core/functions/array.ts +35 -35
  73. package/src/core/functions/control-flow.ts +79 -79
  74. package/src/core/functions/datetime.ts +237 -237
  75. package/src/core/functions/json.ts +70 -70
  76. package/src/core/functions/numeric.ts +297 -297
  77. package/src/core/functions/text.ts +334 -334
  78. package/src/core/functions/types.ts +33 -33
  79. package/src/core/hydration/types.ts +51 -51
  80. package/src/core/sql/sql-operator-config.ts +39 -39
  81. package/src/core/sql/sql.ts +152 -152
  82. package/src/decorators/column-decorator.ts +87 -87
  83. package/src/decorators/decorator-metadata.ts +143 -143
  84. package/src/decorators/entity.ts +93 -93
  85. package/src/decorators/index.ts +39 -39
  86. package/src/decorators/relations.ts +231 -231
  87. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  88. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  89. package/src/decorators/transformers/transformer-executor.ts +171 -171
  90. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  91. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  92. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  93. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  94. package/src/decorators/validators/country-validator-registry.ts +64 -64
  95. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  96. package/src/decorators/validators/country-validators.ts +105 -105
  97. package/src/decorators/validators/validator-metadata.ts +59 -59
  98. package/src/dto/dto-types.ts +226 -226
  99. package/src/dto/filter-types.ts +141 -141
  100. package/src/dto/index.ts +95 -95
  101. package/src/dto/openapi/generators/base.ts +29 -29
  102. package/src/dto/openapi/generators/column.ts +37 -37
  103. package/src/dto/openapi/generators/dto.ts +91 -91
  104. package/src/dto/openapi/generators/filter.ts +75 -75
  105. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  106. package/src/dto/openapi/generators/pagination.ts +111 -111
  107. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  108. package/src/dto/openapi/index.ts +12 -12
  109. package/src/dto/openapi/types.ts +101 -101
  110. package/src/dto/openapi/utilities.ts +95 -95
  111. package/src/dto/pagination-utils.ts +150 -150
  112. package/src/dto/transform.ts +197 -197
  113. package/src/index.ts +86 -86
  114. package/src/orm/als.ts +58 -58
  115. package/src/orm/domain-event-bus.ts +119 -119
  116. package/src/orm/entity-context.ts +86 -86
  117. package/src/orm/entity-materializer.ts +159 -159
  118. package/src/orm/entity-meta.ts +97 -97
  119. package/src/orm/entity-registry.ts +39 -39
  120. package/src/orm/entity-relation-cache.ts +39 -39
  121. package/src/orm/execute.ts +194 -194
  122. package/src/orm/execution-context.ts +18 -18
  123. package/src/orm/hydration-context.ts +26 -26
  124. package/src/orm/hydration.ts +112 -112
  125. package/src/orm/identity-map.ts +61 -61
  126. package/src/orm/interceptor-pipeline.ts +16 -16
  127. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  128. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  129. package/src/orm/lazy-batch/has-many.ts +69 -69
  130. package/src/orm/lazy-batch/has-one.ts +68 -68
  131. package/src/orm/lazy-batch/shared.ts +125 -125
  132. package/src/orm/orm-session.ts +620 -620
  133. package/src/orm/orm.ts +34 -34
  134. package/src/orm/pooled-executor-factory.ts +121 -121
  135. package/src/orm/query-logger.ts +40 -40
  136. package/src/orm/relation-change-processor.ts +420 -420
  137. package/src/orm/relations/belongs-to.ts +143 -143
  138. package/src/orm/relations/has-many.ts +204 -204
  139. package/src/orm/relations/has-one.ts +174 -174
  140. package/src/orm/relations/many-to-many.ts +288 -288
  141. package/src/orm/relations/morph-many.ts +156 -156
  142. package/src/orm/relations/morph-one.ts +151 -151
  143. package/src/orm/relations/morph-to.ts +162 -162
  144. package/src/orm/runtime-types.ts +92 -92
  145. package/src/orm/save-graph.ts +561 -561
  146. package/src/orm/transaction-runner.ts +24 -24
  147. package/src/orm/unit-of-work.ts +426 -426
  148. package/src/query/index.ts +69 -69
  149. package/src/query/target.ts +46 -46
  150. package/src/query-builder/column-selector.ts +81 -81
  151. package/src/query-builder/delete.ts +141 -141
  152. package/src/query-builder/insert-query-state.ts +149 -149
  153. package/src/query-builder/insert.ts +117 -117
  154. package/src/query-builder/query-ast-service.ts +287 -287
  155. package/src/query-builder/query-resolution.ts +78 -78
  156. package/src/query-builder/raw-column-parser.ts +38 -38
  157. package/src/query-builder/relation-filter-utils.ts +153 -153
  158. package/src/query-builder/relation-join-planner.ts +10 -10
  159. package/src/query-builder/relation-manager.ts +87 -87
  160. package/src/query-builder/relation-projection-helper.ts +102 -102
  161. package/src/query-builder/relation-types.ts +9 -9
  162. package/src/query-builder/relation-utils.ts +15 -15
  163. package/src/query-builder/select/cte-facet.ts +40 -40
  164. package/src/query-builder/select/from-facet.ts +80 -80
  165. package/src/query-builder/select/join-facet.ts +62 -62
  166. package/src/query-builder/select/predicate-facet.ts +104 -104
  167. package/src/query-builder/select/projection-facet.ts +70 -70
  168. package/src/query-builder/select/relation-facet.ts +81 -81
  169. package/src/query-builder/select/setop-facet.ts +36 -36
  170. package/src/query-builder/select-helpers.ts +1 -1
  171. package/src/query-builder/select-query-builder-deps.ts +130 -130
  172. package/src/query-builder/select-query-state.ts +207 -207
  173. package/src/query-builder/update.ts +151 -151
  174. package/src/schema/column-types.ts +322 -322
  175. package/src/schema/table-guards.ts +37 -37
  176. package/src/schema/table.ts +281 -281
  177. package/src/tree/index.ts +13 -13
  178. package/src/tree/nested-set-strategy.ts +545 -545
  179. package/src/tree/tree-decorator.ts +397 -397
  180. package/src/tree/tree-manager.ts +754 -754
  181. package/src/tree/tree-query.ts +427 -427
  182. package/src/tree/tree-types.ts +299 -299
@@ -1,37 +1,37 @@
1
- import { ColumnDef } from './column-types.js';
2
- import type { TableDef } from './table.js';
3
-
4
- const isColumnsRecord = (columns: unknown): columns is Record<string, ColumnDef> => {
5
- return typeof columns === 'object' && columns !== null;
6
- };
7
-
8
- const isRelationsRecord = (relations: unknown): relations is Record<string, unknown> => {
9
- return typeof relations === 'object' && relations !== null;
10
- };
11
-
12
- /**
13
- * Type guard that checks if a value is a TableDef.
14
- *
15
- * @param value The value to check.
16
- * @returns True if the value follows the TableDef structure.
17
- */
18
- export const isTableDef = (value: unknown): value is TableDef => {
19
- if (typeof value !== 'object' || value === null) {
20
- return false;
21
- }
22
-
23
- const candidate = value as Partial<TableDef>;
24
- if (typeof candidate.name !== 'string') {
25
- return false;
26
- }
27
-
28
- if (!isColumnsRecord(candidate.columns)) {
29
- return false;
30
- }
31
-
32
- if (!isRelationsRecord(candidate.relations)) {
33
- return false;
34
- }
35
-
36
- return true;
37
- };
1
+ import { ColumnDef } from './column-types.js';
2
+ import type { TableDef } from './table.js';
3
+
4
+ const isColumnsRecord = (columns: unknown): columns is Record<string, ColumnDef> => {
5
+ return typeof columns === 'object' && columns !== null;
6
+ };
7
+
8
+ const isRelationsRecord = (relations: unknown): relations is Record<string, unknown> => {
9
+ return typeof relations === 'object' && relations !== null;
10
+ };
11
+
12
+ /**
13
+ * Type guard that checks if a value is a TableDef.
14
+ *
15
+ * @param value The value to check.
16
+ * @returns True if the value follows the TableDef structure.
17
+ */
18
+ export const isTableDef = (value: unknown): value is TableDef => {
19
+ if (typeof value !== 'object' || value === null) {
20
+ return false;
21
+ }
22
+
23
+ const candidate = value as Partial<TableDef>;
24
+ if (typeof candidate.name !== 'string') {
25
+ return false;
26
+ }
27
+
28
+ if (!isColumnsRecord(candidate.columns)) {
29
+ return false;
30
+ }
31
+
32
+ if (!isRelationsRecord(candidate.relations)) {
33
+ return false;
34
+ }
35
+
36
+ return true;
37
+ };
@@ -1,281 +1,281 @@
1
- import type { ColumnDef } from './column-types.js';
2
- import type { RelationDef } from './relation.js';
3
-
4
- export interface IndexColumn {
5
- column: string;
6
- order?: 'ASC' | 'DESC';
7
- nulls?: 'FIRST' | 'LAST';
8
- }
9
-
10
- export interface IndexDef {
11
- name?: string;
12
- columns: (string | IndexColumn)[];
13
- unique?: boolean;
14
- where?: string;
15
- }
16
-
17
- export interface CheckConstraint {
18
- name?: string;
19
- expression: string;
20
- }
21
-
22
- export interface TableOptions {
23
- schema?: string;
24
- primaryKey?: string[];
25
- indexes?: IndexDef[];
26
- checks?: CheckConstraint[];
27
- comment?: string;
28
- engine?: string;
29
- charset?: string;
30
- collation?: string;
31
- }
32
-
33
- export interface TableHooks<TEntity = unknown, TContext = unknown> {
34
- beforeInsert?(ctx: TContext, entity: TEntity): Promise<void> | void;
35
- afterInsert?(ctx: TContext, entity: TEntity): Promise<void> | void;
36
- beforeUpdate?(ctx: TContext, entity: TEntity): Promise<void> | void;
37
- afterUpdate?(ctx: TContext, entity: TEntity): Promise<void> | void;
38
- beforeDelete?(ctx: TContext, entity: TEntity): Promise<void> | void;
39
- afterDelete?(ctx: TContext, entity: TEntity): Promise<void> | void;
40
- }
41
-
42
- /**
43
- * Definition of a database table with its columns and relationships
44
- * @typeParam T - Type of the columns record
45
- */
46
- export interface TableDef<
47
- T extends Record<string, ColumnDef> = Record<string, ColumnDef>,
48
- TEntity = unknown,
49
- TContext = unknown
50
- > {
51
- /** Name of the table */
52
- name: string;
53
- /** Optional schema/catalog name */
54
- schema?: string;
55
- /** Record of column definitions keyed by column name */
56
- columns: T;
57
- /** Record of relationship definitions keyed by relation name */
58
- relations: Record<string, RelationDef>;
59
- /** Optional lifecycle hooks */
60
- hooks?: TableHooks<TEntity, TContext>;
61
- /** Composite primary key definition (falls back to column.primary flags) */
62
- primaryKey?: string[];
63
- /** Secondary indexes */
64
- indexes?: IndexDef[];
65
- /** Table-level check constraints */
66
- checks?: CheckConstraint[];
67
- /** Table comment/description */
68
- comment?: string;
69
- /** Dialect-specific options */
70
- engine?: string;
71
- charset?: string;
72
- collation?: string;
73
- }
74
-
75
- /**
76
- * Creates a table definition with columns and relationships
77
- * @typeParam T - Type of the columns record
78
- * @param name - Name of the table
79
- * @param columns - Record of column definitions
80
- * @param relations - Record of relationship definitions (optional)
81
- * @returns Complete table definition with runtime-filled column metadata
82
- *
83
- * @example
84
- * ```typescript
85
- * const usersTable = defineTable('users', {
86
- * id: col.primaryKey(col.int()),
87
- * name: col.varchar(255),
88
- * email: col.varchar(255)
89
- * });
90
- * ```
91
- */
92
- export const defineTable = <
93
- T extends Record<string, ColumnDef>,
94
- TEntity = unknown,
95
- TContext = unknown
96
- >(
97
- name: string,
98
- columns: T,
99
- relations: Record<string, RelationDef> = {},
100
- hooks?: TableHooks<TEntity, TContext>,
101
- options: TableOptions = {}
102
- ): TableDef<T, TEntity, TContext> => {
103
- // Runtime mutability to assign names to column definitions for convenience
104
- const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
105
- const colDef = { ...def, name: key, table: name };
106
- (acc as Record<string, unknown>)[key] = colDef;
107
- return acc;
108
- }, {} as T);
109
-
110
- return {
111
- name,
112
- schema: options.schema,
113
- columns: colsWithNames,
114
- relations,
115
- hooks,
116
- primaryKey: options.primaryKey,
117
- indexes: options.indexes,
118
- checks: options.checks,
119
- comment: options.comment,
120
- engine: options.engine,
121
- charset: options.charset,
122
- collation: options.collation
123
- };
124
- };
125
-
126
- /**
127
- * Assigns relations to a table definition while preserving literal typing.
128
- */
129
- export function setRelations<
130
- TTable extends TableDef,
131
- TRelations extends Record<string, RelationDef>
132
- >(
133
- table: TTable,
134
- relations: TRelations
135
- ): asserts table is TTable & { relations: TRelations } {
136
- table.relations = relations;
137
- }
138
-
139
- /**
140
- * Adds (or replaces) a single named relation on an existing table definition.
141
- * Safe to call at any time — before or after queries are built.
142
- *
143
- * @param table - The table definition to mutate
144
- * @param name - The relation name (property key used in `.include()` / hydration)
145
- * @param relation - The fully-resolved relation definition
146
- *
147
- * @example
148
- * ```ts
149
- * addRelation(usersTable, 'comments', hasMany(commentsTable, 'user_id'));
150
- * ```
151
- */
152
- export function addRelation<TTable extends TableDef>(
153
- table: TTable,
154
- name: string,
155
- relation: RelationDef
156
- ): void {
157
- table.relations[name] = relation;
158
- }
159
-
160
- type DirectColumnKeys<T extends TableDef> =
161
- Exclude<keyof T["columns"] & string, keyof T | "$">;
162
-
163
- export type TableRef<T extends TableDef> =
164
- T &
165
- { [K in DirectColumnKeys<T>]: T["columns"][K] } & {
166
- /**
167
- * Escape hatch for collisions:
168
- * - tref.name => table name (string)
169
- * - tref.$.name => column def for "name"
170
- */
171
- $: T["columns"];
172
- };
173
-
174
- const TABLE_REF_CACHE: WeakMap<object, unknown> = new WeakMap();
175
-
176
- const withColumnProps = <T extends TableDef>(table: T): TableRef<T> => {
177
- const cached = TABLE_REF_CACHE.get(table);
178
- if (cached) return cached as TableRef<T>;
179
-
180
- const proxy = new Proxy(table as object, {
181
- get(target, prop, receiver) {
182
- const t = target as TableDef;
183
- if (prop === "$") return t.columns;
184
-
185
- // Prefer real table fields first (prevents collision surprises)
186
- if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver);
187
-
188
- // Fall back to columns bag
189
- if (typeof prop === "string" && prop in t.columns) return t.columns[prop];
190
-
191
- return undefined;
192
- },
193
-
194
- has(target, prop) {
195
- const t = target as TableDef;
196
- return (
197
- prop === "$" ||
198
- Reflect.has(target, prop) ||
199
- (typeof prop === "string" && prop in t.columns)
200
- );
201
- },
202
-
203
- ownKeys(target) {
204
- const t = target as TableDef;
205
- const base = Reflect.ownKeys(target);
206
- const cols = Object.keys(t.columns);
207
-
208
- for (const k of cols) {
209
- if (!base.includes(k)) base.push(k);
210
- }
211
- if (!base.includes("$")) base.push("$");
212
- return base;
213
- },
214
-
215
- getOwnPropertyDescriptor(target, prop) {
216
- if (prop === "$") {
217
- return {
218
- configurable: true,
219
- enumerable: false,
220
- get() {
221
- return (target as TableDef).columns;
222
- },
223
- };
224
- }
225
-
226
- if (
227
- typeof prop === "string" &&
228
- prop in (target as TableDef).columns &&
229
- !Reflect.has(target, prop)
230
- ) {
231
- return {
232
- configurable: true,
233
- enumerable: true,
234
- value: (target as TableDef).columns[prop],
235
- writable: false,
236
- };
237
- }
238
-
239
- return Reflect.getOwnPropertyDescriptor(target, prop);
240
- },
241
- }) as TableRef<T>;
242
-
243
- TABLE_REF_CACHE.set(table, proxy);
244
- return proxy;
245
- };
246
-
247
- /**
248
- * Public API: opt-in ergonomic table reference.
249
- * Usage:
250
- * const t = tableRef(todos);
251
- * qb.where(eq(t.done, false)).orderBy(t.id, "ASC");
252
- * Collisions:
253
- * t.name is the table name (real field)
254
- * t.$.name is the "name" column (escape hatch)
255
- */
256
- export const tableRef = <T extends TableDef>(table: T): TableRef<T> => withColumnProps(table);
257
-
258
- /**
259
- * Public API: dynamic column lookup by string key.
260
- *
261
- * Useful when the column name is only known at runtime, or when a column name
262
- * collides with a real table field (e.g. "name").
263
- *
264
- * @example
265
- * ```ts
266
- * const t = tableRef(todos);
267
- * const key = runtimeKey();
268
- * where(eq(getColumn(t, key), 123));
269
- * // or: t.$.name (escape hatch)
270
- * ```
271
- */
272
- export function getColumn<T extends TableDef, K extends keyof T['columns'] & string>(table: T, key: K): T['columns'][K];
273
- export function getColumn<T extends TableDef>(table: T, key: string): ColumnDef;
274
- export function getColumn<T extends TableDef>(table: T, key: string): ColumnDef {
275
- const col = table.columns[key] as ColumnDef | undefined;
276
- if (!col) {
277
- const tableName = table.name || '<unknown>';
278
- throw new Error(`Column '${key}' does not exist on table '${tableName}'`);
279
- }
280
- return col;
281
- }
1
+ import type { ColumnDef } from './column-types.js';
2
+ import type { RelationDef } from './relation.js';
3
+
4
+ export interface IndexColumn {
5
+ column: string;
6
+ order?: 'ASC' | 'DESC';
7
+ nulls?: 'FIRST' | 'LAST';
8
+ }
9
+
10
+ export interface IndexDef {
11
+ name?: string;
12
+ columns: (string | IndexColumn)[];
13
+ unique?: boolean;
14
+ where?: string;
15
+ }
16
+
17
+ export interface CheckConstraint {
18
+ name?: string;
19
+ expression: string;
20
+ }
21
+
22
+ export interface TableOptions {
23
+ schema?: string;
24
+ primaryKey?: string[];
25
+ indexes?: IndexDef[];
26
+ checks?: CheckConstraint[];
27
+ comment?: string;
28
+ engine?: string;
29
+ charset?: string;
30
+ collation?: string;
31
+ }
32
+
33
+ export interface TableHooks<TEntity = unknown, TContext = unknown> {
34
+ beforeInsert?(ctx: TContext, entity: TEntity): Promise<void> | void;
35
+ afterInsert?(ctx: TContext, entity: TEntity): Promise<void> | void;
36
+ beforeUpdate?(ctx: TContext, entity: TEntity): Promise<void> | void;
37
+ afterUpdate?(ctx: TContext, entity: TEntity): Promise<void> | void;
38
+ beforeDelete?(ctx: TContext, entity: TEntity): Promise<void> | void;
39
+ afterDelete?(ctx: TContext, entity: TEntity): Promise<void> | void;
40
+ }
41
+
42
+ /**
43
+ * Definition of a database table with its columns and relationships
44
+ * @typeParam T - Type of the columns record
45
+ */
46
+ export interface TableDef<
47
+ T extends Record<string, ColumnDef> = Record<string, ColumnDef>,
48
+ TEntity = unknown,
49
+ TContext = unknown
50
+ > {
51
+ /** Name of the table */
52
+ name: string;
53
+ /** Optional schema/catalog name */
54
+ schema?: string;
55
+ /** Record of column definitions keyed by column name */
56
+ columns: T;
57
+ /** Record of relationship definitions keyed by relation name */
58
+ relations: Record<string, RelationDef>;
59
+ /** Optional lifecycle hooks */
60
+ hooks?: TableHooks<TEntity, TContext>;
61
+ /** Composite primary key definition (falls back to column.primary flags) */
62
+ primaryKey?: string[];
63
+ /** Secondary indexes */
64
+ indexes?: IndexDef[];
65
+ /** Table-level check constraints */
66
+ checks?: CheckConstraint[];
67
+ /** Table comment/description */
68
+ comment?: string;
69
+ /** Dialect-specific options */
70
+ engine?: string;
71
+ charset?: string;
72
+ collation?: string;
73
+ }
74
+
75
+ /**
76
+ * Creates a table definition with columns and relationships
77
+ * @typeParam T - Type of the columns record
78
+ * @param name - Name of the table
79
+ * @param columns - Record of column definitions
80
+ * @param relations - Record of relationship definitions (optional)
81
+ * @returns Complete table definition with runtime-filled column metadata
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * const usersTable = defineTable('users', {
86
+ * id: col.primaryKey(col.int()),
87
+ * name: col.varchar(255),
88
+ * email: col.varchar(255)
89
+ * });
90
+ * ```
91
+ */
92
+ export const defineTable = <
93
+ T extends Record<string, ColumnDef>,
94
+ TEntity = unknown,
95
+ TContext = unknown
96
+ >(
97
+ name: string,
98
+ columns: T,
99
+ relations: Record<string, RelationDef> = {},
100
+ hooks?: TableHooks<TEntity, TContext>,
101
+ options: TableOptions = {}
102
+ ): TableDef<T, TEntity, TContext> => {
103
+ // Runtime mutability to assign names to column definitions for convenience
104
+ const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
105
+ const colDef = { ...def, name: key, table: name };
106
+ (acc as Record<string, unknown>)[key] = colDef;
107
+ return acc;
108
+ }, {} as T);
109
+
110
+ return {
111
+ name,
112
+ schema: options.schema,
113
+ columns: colsWithNames,
114
+ relations,
115
+ hooks,
116
+ primaryKey: options.primaryKey,
117
+ indexes: options.indexes,
118
+ checks: options.checks,
119
+ comment: options.comment,
120
+ engine: options.engine,
121
+ charset: options.charset,
122
+ collation: options.collation
123
+ };
124
+ };
125
+
126
+ /**
127
+ * Assigns relations to a table definition while preserving literal typing.
128
+ */
129
+ export function setRelations<
130
+ TTable extends TableDef,
131
+ TRelations extends Record<string, RelationDef>
132
+ >(
133
+ table: TTable,
134
+ relations: TRelations
135
+ ): asserts table is TTable & { relations: TRelations } {
136
+ table.relations = relations;
137
+ }
138
+
139
+ /**
140
+ * Adds (or replaces) a single named relation on an existing table definition.
141
+ * Safe to call at any time — before or after queries are built.
142
+ *
143
+ * @param table - The table definition to mutate
144
+ * @param name - The relation name (property key used in `.include()` / hydration)
145
+ * @param relation - The fully-resolved relation definition
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * addRelation(usersTable, 'comments', hasMany(commentsTable, 'user_id'));
150
+ * ```
151
+ */
152
+ export function addRelation<TTable extends TableDef>(
153
+ table: TTable,
154
+ name: string,
155
+ relation: RelationDef
156
+ ): void {
157
+ table.relations[name] = relation;
158
+ }
159
+
160
+ type DirectColumnKeys<T extends TableDef> =
161
+ Exclude<keyof T["columns"] & string, keyof T | "$">;
162
+
163
+ export type TableRef<T extends TableDef> =
164
+ T &
165
+ { [K in DirectColumnKeys<T>]: T["columns"][K] } & {
166
+ /**
167
+ * Escape hatch for collisions:
168
+ * - tref.name => table name (string)
169
+ * - tref.$.name => column def for "name"
170
+ */
171
+ $: T["columns"];
172
+ };
173
+
174
+ const TABLE_REF_CACHE: WeakMap<object, unknown> = new WeakMap();
175
+
176
+ const withColumnProps = <T extends TableDef>(table: T): TableRef<T> => {
177
+ const cached = TABLE_REF_CACHE.get(table);
178
+ if (cached) return cached as TableRef<T>;
179
+
180
+ const proxy = new Proxy(table as object, {
181
+ get(target, prop, receiver) {
182
+ const t = target as TableDef;
183
+ if (prop === "$") return t.columns;
184
+
185
+ // Prefer real table fields first (prevents collision surprises)
186
+ if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver);
187
+
188
+ // Fall back to columns bag
189
+ if (typeof prop === "string" && prop in t.columns) return t.columns[prop];
190
+
191
+ return undefined;
192
+ },
193
+
194
+ has(target, prop) {
195
+ const t = target as TableDef;
196
+ return (
197
+ prop === "$" ||
198
+ Reflect.has(target, prop) ||
199
+ (typeof prop === "string" && prop in t.columns)
200
+ );
201
+ },
202
+
203
+ ownKeys(target) {
204
+ const t = target as TableDef;
205
+ const base = Reflect.ownKeys(target);
206
+ const cols = Object.keys(t.columns);
207
+
208
+ for (const k of cols) {
209
+ if (!base.includes(k)) base.push(k);
210
+ }
211
+ if (!base.includes("$")) base.push("$");
212
+ return base;
213
+ },
214
+
215
+ getOwnPropertyDescriptor(target, prop) {
216
+ if (prop === "$") {
217
+ return {
218
+ configurable: true,
219
+ enumerable: false,
220
+ get() {
221
+ return (target as TableDef).columns;
222
+ },
223
+ };
224
+ }
225
+
226
+ if (
227
+ typeof prop === "string" &&
228
+ prop in (target as TableDef).columns &&
229
+ !Reflect.has(target, prop)
230
+ ) {
231
+ return {
232
+ configurable: true,
233
+ enumerable: true,
234
+ value: (target as TableDef).columns[prop],
235
+ writable: false,
236
+ };
237
+ }
238
+
239
+ return Reflect.getOwnPropertyDescriptor(target, prop);
240
+ },
241
+ }) as TableRef<T>;
242
+
243
+ TABLE_REF_CACHE.set(table, proxy);
244
+ return proxy;
245
+ };
246
+
247
+ /**
248
+ * Public API: opt-in ergonomic table reference.
249
+ * Usage:
250
+ * const t = tableRef(todos);
251
+ * qb.where(eq(t.done, false)).orderBy(t.id, "ASC");
252
+ * Collisions:
253
+ * t.name is the table name (real field)
254
+ * t.$.name is the "name" column (escape hatch)
255
+ */
256
+ export const tableRef = <T extends TableDef>(table: T): TableRef<T> => withColumnProps(table);
257
+
258
+ /**
259
+ * Public API: dynamic column lookup by string key.
260
+ *
261
+ * Useful when the column name is only known at runtime, or when a column name
262
+ * collides with a real table field (e.g. "name").
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * const t = tableRef(todos);
267
+ * const key = runtimeKey();
268
+ * where(eq(getColumn(t, key), 123));
269
+ * // or: t.$.name (escape hatch)
270
+ * ```
271
+ */
272
+ export function getColumn<T extends TableDef, K extends keyof T['columns'] & string>(table: T, key: K): T['columns'][K];
273
+ export function getColumn<T extends TableDef>(table: T, key: string): ColumnDef;
274
+ export function getColumn<T extends TableDef>(table: T, key: string): ColumnDef {
275
+ const col = table.columns[key] as ColumnDef | undefined;
276
+ if (!col) {
277
+ const tableName = table.name || '<unknown>';
278
+ throw new Error(`Column '${key}' does not exist on table '${tableName}'`);
279
+ }
280
+ return col;
281
+ }
package/src/tree/index.ts CHANGED
@@ -1,13 +1,13 @@
1
- /**
2
- * MetalORM Tree Behavior
3
- *
4
- * Provides support for hierarchical data using the Nested Set (MPTT) model.
5
- *
6
- * @module tree
7
- */
8
-
9
- export * from './tree-types.js';
10
- export * from './nested-set-strategy.js';
11
- export * from './tree-query.js';
12
- export * from './tree-manager.js';
13
- export * from './tree-decorator.js';
1
+ /**
2
+ * MetalORM Tree Behavior
3
+ *
4
+ * Provides support for hierarchical data using the Nested Set (MPTT) model.
5
+ *
6
+ * @module tree
7
+ */
8
+
9
+ export * from './tree-types.js';
10
+ export * from './nested-set-strategy.js';
11
+ export * from './tree-query.js';
12
+ export * from './tree-manager.js';
13
+ export * from './tree-decorator.js';