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.
- package/README.md +758 -769
- package/dist/index.cjs +106 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +106 -43
- package/dist/index.js.map +1 -1
- package/package.json +100 -96
- package/scripts/generate-entities/render.mjs +21 -12
- package/scripts/generate-entities/schema.mjs +209 -195
- package/scripts/generate-entities/tree-detection.mjs +159 -153
- package/scripts/inflection/pt-br.mjs +468 -468
- package/scripts/run-eslint.mjs +33 -34
- package/scripts/show-sql-seed.sql +102 -0
- package/scripts/show-sql.mjs +321 -325
- package/src/bulk/bulk-delete-executor.ts +3 -5
- package/src/bulk/bulk-insert-executor.ts +7 -7
- package/src/bulk/bulk-update-executor.ts +2 -2
- package/src/bulk/bulk-upsert-executor.ts +5 -7
- package/src/codegen/naming-strategy.ts +54 -54
- package/src/codegen/typescript.ts +482 -482
- package/src/core/ast/adapters.ts +1 -1
- package/src/core/ast/builders.ts +123 -123
- package/src/core/ast/expression-builders.ts +745 -745
- package/src/core/ast/expression-nodes.ts +304 -304
- package/src/core/ast/expression-visitor.ts +212 -212
- package/src/core/ast/expression.ts +20 -20
- package/src/core/ast/helpers.ts +14 -14
- package/src/core/ast/join.ts +18 -18
- package/src/core/ast/query.ts +200 -200
- package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/render-reference.test.ts +70 -70
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
- package/src/core/ddl/introspect/catalogs/index.ts +5 -5
- package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
- package/src/core/ddl/introspect/context.ts +15 -15
- package/src/core/ddl/introspect/functions/postgres.ts +35 -35
- package/src/core/ddl/introspect/mysql.ts +113 -36
- package/src/core/ddl/introspect/registry.ts +40 -40
- package/src/core/ddl/introspect/run-select.ts +35 -35
- package/src/core/ddl/introspect/utils.ts +56 -56
- package/src/core/ddl/naming-strategy.ts +16 -16
- package/src/core/ddl/schema-dialect.ts +56 -56
- package/src/core/ddl/schema-diff.ts +234 -234
- package/src/core/ddl/schema-generator.ts +1 -1
- package/src/core/ddl/schema-introspect.ts +25 -25
- package/src/core/ddl/sql-writing.ts +171 -171
- package/src/core/dialect/abstract.ts +541 -541
- package/src/core/dialect/base/cte-compiler.ts +33 -33
- package/src/core/dialect/base/function-table-formatter.ts +103 -103
- package/src/core/dialect/base/groupby-compiler.ts +21 -21
- package/src/core/dialect/base/join-compiler.ts +25 -25
- package/src/core/dialect/base/orderby-compiler.ts +35 -35
- package/src/core/dialect/base/pagination-strategy.ts +32 -32
- package/src/core/dialect/base/returning-strategy.ts +57 -57
- package/src/core/dialect/base/sql-dialect.ts +294 -294
- package/src/core/dialect/dialect-factory.ts +91 -91
- package/src/core/dialect/mssql/functions.ts +120 -120
- package/src/core/dialect/mssql/index.ts +317 -317
- package/src/core/dialect/mysql/functions.ts +103 -103
- package/src/core/dialect/mysql/index.ts +105 -105
- package/src/core/dialect/postgres/functions.ts +108 -108
- package/src/core/dialect/postgres/index.ts +91 -91
- package/src/core/dialect/sqlite/functions.ts +129 -129
- package/src/core/dialect/sqlite/index.ts +67 -67
- package/src/core/driver/database-driver.ts +19 -19
- package/src/core/driver/mssql-driver.ts +23 -23
- package/src/core/driver/mysql-driver.ts +23 -23
- package/src/core/driver/postgres-driver.ts +23 -23
- package/src/core/driver/sqlite-driver.ts +23 -23
- package/src/core/execution/db-executor.ts +92 -92
- package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
- package/src/core/execution/executors/mssql-executor.ts +184 -184
- package/src/core/execution/executors/mysql-executor.ts +88 -88
- package/src/core/execution/executors/postgres-executor.ts +26 -26
- package/src/core/execution/executors/sqlite-executor.ts +42 -42
- package/src/core/execution/pooling/pool-types.ts +30 -30
- package/src/core/execution/pooling/pool.ts +275 -275
- package/src/core/functions/array.ts +35 -35
- package/src/core/functions/control-flow.ts +79 -79
- package/src/core/functions/datetime.ts +237 -237
- package/src/core/functions/json.ts +70 -70
- package/src/core/functions/numeric.ts +297 -297
- package/src/core/functions/text.ts +334 -334
- package/src/core/functions/types.ts +33 -33
- package/src/core/hydration/types.ts +51 -51
- package/src/core/sql/sql-operator-config.ts +39 -39
- package/src/core/sql/sql.ts +152 -152
- package/src/decorators/column-decorator.ts +87 -87
- package/src/decorators/decorator-metadata.ts +143 -143
- package/src/decorators/entity.ts +93 -93
- package/src/decorators/index.ts +39 -39
- package/src/decorators/relations.ts +231 -231
- package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
- package/src/decorators/transformers/transformer-decorators.ts +134 -134
- package/src/decorators/transformers/transformer-executor.ts +171 -171
- package/src/decorators/transformers/transformer-metadata.ts +73 -73
- package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
- package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
- package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
- package/src/decorators/validators/country-validator-registry.ts +64 -64
- package/src/decorators/validators/country-validators-decorators.ts +203 -203
- package/src/decorators/validators/country-validators.ts +105 -105
- package/src/decorators/validators/validator-metadata.ts +59 -59
- package/src/dto/dto-types.ts +226 -226
- package/src/dto/filter-types.ts +141 -141
- package/src/dto/index.ts +95 -95
- package/src/dto/openapi/generators/base.ts +29 -29
- package/src/dto/openapi/generators/column.ts +37 -37
- package/src/dto/openapi/generators/dto.ts +91 -91
- package/src/dto/openapi/generators/filter.ts +75 -75
- package/src/dto/openapi/generators/nested-dto.ts +618 -618
- package/src/dto/openapi/generators/pagination.ts +111 -111
- package/src/dto/openapi/generators/relation-filter.ts +228 -228
- package/src/dto/openapi/index.ts +12 -12
- package/src/dto/openapi/types.ts +101 -101
- package/src/dto/openapi/utilities.ts +95 -95
- package/src/dto/pagination-utils.ts +150 -150
- package/src/dto/transform.ts +197 -197
- package/src/index.ts +86 -86
- package/src/orm/als.ts +58 -58
- package/src/orm/domain-event-bus.ts +119 -119
- package/src/orm/entity-context.ts +86 -86
- package/src/orm/entity-materializer.ts +159 -159
- package/src/orm/entity-meta.ts +97 -97
- package/src/orm/entity-registry.ts +39 -39
- package/src/orm/entity-relation-cache.ts +39 -39
- package/src/orm/execute.ts +194 -194
- package/src/orm/execution-context.ts +18 -18
- package/src/orm/hydration-context.ts +26 -26
- package/src/orm/hydration.ts +112 -112
- package/src/orm/identity-map.ts +61 -61
- package/src/orm/interceptor-pipeline.ts +16 -16
- package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
- package/src/orm/lazy-batch/belongs-to.ts +108 -108
- package/src/orm/lazy-batch/has-many.ts +69 -69
- package/src/orm/lazy-batch/has-one.ts +68 -68
- package/src/orm/lazy-batch/shared.ts +125 -125
- package/src/orm/orm-session.ts +620 -620
- package/src/orm/orm.ts +34 -34
- package/src/orm/pooled-executor-factory.ts +121 -121
- package/src/orm/query-logger.ts +40 -40
- package/src/orm/relation-change-processor.ts +420 -420
- package/src/orm/relations/belongs-to.ts +143 -143
- package/src/orm/relations/has-many.ts +204 -204
- package/src/orm/relations/has-one.ts +174 -174
- package/src/orm/relations/many-to-many.ts +288 -288
- package/src/orm/relations/morph-many.ts +156 -156
- package/src/orm/relations/morph-one.ts +151 -151
- package/src/orm/relations/morph-to.ts +162 -162
- package/src/orm/runtime-types.ts +92 -92
- package/src/orm/save-graph.ts +561 -561
- package/src/orm/transaction-runner.ts +24 -24
- package/src/orm/unit-of-work.ts +426 -426
- package/src/query/index.ts +69 -69
- package/src/query/target.ts +46 -46
- package/src/query-builder/column-selector.ts +81 -81
- package/src/query-builder/delete.ts +141 -141
- package/src/query-builder/insert-query-state.ts +149 -149
- package/src/query-builder/insert.ts +117 -117
- package/src/query-builder/query-ast-service.ts +287 -287
- package/src/query-builder/query-resolution.ts +78 -78
- package/src/query-builder/raw-column-parser.ts +38 -38
- package/src/query-builder/relation-filter-utils.ts +153 -153
- package/src/query-builder/relation-join-planner.ts +10 -10
- package/src/query-builder/relation-manager.ts +87 -87
- package/src/query-builder/relation-projection-helper.ts +102 -102
- package/src/query-builder/relation-types.ts +9 -9
- package/src/query-builder/relation-utils.ts +15 -15
- package/src/query-builder/select/cte-facet.ts +40 -40
- package/src/query-builder/select/from-facet.ts +80 -80
- package/src/query-builder/select/join-facet.ts +62 -62
- package/src/query-builder/select/predicate-facet.ts +104 -104
- package/src/query-builder/select/projection-facet.ts +70 -70
- package/src/query-builder/select/relation-facet.ts +81 -81
- package/src/query-builder/select/setop-facet.ts +36 -36
- package/src/query-builder/select-helpers.ts +1 -1
- package/src/query-builder/select-query-builder-deps.ts +130 -130
- package/src/query-builder/select-query-state.ts +207 -207
- package/src/query-builder/update.ts +151 -151
- package/src/schema/column-types.ts +322 -322
- package/src/schema/table-guards.ts +37 -37
- package/src/schema/table.ts +281 -281
- package/src/tree/index.ts +13 -13
- package/src/tree/nested-set-strategy.ts +545 -545
- package/src/tree/tree-decorator.ts +397 -397
- package/src/tree/tree-manager.ts +754 -754
- package/src/tree/tree-query.ts +427 -427
- 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
|
+
};
|
package/src/schema/table.ts
CHANGED
|
@@ -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';
|