metal-orm 1.1.10 → 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 (192) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs +106 -43
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +5 -5
  5. package/dist/index.d.ts +5 -5
  6. package/dist/index.js +106 -43
  7. package/dist/index.js.map +1 -1
  8. package/package.json +100 -96
  9. package/scripts/generate-entities/render.mjs +21 -12
  10. package/scripts/generate-entities/schema.mjs +209 -195
  11. package/scripts/generate-entities/tree-detection.mjs +159 -153
  12. package/scripts/inflection/pt-br.mjs +468 -468
  13. package/scripts/run-eslint.mjs +33 -34
  14. package/scripts/show-sql-seed.sql +102 -0
  15. package/scripts/show-sql.mjs +321 -325
  16. package/src/bulk/bulk-delete-executor.ts +3 -5
  17. package/src/bulk/bulk-insert-executor.ts +7 -7
  18. package/src/bulk/bulk-update-executor.ts +2 -2
  19. package/src/bulk/bulk-upsert-executor.ts +5 -7
  20. package/src/codegen/naming-strategy.ts +54 -54
  21. package/src/codegen/typescript.ts +482 -482
  22. package/src/core/ast/adapters.ts +1 -1
  23. package/src/core/ast/builders.ts +123 -123
  24. package/src/core/ast/expression-builders.ts +745 -745
  25. package/src/core/ast/expression-nodes.ts +304 -304
  26. package/src/core/ast/expression-visitor.ts +212 -212
  27. package/src/core/ast/expression.ts +20 -20
  28. package/src/core/ast/helpers.ts +14 -14
  29. package/src/core/ast/join.ts +18 -18
  30. package/src/core/ast/query.ts +200 -200
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  32. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  33. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  34. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  35. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  37. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  39. package/src/core/ddl/introspect/context.ts +15 -15
  40. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  41. package/src/core/ddl/introspect/mysql.ts +113 -36
  42. package/src/core/ddl/introspect/registry.ts +40 -40
  43. package/src/core/ddl/introspect/run-select.ts +35 -35
  44. package/src/core/ddl/introspect/utils.ts +56 -56
  45. package/src/core/ddl/naming-strategy.ts +16 -16
  46. package/src/core/ddl/schema-dialect.ts +56 -56
  47. package/src/core/ddl/schema-diff.ts +234 -234
  48. package/src/core/ddl/schema-generator.ts +1 -1
  49. package/src/core/ddl/schema-introspect.ts +25 -25
  50. package/src/core/ddl/sql-writing.ts +171 -171
  51. package/src/core/dialect/abstract.ts +541 -541
  52. package/src/core/dialect/base/cte-compiler.ts +33 -33
  53. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  54. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  55. package/src/core/dialect/base/join-compiler.ts +25 -25
  56. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  57. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  58. package/src/core/dialect/base/returning-strategy.ts +57 -57
  59. package/src/core/dialect/base/sql-dialect.ts +294 -294
  60. package/src/core/dialect/dialect-factory.ts +91 -91
  61. package/src/core/dialect/mssql/functions.ts +120 -120
  62. package/src/core/dialect/mssql/index.ts +317 -317
  63. package/src/core/dialect/mysql/functions.ts +103 -103
  64. package/src/core/dialect/mysql/index.ts +105 -105
  65. package/src/core/dialect/postgres/functions.ts +108 -108
  66. package/src/core/dialect/postgres/index.ts +91 -91
  67. package/src/core/dialect/sqlite/functions.ts +129 -129
  68. package/src/core/dialect/sqlite/index.ts +67 -67
  69. package/src/core/driver/database-driver.ts +19 -19
  70. package/src/core/driver/mssql-driver.ts +23 -23
  71. package/src/core/driver/mysql-driver.ts +23 -23
  72. package/src/core/driver/postgres-driver.ts +23 -23
  73. package/src/core/driver/sqlite-driver.ts +23 -23
  74. package/src/core/execution/db-executor.ts +92 -92
  75. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
  76. package/src/core/execution/executors/mssql-executor.ts +184 -184
  77. package/src/core/execution/executors/mysql-executor.ts +88 -88
  78. package/src/core/execution/executors/postgres-executor.ts +26 -26
  79. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  80. package/src/core/execution/pooling/pool-types.ts +30 -30
  81. package/src/core/execution/pooling/pool.ts +275 -275
  82. package/src/core/functions/array.ts +35 -35
  83. package/src/core/functions/control-flow.ts +79 -79
  84. package/src/core/functions/datetime.ts +237 -237
  85. package/src/core/functions/json.ts +70 -70
  86. package/src/core/functions/numeric.ts +297 -297
  87. package/src/core/functions/text.ts +334 -334
  88. package/src/core/functions/types.ts +33 -33
  89. package/src/core/hydration/types.ts +51 -51
  90. package/src/core/sql/sql-operator-config.ts +39 -39
  91. package/src/core/sql/sql.ts +152 -152
  92. package/src/decorators/column-decorator.ts +87 -87
  93. package/src/decorators/decorator-metadata.ts +143 -143
  94. package/src/decorators/entity.ts +93 -93
  95. package/src/decorators/index.ts +39 -39
  96. package/src/decorators/relations.ts +231 -231
  97. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  98. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  99. package/src/decorators/transformers/transformer-executor.ts +171 -171
  100. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  101. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  102. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  103. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  104. package/src/decorators/validators/country-validator-registry.ts +64 -64
  105. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  106. package/src/decorators/validators/country-validators.ts +105 -105
  107. package/src/decorators/validators/validator-metadata.ts +59 -59
  108. package/src/dto/dto-types.ts +226 -226
  109. package/src/dto/filter-types.ts +141 -141
  110. package/src/dto/index.ts +95 -95
  111. package/src/dto/openapi/generators/base.ts +29 -29
  112. package/src/dto/openapi/generators/column.ts +37 -37
  113. package/src/dto/openapi/generators/dto.ts +91 -91
  114. package/src/dto/openapi/generators/filter.ts +75 -75
  115. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  116. package/src/dto/openapi/generators/pagination.ts +111 -111
  117. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  118. package/src/dto/openapi/index.ts +12 -12
  119. package/src/dto/openapi/types.ts +101 -101
  120. package/src/dto/openapi/utilities.ts +95 -95
  121. package/src/dto/pagination-utils.ts +150 -150
  122. package/src/dto/transform.ts +197 -197
  123. package/src/index.ts +86 -86
  124. package/src/orm/als.ts +58 -58
  125. package/src/orm/domain-event-bus.ts +119 -119
  126. package/src/orm/entity-context.ts +86 -86
  127. package/src/orm/entity-materializer.ts +159 -159
  128. package/src/orm/entity-meta.ts +97 -97
  129. package/src/orm/entity-registry.ts +39 -39
  130. package/src/orm/entity-relation-cache.ts +39 -39
  131. package/src/orm/execute.ts +194 -194
  132. package/src/orm/execution-context.ts +18 -18
  133. package/src/orm/hydration-context.ts +26 -26
  134. package/src/orm/hydration.ts +112 -112
  135. package/src/orm/identity-map.ts +61 -61
  136. package/src/orm/interceptor-pipeline.ts +16 -16
  137. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  138. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  139. package/src/orm/lazy-batch/has-many.ts +69 -69
  140. package/src/orm/lazy-batch/has-one.ts +68 -68
  141. package/src/orm/lazy-batch/shared.ts +125 -125
  142. package/src/orm/orm-session.ts +620 -620
  143. package/src/orm/orm.ts +34 -34
  144. package/src/orm/pooled-executor-factory.ts +121 -121
  145. package/src/orm/query-logger.ts +40 -40
  146. package/src/orm/relation-change-processor.ts +420 -420
  147. package/src/orm/relations/belongs-to.ts +143 -143
  148. package/src/orm/relations/has-many.ts +204 -204
  149. package/src/orm/relations/has-one.ts +174 -174
  150. package/src/orm/relations/many-to-many.ts +288 -288
  151. package/src/orm/relations/morph-many.ts +156 -156
  152. package/src/orm/relations/morph-one.ts +151 -151
  153. package/src/orm/relations/morph-to.ts +162 -162
  154. package/src/orm/runtime-types.ts +92 -92
  155. package/src/orm/save-graph.ts +561 -561
  156. package/src/orm/transaction-runner.ts +24 -24
  157. package/src/orm/unit-of-work.ts +426 -426
  158. package/src/query/index.ts +69 -69
  159. package/src/query/target.ts +46 -46
  160. package/src/query-builder/column-selector.ts +81 -81
  161. package/src/query-builder/delete.ts +141 -141
  162. package/src/query-builder/insert-query-state.ts +149 -149
  163. package/src/query-builder/insert.ts +117 -117
  164. package/src/query-builder/query-ast-service.ts +287 -287
  165. package/src/query-builder/query-resolution.ts +78 -78
  166. package/src/query-builder/raw-column-parser.ts +38 -38
  167. package/src/query-builder/relation-filter-utils.ts +153 -153
  168. package/src/query-builder/relation-join-planner.ts +10 -10
  169. package/src/query-builder/relation-manager.ts +87 -87
  170. package/src/query-builder/relation-projection-helper.ts +102 -102
  171. package/src/query-builder/relation-types.ts +9 -9
  172. package/src/query-builder/relation-utils.ts +15 -15
  173. package/src/query-builder/select/cte-facet.ts +40 -40
  174. package/src/query-builder/select/from-facet.ts +80 -80
  175. package/src/query-builder/select/join-facet.ts +62 -62
  176. package/src/query-builder/select/predicate-facet.ts +104 -104
  177. package/src/query-builder/select/projection-facet.ts +70 -70
  178. package/src/query-builder/select/relation-facet.ts +81 -81
  179. package/src/query-builder/select/setop-facet.ts +36 -36
  180. package/src/query-builder/select-helpers.ts +1 -1
  181. package/src/query-builder/select-query-builder-deps.ts +130 -130
  182. package/src/query-builder/select-query-state.ts +207 -207
  183. package/src/query-builder/update.ts +151 -151
  184. package/src/schema/column-types.ts +322 -322
  185. package/src/schema/table-guards.ts +37 -37
  186. package/src/schema/table.ts +281 -281
  187. package/src/tree/index.ts +13 -13
  188. package/src/tree/nested-set-strategy.ts +545 -545
  189. package/src/tree/tree-decorator.ts +397 -397
  190. package/src/tree/tree-manager.ts +754 -754
  191. package/src/tree/tree-query.ts +427 -427
  192. 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';