metal-orm 1.0.42 → 1.0.44
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.
- package/README.md +195 -37
- package/dist/index.cjs +1014 -538
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1267 -371
- package/dist/index.d.ts +1267 -371
- package/dist/index.js +1012 -536
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/scripts/run-eslint.mjs +34 -0
- package/src/codegen/typescript.ts +32 -15
- package/src/core/ast/adapters.ts +8 -2
- package/src/core/ast/builders.ts +105 -76
- package/src/core/ast/expression-builders.ts +430 -392
- package/src/core/ast/expression-nodes.ts +14 -5
- package/src/core/ast/expression-visitor.ts +56 -14
- package/src/core/ast/helpers.ts +23 -0
- package/src/core/ast/join-node.ts +18 -2
- package/src/core/ast/query.ts +6 -6
- package/src/core/ast/window-functions.ts +10 -2
- package/src/core/ddl/dialects/base-schema-dialect.ts +37 -4
- package/src/core/ddl/dialects/index.ts +1 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +5 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +3 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +14 -1
- package/src/core/ddl/dialects/render-reference.test.ts +69 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +10 -0
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
- package/src/core/ddl/introspect/context.ts +6 -0
- package/src/core/ddl/introspect/functions/postgres.ts +13 -0
- package/src/core/ddl/introspect/mssql.ts +53 -8
- package/src/core/ddl/introspect/mysql.ts +32 -6
- package/src/core/ddl/introspect/postgres.ts +102 -34
- package/src/core/ddl/introspect/registry.ts +14 -0
- package/src/core/ddl/introspect/run-select.ts +19 -4
- package/src/core/ddl/introspect/sqlite.ts +78 -11
- package/src/core/ddl/introspect/types.ts +0 -1
- package/src/core/ddl/introspect/utils.ts +21 -3
- package/src/core/ddl/naming-strategy.ts +6 -0
- package/src/core/ddl/schema-dialect.ts +20 -6
- package/src/core/ddl/schema-diff.ts +22 -0
- package/src/core/ddl/schema-generator.ts +26 -12
- package/src/core/ddl/schema-plan-executor.ts +6 -0
- package/src/core/ddl/schema-types.ts +6 -0
- package/src/core/ddl/sql-writing.ts +4 -4
- package/src/core/dialect/abstract.ts +19 -7
- package/src/core/dialect/base/function-table-formatter.ts +3 -2
- package/src/core/dialect/base/join-compiler.ts +5 -3
- package/src/core/dialect/base/returning-strategy.ts +1 -0
- package/src/core/dialect/base/sql-dialect.ts +3 -3
- package/src/core/dialect/mssql/functions.ts +24 -25
- package/src/core/dialect/mssql/index.ts +1 -4
- package/src/core/dialect/mysql/functions.ts +0 -1
- package/src/core/dialect/postgres/functions.ts +33 -34
- package/src/core/dialect/postgres/index.ts +1 -0
- package/src/core/dialect/sqlite/functions.ts +18 -19
- package/src/core/dialect/sqlite/index.ts +2 -0
- package/src/core/execution/db-executor.ts +1 -1
- package/src/core/execution/executors/mysql-executor.ts +2 -2
- package/src/core/execution/executors/postgres-executor.ts +1 -1
- package/src/core/execution/pooling/pool.ts +12 -5
- package/src/core/functions/datetime.ts +58 -34
- package/src/core/functions/numeric.ts +96 -31
- package/src/core/functions/standard-strategy.ts +35 -0
- package/src/core/functions/text.ts +84 -23
- package/src/core/functions/types.ts +23 -8
- package/src/decorators/bootstrap.ts +42 -11
- package/src/decorators/column.ts +20 -11
- package/src/decorators/decorator-metadata.ts +30 -9
- package/src/decorators/entity.ts +29 -5
- package/src/decorators/index.ts +3 -0
- package/src/decorators/relations.ts +34 -11
- package/src/orm/als.ts +34 -9
- package/src/orm/entity-context.ts +62 -8
- package/src/orm/entity-meta.ts +8 -8
- package/src/orm/entity-metadata.ts +131 -16
- package/src/orm/entity.ts +28 -29
- package/src/orm/execute.ts +19 -4
- package/src/orm/hydration.ts +42 -39
- package/src/orm/identity-map.ts +1 -1
- package/src/orm/lazy-batch.ts +74 -104
- package/src/orm/orm-session.ts +24 -23
- package/src/orm/orm.ts +2 -5
- package/src/orm/relation-change-processor.ts +12 -11
- package/src/orm/relations/belongs-to.ts +11 -11
- package/src/orm/relations/has-many.ts +54 -10
- package/src/orm/relations/has-one.ts +8 -7
- package/src/orm/relations/many-to-many.ts +13 -13
- package/src/orm/runtime-types.ts +4 -4
- package/src/orm/save-graph.ts +31 -25
- package/src/orm/unit-of-work.ts +17 -17
- package/src/query/index.ts +74 -0
- package/src/query/target.ts +46 -0
- package/src/query-builder/delete-query-state.ts +30 -0
- package/src/query-builder/delete.ts +64 -18
- package/src/query-builder/hydration-manager.ts +52 -5
- package/src/query-builder/insert-query-state.ts +30 -0
- package/src/query-builder/insert.ts +58 -10
- package/src/query-builder/query-ast-service.ts +7 -2
- package/src/query-builder/query-resolution.ts +78 -0
- package/src/query-builder/raw-column-parser.ts +7 -1
- package/src/query-builder/relation-alias.ts +7 -0
- package/src/query-builder/relation-conditions.ts +61 -48
- package/src/query-builder/relation-service.ts +68 -63
- package/src/query-builder/relation-utils.ts +3 -0
- package/src/query-builder/select/cte-facet.ts +40 -0
- package/src/query-builder/select/from-facet.ts +80 -0
- package/src/query-builder/select/join-facet.ts +62 -0
- package/src/query-builder/select/predicate-facet.ts +103 -0
- package/src/query-builder/select/projection-facet.ts +69 -0
- package/src/query-builder/select/relation-facet.ts +81 -0
- package/src/query-builder/select/setop-facet.ts +36 -0
- package/src/query-builder/select-helpers.ts +15 -2
- package/src/query-builder/select-query-builder-deps.ts +19 -1
- package/src/query-builder/select-query-state.ts +2 -1
- package/src/query-builder/select.ts +795 -1163
- package/src/query-builder/update-query-state.ts +52 -0
- package/src/query-builder/update.ts +69 -18
- package/src/schema/column.ts +26 -26
- package/src/schema/table-guards.ts +31 -0
- package/src/schema/table.ts +47 -18
- package/src/schema/types.ts +22 -22
|
@@ -14,9 +14,21 @@ import {
|
|
|
14
14
|
import { JoinNode } from '../core/ast/join.js';
|
|
15
15
|
import { createTableNode } from '../core/ast/builders.js';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Literal values that can be used in UPDATE statements
|
|
19
|
+
*/
|
|
17
20
|
type LiteralValue = string | number | boolean | null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Values allowed in UPDATE SET clauses
|
|
24
|
+
*/
|
|
18
25
|
type UpdateValue = OperandNode | LiteralValue;
|
|
19
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Type guard to check if a value is valid for UPDATE operations
|
|
29
|
+
* @param value - Value to check
|
|
30
|
+
* @returns True if value is a valid update value
|
|
31
|
+
*/
|
|
20
32
|
const isUpdateValue = (value: unknown): value is UpdateValue => {
|
|
21
33
|
if (value === null) return true;
|
|
22
34
|
switch (typeof value) {
|
|
@@ -36,6 +48,11 @@ export class UpdateQueryState {
|
|
|
36
48
|
public readonly table: TableDef;
|
|
37
49
|
public readonly ast: UpdateQueryNode;
|
|
38
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Creates a new UpdateQueryState instance
|
|
53
|
+
* @param table - Table definition for the update
|
|
54
|
+
* @param ast - Optional existing AST
|
|
55
|
+
*/
|
|
39
56
|
constructor(table: TableDef, ast?: UpdateQueryNode) {
|
|
40
57
|
this.table = table;
|
|
41
58
|
this.ast = ast ?? {
|
|
@@ -46,10 +63,20 @@ export class UpdateQueryState {
|
|
|
46
63
|
};
|
|
47
64
|
}
|
|
48
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Creates a new UpdateQueryState with updated AST
|
|
68
|
+
* @param nextAst - Updated AST
|
|
69
|
+
* @returns New UpdateQueryState instance
|
|
70
|
+
*/
|
|
49
71
|
private clone(nextAst: UpdateQueryNode): UpdateQueryState {
|
|
50
72
|
return new UpdateQueryState(this.table, nextAst);
|
|
51
73
|
}
|
|
52
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Sets the columns to update with their new values
|
|
77
|
+
* @param values - Record of column names to values
|
|
78
|
+
* @returns New UpdateQueryState with SET clause
|
|
79
|
+
*/
|
|
53
80
|
withSet(values: Record<string, unknown>): UpdateQueryState {
|
|
54
81
|
const assignments: UpdateAssignmentNode[] = Object.entries(values).map(([column, rawValue]) => {
|
|
55
82
|
if (!isUpdateValue(rawValue)) {
|
|
@@ -74,6 +101,11 @@ export class UpdateQueryState {
|
|
|
74
101
|
});
|
|
75
102
|
}
|
|
76
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Adds a WHERE condition to the update query
|
|
106
|
+
* @param expr - WHERE expression
|
|
107
|
+
* @returns New UpdateQueryState with WHERE clause
|
|
108
|
+
*/
|
|
77
109
|
withWhere(expr: ExpressionNode): UpdateQueryState {
|
|
78
110
|
return this.clone({
|
|
79
111
|
...this.ast,
|
|
@@ -81,6 +113,11 @@ export class UpdateQueryState {
|
|
|
81
113
|
});
|
|
82
114
|
}
|
|
83
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Adds a RETURNING clause to the update query
|
|
118
|
+
* @param columns - Columns to return
|
|
119
|
+
* @returns New UpdateQueryState with RETURNING clause
|
|
120
|
+
*/
|
|
84
121
|
withReturning(columns: ColumnNode[]): UpdateQueryState {
|
|
85
122
|
return this.clone({
|
|
86
123
|
...this.ast,
|
|
@@ -88,6 +125,11 @@ export class UpdateQueryState {
|
|
|
88
125
|
});
|
|
89
126
|
}
|
|
90
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Sets the FROM clause for the update query
|
|
130
|
+
* @param from - Table source for FROM
|
|
131
|
+
* @returns New UpdateQueryState with FROM clause
|
|
132
|
+
*/
|
|
91
133
|
withFrom(from: TableSourceNode): UpdateQueryState {
|
|
92
134
|
return this.clone({
|
|
93
135
|
...this.ast,
|
|
@@ -95,6 +137,11 @@ export class UpdateQueryState {
|
|
|
95
137
|
});
|
|
96
138
|
}
|
|
97
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Adds a JOIN to the update query
|
|
142
|
+
* @param join - Join node to add
|
|
143
|
+
* @returns New UpdateQueryState with JOIN
|
|
144
|
+
*/
|
|
98
145
|
withJoin(join: JoinNode): UpdateQueryState {
|
|
99
146
|
return this.clone({
|
|
100
147
|
...this.ast,
|
|
@@ -102,6 +149,11 @@ export class UpdateQueryState {
|
|
|
102
149
|
});
|
|
103
150
|
}
|
|
104
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Applies an alias to the table being updated
|
|
154
|
+
* @param alias - Alias for the table
|
|
155
|
+
* @returns New UpdateQueryState with table alias
|
|
156
|
+
*/
|
|
105
157
|
withTableAlias(alias: string): UpdateQueryState {
|
|
106
158
|
return this.clone({
|
|
107
159
|
...this.ast,
|
|
@@ -2,12 +2,14 @@ import { TableDef } from '../schema/table.js';
|
|
|
2
2
|
import { ColumnDef } from '../schema/column.js';
|
|
3
3
|
import { ColumnNode, ExpressionNode } from '../core/ast/expression.js';
|
|
4
4
|
import { JOIN_KINDS, JoinKind } from '../core/sql/sql.js';
|
|
5
|
-
import { CompiledQuery,
|
|
5
|
+
import { CompiledQuery, Dialect } from '../core/dialect/abstract.js';
|
|
6
6
|
import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
|
|
7
7
|
import { TableSourceNode, UpdateQueryNode } from '../core/ast/query.js';
|
|
8
8
|
import { UpdateQueryState } from './update-query-state.js';
|
|
9
9
|
import { createJoinNode } from '../core/ast/join-node.js';
|
|
10
10
|
import { buildColumnNode } from '../core/ast/builders.js';
|
|
11
|
+
import { OrmSession } from '../orm/orm-session.js';
|
|
12
|
+
import { QueryResult } from '../core/execution/db-executor.js';
|
|
11
13
|
|
|
12
14
|
type UpdateDialectInput = Dialect | DialectKey;
|
|
13
15
|
|
|
@@ -18,6 +20,11 @@ export class UpdateQueryBuilder<T> {
|
|
|
18
20
|
private readonly table: TableDef;
|
|
19
21
|
private readonly state: UpdateQueryState;
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new UpdateQueryBuilder instance
|
|
25
|
+
* @param table - The table definition for the UPDATE query
|
|
26
|
+
* @param state - Optional initial query state, defaults to a new UpdateQueryState
|
|
27
|
+
*/
|
|
21
28
|
constructor(table: TableDef, state?: UpdateQueryState) {
|
|
22
29
|
this.table = table;
|
|
23
30
|
this.state = state ?? new UpdateQueryState(table);
|
|
@@ -27,15 +34,33 @@ export class UpdateQueryBuilder<T> {
|
|
|
27
34
|
return new UpdateQueryBuilder(this.table, state);
|
|
28
35
|
}
|
|
29
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Sets an alias for the table in the UPDATE query
|
|
39
|
+
* @param alias - The alias to assign to the table
|
|
40
|
+
* @returns A new UpdateQueryBuilder with the table alias set
|
|
41
|
+
*/
|
|
30
42
|
as(alias: string): UpdateQueryBuilder<T> {
|
|
31
43
|
return this.clone(this.state.withTableAlias(alias));
|
|
32
44
|
}
|
|
33
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Adds a FROM clause to the UPDATE query
|
|
48
|
+
* @param source - The table source to use in the FROM clause
|
|
49
|
+
* @returns A new UpdateQueryBuilder with the FROM clause added
|
|
50
|
+
*/
|
|
34
51
|
from(source: TableDef | TableSourceNode): UpdateQueryBuilder<T> {
|
|
35
52
|
const tableSource = this.resolveTableSource(source);
|
|
36
53
|
return this.clone(this.state.withFrom(tableSource));
|
|
37
54
|
}
|
|
38
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Adds a JOIN clause to the UPDATE query
|
|
58
|
+
* @param table - The table to join with
|
|
59
|
+
* @param condition - The join condition expression
|
|
60
|
+
* @param kind - The type of join (defaults to INNER)
|
|
61
|
+
* @param relationName - Optional name for the relation
|
|
62
|
+
* @returns A new UpdateQueryBuilder with the JOIN clause added
|
|
63
|
+
*/
|
|
39
64
|
join(
|
|
40
65
|
table: TableDef | TableSourceNode | string,
|
|
41
66
|
condition: ExpressionNode,
|
|
@@ -47,14 +72,29 @@ export class UpdateQueryBuilder<T> {
|
|
|
47
72
|
return this.clone(this.state.withJoin(joinNode));
|
|
48
73
|
}
|
|
49
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Adds a SET clause to the UPDATE query
|
|
77
|
+
* @param values - The column-value pairs to update
|
|
78
|
+
* @returns A new UpdateQueryBuilder with the SET clause added
|
|
79
|
+
*/
|
|
50
80
|
set(values: Record<string, unknown>): UpdateQueryBuilder<T> {
|
|
51
81
|
return this.clone(this.state.withSet(values));
|
|
52
82
|
}
|
|
53
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Adds a WHERE clause to the UPDATE query
|
|
86
|
+
* @param expr - The expression to use as the WHERE condition
|
|
87
|
+
* @returns A new UpdateQueryBuilder with the WHERE clause added
|
|
88
|
+
*/
|
|
54
89
|
where(expr: ExpressionNode): UpdateQueryBuilder<T> {
|
|
55
90
|
return this.clone(this.state.withWhere(expr));
|
|
56
91
|
}
|
|
57
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Adds a RETURNING clause to the UPDATE query
|
|
95
|
+
* @param columns - Columns to return after update
|
|
96
|
+
* @returns A new UpdateQueryBuilder with the RETURNING clause added
|
|
97
|
+
*/
|
|
58
98
|
returning(...columns: (ColumnDef | ColumnNode)[]): UpdateQueryBuilder<T> {
|
|
59
99
|
if (!columns.length) return this;
|
|
60
100
|
const nodes = columns.map(column => buildColumnNode(this.table, column));
|
|
@@ -73,28 +113,39 @@ export class UpdateQueryBuilder<T> {
|
|
|
73
113
|
return this.resolveTableSource(table);
|
|
74
114
|
}
|
|
75
115
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
compile(dialect: UpdateDialectInput): CompiledQuery
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// UpdateCompiler path – old behavior
|
|
86
|
-
return (arg as UpdateCompiler).compileUpdate(this.state.ast);
|
|
87
|
-
}
|
|
116
|
+
/**
|
|
117
|
+
* Compiles the UPDATE query for the specified dialect
|
|
118
|
+
* @param dialect - The SQL dialect to compile for
|
|
119
|
+
* @returns The compiled query with SQL and parameters
|
|
120
|
+
*/
|
|
121
|
+
compile(dialect: UpdateDialectInput): CompiledQuery {
|
|
122
|
+
const resolved = resolveDialectInput(dialect);
|
|
123
|
+
return resolved.compileUpdate(this.state.ast);
|
|
124
|
+
}
|
|
88
125
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Returns the SQL string for the UPDATE query
|
|
128
|
+
* @param dialect - The SQL dialect to generate SQL for
|
|
129
|
+
* @returns The SQL string representation of the query
|
|
130
|
+
*/
|
|
131
|
+
toSql(dialect: UpdateDialectInput): string {
|
|
132
|
+
return this.compile(dialect).sql;
|
|
92
133
|
}
|
|
93
134
|
|
|
94
|
-
|
|
95
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Executes the UPDATE query using the provided session
|
|
137
|
+
* @param session - The ORM session to execute the query with
|
|
138
|
+
* @returns A promise that resolves to the query results
|
|
139
|
+
*/
|
|
140
|
+
async execute(session: OrmSession): Promise<QueryResult[]> {
|
|
141
|
+
const compiled = this.compile(session.dialect);
|
|
142
|
+
return session.executor.executeSql(compiled.sql, compiled.params);
|
|
96
143
|
}
|
|
97
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Returns the Abstract Syntax Tree (AST) representation of the query
|
|
147
|
+
* @returns The AST node for the UPDATE query
|
|
148
|
+
*/
|
|
98
149
|
getAST(): UpdateQueryNode {
|
|
99
150
|
return this.state.ast;
|
|
100
151
|
}
|
package/src/schema/column.ts
CHANGED
|
@@ -100,7 +100,7 @@ export interface ColumnDef<T extends ColumnType = ColumnType, TRuntime = unknown
|
|
|
100
100
|
/** Column comment/description */
|
|
101
101
|
comment?: string;
|
|
102
102
|
/** Additional arguments for the column type (e.g., VARCHAR length) */
|
|
103
|
-
args?:
|
|
103
|
+
args?: unknown[];
|
|
104
104
|
/** Table name this column belongs to (filled at runtime by defineTable) */
|
|
105
105
|
table?: string;
|
|
106
106
|
}
|
|
@@ -234,28 +234,28 @@ export const col = {
|
|
|
234
234
|
* Marks a column as UNIQUE
|
|
235
235
|
*/
|
|
236
236
|
unique: <T extends ColumnType>(def: ColumnDef<T>, name?: string): ColumnDef<T> =>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
({
|
|
238
|
+
...def,
|
|
239
|
+
unique: name ?? true
|
|
240
|
+
}),
|
|
241
241
|
|
|
242
242
|
/**
|
|
243
243
|
* Sets a default value for the column
|
|
244
244
|
*/
|
|
245
245
|
default: <T extends ColumnType>(def: ColumnDef<T>, value: unknown): ColumnDef<T> =>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
({
|
|
247
|
+
...def,
|
|
248
|
+
default: value
|
|
249
|
+
}),
|
|
250
250
|
|
|
251
251
|
/**
|
|
252
252
|
* Sets a raw SQL default value for the column
|
|
253
253
|
*/
|
|
254
254
|
defaultRaw: <T extends ColumnType>(def: ColumnDef<T>, expression: string): ColumnDef<T> =>
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
({
|
|
256
|
+
...def,
|
|
257
|
+
default: { raw: expression }
|
|
258
|
+
}),
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
261
|
* Marks a column as auto-increment / identity
|
|
@@ -264,27 +264,27 @@ export const col = {
|
|
|
264
264
|
def: ColumnDef<T>,
|
|
265
265
|
strategy: ColumnDef['generated'] = 'byDefault'
|
|
266
266
|
): ColumnDef<T> =>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
267
|
+
({
|
|
268
|
+
...def,
|
|
269
|
+
autoIncrement: true,
|
|
270
|
+
generated: strategy
|
|
271
|
+
}),
|
|
272
272
|
|
|
273
273
|
/**
|
|
274
274
|
* Adds a foreign key reference
|
|
275
275
|
*/
|
|
276
276
|
references: <T extends ColumnType>(def: ColumnDef<T>, ref: ForeignKeyReference): ColumnDef<T> =>
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
({
|
|
278
|
+
...def,
|
|
279
|
+
references: ref
|
|
280
|
+
}),
|
|
281
281
|
|
|
282
282
|
/**
|
|
283
283
|
* Adds a check constraint to the column
|
|
284
284
|
*/
|
|
285
285
|
check: <T extends ColumnType>(def: ColumnDef<T>, expression: string): ColumnDef<T> =>
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
286
|
+
({
|
|
287
|
+
...def,
|
|
288
|
+
check: expression
|
|
289
|
+
})
|
|
290
290
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ColumnDef } from './column.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
|
+
export const isTableDef = (value: unknown): value is TableDef => {
|
|
13
|
+
if (typeof value !== 'object' || value === null) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const candidate = value as Partial<TableDef>;
|
|
18
|
+
if (typeof candidate.name !== 'string') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!isColumnsRecord(candidate.columns)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!isRelationsRecord(candidate.relations)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return true;
|
|
31
|
+
};
|
package/src/schema/table.ts
CHANGED
|
@@ -31,12 +31,12 @@ export interface TableOptions {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export interface TableHooks {
|
|
34
|
-
beforeInsert?(ctx: unknown, entity:
|
|
35
|
-
afterInsert?(ctx: unknown, entity:
|
|
36
|
-
beforeUpdate?(ctx: unknown, entity:
|
|
37
|
-
afterUpdate?(ctx: unknown, entity:
|
|
38
|
-
beforeDelete?(ctx: unknown, entity:
|
|
39
|
-
afterDelete?(ctx: unknown, entity:
|
|
34
|
+
beforeInsert?(ctx: unknown, entity: unknown): Promise<void> | void;
|
|
35
|
+
afterInsert?(ctx: unknown, entity: unknown): Promise<void> | void;
|
|
36
|
+
beforeUpdate?(ctx: unknown, entity: unknown): Promise<void> | void;
|
|
37
|
+
afterUpdate?(ctx: unknown, entity: unknown): Promise<void> | void;
|
|
38
|
+
beforeDelete?(ctx: unknown, entity: unknown): Promise<void> | void;
|
|
39
|
+
afterDelete?(ctx: unknown, entity: unknown): Promise<void> | void;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -94,7 +94,8 @@ export const defineTable = <T extends Record<string, ColumnDef>>(
|
|
|
94
94
|
): TableDef<T> => {
|
|
95
95
|
// Runtime mutability to assign names to column definitions for convenience
|
|
96
96
|
const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
|
|
97
|
-
|
|
97
|
+
const colDef = { ...def, name: key, table: name };
|
|
98
|
+
(acc as Record<string, unknown>)[key] = colDef;
|
|
98
99
|
return acc;
|
|
99
100
|
}, {} as T);
|
|
100
101
|
|
|
@@ -128,36 +129,39 @@ export type TableRef<T extends TableDef> =
|
|
|
128
129
|
$: T["columns"];
|
|
129
130
|
};
|
|
130
131
|
|
|
131
|
-
const TABLE_REF_CACHE: WeakMap<object,
|
|
132
|
+
const TABLE_REF_CACHE: WeakMap<object, unknown> = new WeakMap();
|
|
132
133
|
|
|
133
134
|
const withColumnProps = <T extends TableDef>(table: T): TableRef<T> => {
|
|
134
|
-
const cached = TABLE_REF_CACHE.get(table
|
|
135
|
+
const cached = TABLE_REF_CACHE.get(table);
|
|
135
136
|
if (cached) return cached as TableRef<T>;
|
|
136
137
|
|
|
137
|
-
const proxy = new Proxy(table as
|
|
138
|
+
const proxy = new Proxy(table as object, {
|
|
138
139
|
get(target, prop, receiver) {
|
|
139
|
-
|
|
140
|
+
const t = target as TableDef;
|
|
141
|
+
if (prop === "$") return t.columns;
|
|
140
142
|
|
|
141
143
|
// Prefer real table fields first (prevents collision surprises)
|
|
142
144
|
if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver);
|
|
143
145
|
|
|
144
146
|
// Fall back to columns bag
|
|
145
|
-
if (typeof prop === "string" && prop in
|
|
147
|
+
if (typeof prop === "string" && prop in t.columns) return t.columns[prop];
|
|
146
148
|
|
|
147
149
|
return undefined;
|
|
148
150
|
},
|
|
149
151
|
|
|
150
152
|
has(target, prop) {
|
|
153
|
+
const t = target as TableDef;
|
|
151
154
|
return (
|
|
152
155
|
prop === "$" ||
|
|
153
156
|
Reflect.has(target, prop) ||
|
|
154
|
-
(typeof prop === "string" && prop in
|
|
157
|
+
(typeof prop === "string" && prop in t.columns)
|
|
155
158
|
);
|
|
156
159
|
},
|
|
157
160
|
|
|
158
161
|
ownKeys(target) {
|
|
162
|
+
const t = target as TableDef;
|
|
159
163
|
const base = Reflect.ownKeys(target);
|
|
160
|
-
const cols = Object.keys(
|
|
164
|
+
const cols = Object.keys(t.columns);
|
|
161
165
|
|
|
162
166
|
for (const k of cols) {
|
|
163
167
|
if (!base.includes(k)) base.push(k);
|
|
@@ -172,20 +176,20 @@ const withColumnProps = <T extends TableDef>(table: T): TableRef<T> => {
|
|
|
172
176
|
configurable: true,
|
|
173
177
|
enumerable: false,
|
|
174
178
|
get() {
|
|
175
|
-
return target.columns;
|
|
179
|
+
return (target as TableDef).columns;
|
|
176
180
|
},
|
|
177
181
|
};
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
if (
|
|
181
185
|
typeof prop === "string" &&
|
|
182
|
-
prop in target.columns &&
|
|
186
|
+
prop in (target as TableDef).columns &&
|
|
183
187
|
!Reflect.has(target, prop)
|
|
184
188
|
) {
|
|
185
189
|
return {
|
|
186
190
|
configurable: true,
|
|
187
191
|
enumerable: true,
|
|
188
|
-
value: target.columns[prop],
|
|
192
|
+
value: (target as TableDef).columns[prop],
|
|
189
193
|
writable: false,
|
|
190
194
|
};
|
|
191
195
|
}
|
|
@@ -194,7 +198,7 @@ const withColumnProps = <T extends TableDef>(table: T): TableRef<T> => {
|
|
|
194
198
|
},
|
|
195
199
|
}) as TableRef<T>;
|
|
196
200
|
|
|
197
|
-
TABLE_REF_CACHE.set(table
|
|
201
|
+
TABLE_REF_CACHE.set(table, proxy);
|
|
198
202
|
return proxy;
|
|
199
203
|
};
|
|
200
204
|
|
|
@@ -208,3 +212,28 @@ const withColumnProps = <T extends TableDef>(table: T): TableRef<T> => {
|
|
|
208
212
|
* t.$.name is the "name" column (escape hatch)
|
|
209
213
|
*/
|
|
210
214
|
export const tableRef = <T extends TableDef>(table: T): TableRef<T> => withColumnProps(table);
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Public API: dynamic column lookup by string key.
|
|
218
|
+
*
|
|
219
|
+
* Useful when the column name is only known at runtime, or when a column name
|
|
220
|
+
* collides with a real table field (e.g. "name").
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* const t = tableRef(todos);
|
|
225
|
+
* const key = runtimeKey();
|
|
226
|
+
* where(eq(getColumn(t, key), 123));
|
|
227
|
+
* // or: t.$.name (escape hatch)
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
export function getColumn<T extends TableDef, K extends keyof T['columns'] & string>(table: T, key: K): T['columns'][K];
|
|
231
|
+
export function getColumn<T extends TableDef>(table: T, key: string): ColumnDef;
|
|
232
|
+
export function getColumn<T extends TableDef>(table: T, key: string): ColumnDef {
|
|
233
|
+
const col = table.columns[key] as ColumnDef | undefined;
|
|
234
|
+
if (!col) {
|
|
235
|
+
const tableName = table.name || '<unknown>';
|
|
236
|
+
throw new Error(`Column '${key}' does not exist on table '${tableName}'`);
|
|
237
|
+
}
|
|
238
|
+
return col;
|
|
239
|
+
}
|
package/src/schema/types.ts
CHANGED
|
@@ -23,15 +23,15 @@ export type RelationTargetTable<TRel extends RelationDef> =
|
|
|
23
23
|
*/
|
|
24
24
|
export type ColumnToTs<T extends ColumnDef> =
|
|
25
25
|
[unknown] extends [T['tsType']]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
? T['type'] extends 'INT' | 'INTEGER' | 'int' | 'integer' ? number :
|
|
27
|
+
T['type'] extends 'BIGINT' | 'bigint' ? number | bigint :
|
|
28
|
+
T['type'] extends 'DECIMAL' | 'decimal' | 'FLOAT' | 'float' | 'DOUBLE' | 'double' ? number :
|
|
29
|
+
T['type'] extends 'BOOLEAN' | 'boolean' ? boolean :
|
|
30
|
+
T['type'] extends 'JSON' | 'json' ? unknown :
|
|
31
|
+
T['type'] extends 'BLOB' | 'blob' | 'BINARY' | 'binary' | 'VARBINARY' | 'varbinary' | 'BYTEA' | 'bytea' ? Buffer :
|
|
32
|
+
T['type'] extends 'DATE' | 'date' | 'DATETIME' | 'datetime' | 'TIMESTAMP' | 'timestamp' | 'TIMESTAMPTZ' | 'timestamptz' ? string :
|
|
33
|
+
string
|
|
34
|
+
: Exclude<T['tsType'], undefined>;
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Infers a row shape from a table definition
|
|
@@ -41,10 +41,10 @@ export type InferRow<TTable extends TableDef> = {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
type RelationResult<T extends RelationDef> =
|
|
44
|
-
T extends HasManyRelation<infer TTarget>
|
|
45
|
-
T extends HasOneRelation<infer TTarget>
|
|
46
|
-
T extends BelongsToRelation<infer TTarget>
|
|
47
|
-
T extends BelongsToManyRelation<infer TTarget>
|
|
44
|
+
T extends HasManyRelation<infer TTarget> ? InferRow<TTarget>[] :
|
|
45
|
+
T extends HasOneRelation<infer TTarget> ? InferRow<TTarget> | null :
|
|
46
|
+
T extends BelongsToRelation<infer TTarget> ? InferRow<TTarget> | null :
|
|
47
|
+
T extends BelongsToManyRelation<infer TTarget> ? (InferRow<TTarget> & { _pivot?: unknown })[] :
|
|
48
48
|
never;
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -88,15 +88,15 @@ export type EntityInstance<
|
|
|
88
88
|
TRow = InferRow<TTable>
|
|
89
89
|
> = TRow & {
|
|
90
90
|
[K in keyof RelationMap<TTable>]:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
TTable['relations'][K] extends HasManyRelation<infer TTarget>
|
|
92
|
+
? HasManyCollection<EntityInstance<TTarget>>
|
|
93
|
+
: TTable['relations'][K] extends HasOneRelation<infer TTarget>
|
|
94
|
+
? HasOneReference<EntityInstance<TTarget>>
|
|
95
|
+
: TTable['relations'][K] extends BelongsToManyRelation<infer TTarget>
|
|
96
|
+
? ManyToManyCollection<EntityInstance<TTarget>>
|
|
97
|
+
: TTable['relations'][K] extends BelongsToRelation<infer TTarget>
|
|
98
|
+
? BelongsToReference<EntityInstance<TTarget>>
|
|
99
|
+
: never;
|
|
100
100
|
} & {
|
|
101
101
|
$load<K extends keyof RelationMap<TTable>>(relation: K): Promise<RelationMap<TTable>[K]>;
|
|
102
102
|
};
|