metal-orm 1.0.90 → 1.0.92
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/dist/index.cjs +214 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -32
- package/dist/index.d.ts +71 -32
- package/dist/index.js +206 -118
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/scripts/generate-entities/render.mjs +16 -3
- package/src/core/ddl/introspect/utils.ts +45 -45
- package/src/decorators/bootstrap.ts +37 -37
- package/src/decorators/column-decorator.ts +3 -1
- package/src/dto/apply-filter.ts +279 -281
- package/src/dto/dto-types.ts +229 -229
- package/src/dto/filter-types.ts +193 -193
- package/src/dto/index.ts +97 -97
- package/src/dto/openapi/generators/base.ts +29 -29
- package/src/dto/openapi/generators/column.ts +37 -34
- package/src/dto/openapi/generators/dto.ts +94 -94
- package/src/dto/openapi/generators/filter.ts +75 -74
- package/src/dto/openapi/generators/nested-dto.ts +618 -532
- package/src/dto/openapi/generators/pagination.ts +111 -111
- package/src/dto/openapi/generators/relation-filter.ts +228 -210
- package/src/dto/openapi/index.ts +17 -17
- package/src/dto/openapi/type-mappings.ts +191 -191
- package/src/dto/openapi/types.ts +101 -83
- package/src/dto/openapi/utilities.ts +90 -45
- package/src/dto/pagination-utils.ts +150 -150
- package/src/dto/transform.ts +197 -193
- package/src/index.ts +69 -69
- package/src/orm/entity-context.ts +9 -9
- package/src/orm/entity-metadata.ts +14 -14
- package/src/orm/entity.ts +74 -74
- package/src/orm/orm-session.ts +159 -159
- package/src/orm/relation-change-processor.ts +3 -3
- package/src/orm/runtime-types.ts +5 -5
- package/src/schema/column-types.ts +4 -4
- package/src/schema/types.ts +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metal-orm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.92",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"engines": {
|
|
@@ -59,7 +59,6 @@
|
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@electric-sql/pglite": "^0.3.14",
|
|
63
62
|
"@typescript-eslint/eslint-plugin": "^8.20.0",
|
|
64
63
|
"@typescript-eslint/parser": "^8.20.0",
|
|
65
64
|
"@vitest/ui": "^4.0.14",
|
|
@@ -75,5 +74,8 @@
|
|
|
75
74
|
"tsup": "^8.0.0",
|
|
76
75
|
"typescript": "^5.5.0",
|
|
77
76
|
"vitest": "^4.0.14"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"@electric-sql/pglite": "^0.3.14"
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -4,6 +4,14 @@ import { buildSchemaMetadata } from './schema.mjs';
|
|
|
4
4
|
|
|
5
5
|
const escapeJsString = value => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
6
6
|
|
|
7
|
+
const sanitizePropertyName = columnName => {
|
|
8
|
+
if (!columnName) return '';
|
|
9
|
+
return columnName
|
|
10
|
+
.replace(/\s+/g, '_')
|
|
11
|
+
.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
12
|
+
.replace(/^[0-9]/, '_$&');
|
|
13
|
+
};
|
|
14
|
+
|
|
7
15
|
const formatJsDoc = comment => {
|
|
8
16
|
if (!comment) return null;
|
|
9
17
|
const normalized = comment.replace(/\r\n?/g, '\n').trim();
|
|
@@ -159,7 +167,7 @@ const buildColumnDoc = column => {
|
|
|
159
167
|
return entries.join('\n');
|
|
160
168
|
};
|
|
161
169
|
|
|
162
|
-
const renderColumnExpression = (column, tablePk, tableSchema, defaultSchema) => {
|
|
170
|
+
const renderColumnExpression = (column, tablePk, tableSchema, defaultSchema, propertyName) => {
|
|
163
171
|
const base = parseColumnType(column.type);
|
|
164
172
|
let expr = base.factory;
|
|
165
173
|
|
|
@@ -198,6 +206,10 @@ const renderColumnExpression = (column, tablePk, tableSchema, defaultSchema) =>
|
|
|
198
206
|
const tsType = base.ts || 'any';
|
|
199
207
|
const optional = !column.notNull;
|
|
200
208
|
|
|
209
|
+
if (column.name !== propertyName) {
|
|
210
|
+
expr = `{ ...${expr}, name: '${escapeJsString(column.name)}' }`;
|
|
211
|
+
}
|
|
212
|
+
|
|
201
213
|
return {
|
|
202
214
|
decorator,
|
|
203
215
|
expr,
|
|
@@ -236,10 +248,11 @@ const renderEntityClassLines = ({ table, className, naming, relations, resolveCl
|
|
|
236
248
|
lines.push(`export class ${className} {`);
|
|
237
249
|
|
|
238
250
|
for (const col of table.columns) {
|
|
239
|
-
const
|
|
251
|
+
const propertyName = sanitizePropertyName(col.name);
|
|
252
|
+
const rendered = renderColumnExpression(col, table.primaryKey, table.schema, defaultSchema, propertyName);
|
|
240
253
|
appendJsDoc(lines, rendered.comment, ' ');
|
|
241
254
|
lines.push(` @${rendered.decorator}(${rendered.expr})`);
|
|
242
|
-
lines.push(` ${
|
|
255
|
+
lines.push(` ${propertyName}${rendered.optional ? '?:' : '!:'} ${rendered.tsType};`);
|
|
243
256
|
lines.push('');
|
|
244
257
|
}
|
|
245
258
|
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { DbExecutor, QueryResult } from '../../execution/db-executor.js';
|
|
2
|
-
import { IntrospectOptions } from './types.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Converts a query result to an array of row objects.
|
|
6
|
-
* @param result - The query result.
|
|
7
|
-
* @returns The array of rows.
|
|
8
|
-
*/
|
|
9
|
-
export const toRows = (result: QueryResult | undefined): Record<string, unknown>[] => {
|
|
10
|
-
if (!result) return [];
|
|
11
|
-
return result.values.map(row =>
|
|
12
|
-
result.columns.reduce<Record<string, unknown>>((acc, col, idx) => {
|
|
13
|
-
acc[col] = row[idx];
|
|
14
|
-
return acc;
|
|
15
|
-
}, {})
|
|
16
|
-
);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Executes a SQL query and returns the rows.
|
|
21
|
-
* @param executor - The database executor.
|
|
22
|
-
* @param sql - The SQL query.
|
|
23
|
-
* @param params - The query parameters.
|
|
24
|
-
* @returns The array of rows.
|
|
25
|
-
*/
|
|
26
|
-
export const queryRows = async (
|
|
27
|
-
executor: DbExecutor,
|
|
28
|
-
sql: string,
|
|
29
|
-
params: unknown[] = []
|
|
30
|
-
): Promise<Record<string, unknown>[]> => {
|
|
31
|
-
const [first] = await executor.executeSql(sql, params);
|
|
32
|
-
return toRows(first);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Checks if a table should be included in introspection based on options.
|
|
37
|
-
* @param name - The table name.
|
|
38
|
-
* @param options - The introspection options.
|
|
39
|
-
* @returns True if the table should be included.
|
|
40
|
-
*/
|
|
41
|
-
export const shouldIncludeTable = (name: string, options: IntrospectOptions): boolean => {
|
|
42
|
-
if (options.includeTables && !options.includeTables.includes(name)) return false;
|
|
43
|
-
if (options.excludeTables && options.excludeTables.includes(name)) return false;
|
|
44
|
-
return true;
|
|
45
|
-
};
|
|
1
|
+
import { DbExecutor, QueryResult } from '../../execution/db-executor.js';
|
|
2
|
+
import { IntrospectOptions } from './types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Converts a query result to an array of row objects.
|
|
6
|
+
* @param result - The query result.
|
|
7
|
+
* @returns The array of rows.
|
|
8
|
+
*/
|
|
9
|
+
export const toRows = (result: QueryResult | undefined): Record<string, unknown>[] => {
|
|
10
|
+
if (!result) return [];
|
|
11
|
+
return result.values.map(row =>
|
|
12
|
+
result.columns.reduce<Record<string, unknown>>((acc, col, idx) => {
|
|
13
|
+
acc[col] = row[idx];
|
|
14
|
+
return acc;
|
|
15
|
+
}, {})
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Executes a SQL query and returns the rows.
|
|
21
|
+
* @param executor - The database executor.
|
|
22
|
+
* @param sql - The SQL query.
|
|
23
|
+
* @param params - The query parameters.
|
|
24
|
+
* @returns The array of rows.
|
|
25
|
+
*/
|
|
26
|
+
export const queryRows = async (
|
|
27
|
+
executor: DbExecutor,
|
|
28
|
+
sql: string,
|
|
29
|
+
params: unknown[] = []
|
|
30
|
+
): Promise<Record<string, unknown>[]> => {
|
|
31
|
+
const [first] = await executor.executeSql(sql, params);
|
|
32
|
+
return toRows(first);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a table should be included in introspection based on options.
|
|
37
|
+
* @param name - The table name.
|
|
38
|
+
* @param options - The introspection options.
|
|
39
|
+
* @returns True if the table should be included.
|
|
40
|
+
*/
|
|
41
|
+
export const shouldIncludeTable = (name: string, options: IntrospectOptions): boolean => {
|
|
42
|
+
if (options.includeTables && !options.includeTables.includes(name)) return false;
|
|
43
|
+
if (options.excludeTables && options.excludeTables.includes(name)) return false;
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
@@ -176,17 +176,17 @@ export const bootstrapEntities = (): TableDef[] => {
|
|
|
176
176
|
* @param ctor - The entity constructor.
|
|
177
177
|
* @returns The table definition or undefined if not found.
|
|
178
178
|
*/
|
|
179
|
-
export const getTableDefFromEntity = <TTable extends TableDef = TableDef>(ctor: EntityConstructor): TTable | undefined => {
|
|
180
|
-
const meta = getEntityMetadata(ctor);
|
|
181
|
-
if (!meta) return undefined;
|
|
182
|
-
if (!meta.table) {
|
|
183
|
-
bootstrapEntities();
|
|
184
|
-
}
|
|
185
|
-
if (!meta.table) {
|
|
186
|
-
throw new Error(`Failed to build table definition for entity '${ctor.name}'`);
|
|
187
|
-
}
|
|
188
|
-
return meta.table as TTable;
|
|
189
|
-
};
|
|
179
|
+
export const getTableDefFromEntity = <TTable extends TableDef = TableDef>(ctor: EntityConstructor): TTable | undefined => {
|
|
180
|
+
const meta = getEntityMetadata(ctor);
|
|
181
|
+
if (!meta) return undefined;
|
|
182
|
+
if (!meta.table) {
|
|
183
|
+
bootstrapEntities();
|
|
184
|
+
}
|
|
185
|
+
if (!meta.table) {
|
|
186
|
+
throw new Error(`Failed to build table definition for entity '${ctor.name}'`);
|
|
187
|
+
}
|
|
188
|
+
return meta.table as TTable;
|
|
189
|
+
};
|
|
190
190
|
|
|
191
191
|
/**
|
|
192
192
|
* Creates a select query builder for the given entity.
|
|
@@ -248,29 +248,29 @@ export const selectFromEntity = <TEntity extends object>(
|
|
|
248
248
|
* Lazily bootstraps entity metadata (via getTableDefFromEntity) and returns a
|
|
249
249
|
* `tableRef(...)`-style proxy so users can write `u.id` instead of `u.columns.id`.
|
|
250
250
|
*/
|
|
251
|
-
export const entityRef = <TEntity extends object>(
|
|
252
|
-
ctor: EntityConstructor<TEntity>
|
|
253
|
-
): TableRef<EntityTable<TEntity>> => {
|
|
254
|
-
const table = getTableDefFromEntity(ctor);
|
|
255
|
-
if (!table) {
|
|
256
|
-
throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
|
|
257
|
-
}
|
|
258
|
-
return tableRef(table as EntityTable<TEntity>);
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
type EntityRefsTuple<T extends readonly EntityConstructor<object>[]> = {
|
|
262
|
-
[K in keyof T]: T[K] extends EntityConstructor<infer TEntity>
|
|
263
|
-
? TableRef<EntityTable<TEntity & object>>
|
|
264
|
-
: never;
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Public API: variadic entity references.
|
|
269
|
-
* Usage:
|
|
270
|
-
* const [u, p] = entityRefs(User, Post);
|
|
271
|
-
*/
|
|
272
|
-
export const entityRefs = <T extends readonly EntityConstructor<object>[]>(
|
|
273
|
-
...ctors: T
|
|
274
|
-
): EntityRefsTuple<T> => {
|
|
275
|
-
return ctors.map(ctor => entityRef(ctor)) as EntityRefsTuple<T>;
|
|
276
|
-
};
|
|
251
|
+
export const entityRef = <TEntity extends object>(
|
|
252
|
+
ctor: EntityConstructor<TEntity>
|
|
253
|
+
): TableRef<EntityTable<TEntity>> => {
|
|
254
|
+
const table = getTableDefFromEntity(ctor);
|
|
255
|
+
if (!table) {
|
|
256
|
+
throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
|
|
257
|
+
}
|
|
258
|
+
return tableRef(table as EntityTable<TEntity>);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
type EntityRefsTuple<T extends readonly EntityConstructor<object>[]> = {
|
|
262
|
+
[K in keyof T]: T[K] extends EntityConstructor<infer TEntity>
|
|
263
|
+
? TableRef<EntityTable<TEntity & object>>
|
|
264
|
+
: never;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Public API: variadic entity references.
|
|
269
|
+
* Usage:
|
|
270
|
+
* const [u, p] = entityRefs(User, Post);
|
|
271
|
+
*/
|
|
272
|
+
export const entityRefs = <T extends readonly EntityConstructor<object>[]>(
|
|
273
|
+
...ctors: T
|
|
274
|
+
): EntityRefsTuple<T> => {
|
|
275
|
+
return ctors.map(ctor => entityRef(ctor)) as EntityRefsTuple<T>;
|
|
276
|
+
};
|
|
@@ -12,6 +12,7 @@ export interface ColumnOptions {
|
|
|
12
12
|
notNull?: boolean;
|
|
13
13
|
primary?: boolean;
|
|
14
14
|
tsType?: ColumnDef['tsType'];
|
|
15
|
+
name?: string;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -35,7 +36,8 @@ const normalizeColumnInput = (input: ColumnInput): ColumnDefLike => {
|
|
|
35
36
|
generated: asDefinition.generated,
|
|
36
37
|
check: asDefinition.check,
|
|
37
38
|
references: asDefinition.references,
|
|
38
|
-
comment: asDefinition.comment
|
|
39
|
+
comment: asDefinition.comment,
|
|
40
|
+
name: asOptions.name ?? asDefinition.name
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
if (!column.type) {
|