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.
Files changed (37) hide show
  1. package/dist/index.cjs +214 -118
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +71 -32
  4. package/dist/index.d.ts +71 -32
  5. package/dist/index.js +206 -118
  6. package/dist/index.js.map +1 -1
  7. package/package.json +4 -2
  8. package/scripts/generate-entities/render.mjs +16 -3
  9. package/src/core/ddl/introspect/utils.ts +45 -45
  10. package/src/decorators/bootstrap.ts +37 -37
  11. package/src/decorators/column-decorator.ts +3 -1
  12. package/src/dto/apply-filter.ts +279 -281
  13. package/src/dto/dto-types.ts +229 -229
  14. package/src/dto/filter-types.ts +193 -193
  15. package/src/dto/index.ts +97 -97
  16. package/src/dto/openapi/generators/base.ts +29 -29
  17. package/src/dto/openapi/generators/column.ts +37 -34
  18. package/src/dto/openapi/generators/dto.ts +94 -94
  19. package/src/dto/openapi/generators/filter.ts +75 -74
  20. package/src/dto/openapi/generators/nested-dto.ts +618 -532
  21. package/src/dto/openapi/generators/pagination.ts +111 -111
  22. package/src/dto/openapi/generators/relation-filter.ts +228 -210
  23. package/src/dto/openapi/index.ts +17 -17
  24. package/src/dto/openapi/type-mappings.ts +191 -191
  25. package/src/dto/openapi/types.ts +101 -83
  26. package/src/dto/openapi/utilities.ts +90 -45
  27. package/src/dto/pagination-utils.ts +150 -150
  28. package/src/dto/transform.ts +197 -193
  29. package/src/index.ts +69 -69
  30. package/src/orm/entity-context.ts +9 -9
  31. package/src/orm/entity-metadata.ts +14 -14
  32. package/src/orm/entity.ts +74 -74
  33. package/src/orm/orm-session.ts +159 -159
  34. package/src/orm/relation-change-processor.ts +3 -3
  35. package/src/orm/runtime-types.ts +5 -5
  36. package/src/schema/column-types.ts +4 -4
  37. 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.90",
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 rendered = renderColumnExpression(col, table.primaryKey, table.schema, defaultSchema);
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(` ${col.name}${rendered.optional ? '?:' : '!:'} ${rendered.tsType};`);
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) {