metal-orm 1.1.0 → 1.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "engines": {
@@ -200,6 +200,13 @@ Flags:
200
200
  --out=<file> Override the generated file (defaults to generated-entities.ts or the index inside --out-dir)
201
201
  --out-dir=<dir> Emit one file per entity inside this directory plus the shared index
202
202
  --help Show this help
203
+
204
+ Naming Overrides JSON format:
205
+ {
206
+ "irregulars": { "person": "people", "child": "children" },
207
+ "relationOverrides": { "User": { "orders": "purchases" } },
208
+ "classNameOverrides": { "users_table": "UserAccount", "order_items": "OrderLineItem" }
209
+ }
203
210
  `
204
211
  );
205
212
  };
@@ -18,8 +18,8 @@ const loadNamingOverrides = async (filePath, fsPromises) => {
18
18
  throw new Error(`Naming overrides at ${filePath} must be an object`);
19
19
  }
20
20
 
21
- // Support both flat format { "singular": "plural" } and structured { irregulars: {...}, relationOverrides: {...} }
22
- const hasStructuredFormat = parsed.irregulars || parsed.relationOverrides;
21
+ // Support both flat format { "singular": "plural" } and structured { irregulars: {...}, relationOverrides: {...}, classNameOverrides: {...} }
22
+ const hasStructuredFormat = parsed.irregulars || parsed.relationOverrides || parsed.classNameOverrides;
23
23
 
24
24
  const irregulars = hasStructuredFormat
25
25
  ? (parsed.irregulars && typeof parsed.irregulars === 'object' ? parsed.irregulars : {})
@@ -29,15 +29,19 @@ const loadNamingOverrides = async (filePath, fsPromises) => {
29
29
  ? parsed.relationOverrides
30
30
  : {};
31
31
 
32
- return { irregulars, relationOverrides };
32
+ const classNameOverrides = hasStructuredFormat && parsed.classNameOverrides && typeof parsed.classNameOverrides === 'object'
33
+ ? parsed.classNameOverrides
34
+ : {};
35
+
36
+ return { irregulars, relationOverrides, classNameOverrides };
33
37
  };
34
38
 
35
39
  export const generateEntities = async (opts, context = {}) => {
36
40
  const { fs: fsPromises = fs, logger = console } = context;
37
- const { irregulars, relationOverrides } = opts.namingOverrides
41
+ const { irregulars, relationOverrides, classNameOverrides } = opts.namingOverrides
38
42
  ? await loadNamingOverrides(opts.namingOverrides, fsPromises)
39
- : { irregulars: undefined, relationOverrides: {} };
40
- const naming = createNamingStrategy(opts.locale, irregulars, relationOverrides);
43
+ : { irregulars: undefined, relationOverrides: {}, classNameOverrides: {} };
44
+ const naming = createNamingStrategy(opts.locale, irregulars, relationOverrides, classNameOverrides);
41
45
 
42
46
  const { executor, cleanup } = await loadDriver(opts.dialect, opts.url, opts.dbPath);
43
47
  let schema;
@@ -0,0 +1,181 @@
1
+ import { Connection, Request } from 'tedious';
2
+
3
+ const REQUIRED_ENV = ['PGE_DIGITAL_HOST', 'PGE_DIGITAL_USER', 'PGE_DIGITAL_PASSWORD'];
4
+
5
+ const hasDbEnv = REQUIRED_ENV.every((name) => !!process.env[name]);
6
+
7
+ if (!hasDbEnv) {
8
+ console.error('Missing required environment variables:', REQUIRED_ENV.filter(n => !process.env[n]));
9
+ process.exit(1);
10
+ }
11
+
12
+ const parseBool = (value, fallback) => {
13
+ if (value === undefined) return fallback;
14
+ return ['1', 'true', 'yes', 'on'].includes(value.toLowerCase());
15
+ };
16
+
17
+ async function queryTableSchema(connection, tableName) {
18
+ return new Promise((resolve, reject) => {
19
+ const query = `
20
+ SELECT
21
+ c.COLUMN_NAME,
22
+ c.DATA_TYPE,
23
+ c.IS_NULLABLE,
24
+ c.CHARACTER_MAXIMUM_LENGTH,
25
+ c.NUMERIC_PRECISION,
26
+ c.NUMERIC_SCALE,
27
+ COLUMNPROPERTY(OBJECT_ID(c.TABLE_SCHEMA + '.' + c.TABLE_NAME), c.COLUMN_NAME, 'IsIdentity') as IS_IDENTITY,
28
+ CASE WHEN pk.COLUMN_NAME IS NOT NULL THEN 1 ELSE 0 END as IS_PRIMARY_KEY
29
+ FROM INFORMATION_SCHEMA.COLUMNS c
30
+ LEFT JOIN (
31
+ SELECT ku.TABLE_CATALOG, ku.TABLE_SCHEMA, ku.TABLE_NAME, ku.COLUMN_NAME
32
+ FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
33
+ JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku ON tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME
34
+ WHERE tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
35
+ ) pk ON c.TABLE_CATALOG = pk.TABLE_CATALOG
36
+ AND c.TABLE_SCHEMA = pk.TABLE_SCHEMA
37
+ AND c.TABLE_NAME = pk.TABLE_NAME
38
+ AND c.COLUMN_NAME = pk.COLUMN_NAME
39
+ WHERE c.TABLE_NAME = '${tableName}'
40
+ ORDER BY c.ORDINAL_POSITION
41
+ `;
42
+
43
+ const request = new Request(query, (err, rowCount) => {
44
+ if (err) reject(err);
45
+ });
46
+
47
+ const columns = [];
48
+ request.on('row', (columns_data) => {
49
+ const col = {};
50
+ columns_data.forEach((c) => {
51
+ col[c.metadata.colName] = c.value;
52
+ });
53
+ columns.push(col);
54
+ });
55
+
56
+ request.on('requestCompleted', () => {
57
+ resolve(columns);
58
+ });
59
+
60
+ connection.execSql(request);
61
+ });
62
+ }
63
+
64
+ async function queryForeignKeys(connection, tableName) {
65
+ return new Promise((resolve, reject) => {
66
+ const query = `
67
+ SELECT
68
+ fk.name AS FK_NAME,
69
+ tp.name AS PARENT_TABLE,
70
+ cp.name AS PARENT_COLUMN,
71
+ tr.name AS REFERENCED_TABLE,
72
+ cr.name AS REFERENCED_COLUMN
73
+ FROM sys.foreign_keys fk
74
+ INNER JOIN sys.tables tp ON fk.parent_object_id = tp.object_id
75
+ INNER JOIN sys.tables tr ON fk.referenced_object_id = tr.object_id
76
+ INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
77
+ INNER JOIN sys.columns cp ON fkc.parent_object_id = cp.object_id AND fkc.parent_column_id = cp.column_id
78
+ INNER JOIN sys.columns cr ON fkc.referenced_object_id = cr.object_id AND fkc.referenced_column_id = cr.column_id
79
+ WHERE tp.name = '${tableName}' OR tr.name = '${tableName}'
80
+ `;
81
+
82
+ const request = new Request(query, (err, rowCount) => {
83
+ if (err) reject(err);
84
+ });
85
+
86
+ const fks = [];
87
+ request.on('row', (columns) => {
88
+ const fk = {};
89
+ columns.forEach((c) => {
90
+ fk[c.metadata.colName] = c.value;
91
+ });
92
+ fks.push(fk);
93
+ });
94
+
95
+ request.on('requestCompleted', () => {
96
+ resolve(fks);
97
+ });
98
+
99
+ connection.execSql(request);
100
+ });
101
+ }
102
+
103
+ async function main() {
104
+ const { PGE_DIGITAL_HOST, PGE_DIGITAL_USER, PGE_DIGITAL_PASSWORD } = process.env;
105
+ const database = process.env.PGE_DIGITAL_DATABASE ?? 'PGE_DIGITAL';
106
+ const encrypt = parseBool(process.env.PGE_DIGITAL_ENCRYPT, true);
107
+ const trustServerCertificate = parseBool(process.env.PGE_DIGITAL_TRUST_CERT, true);
108
+ const port = Number(process.env.PGE_DIGITAL_PORT ?? '1433');
109
+
110
+ const tablesToInspect = ['carga', 'registro_tramitacao', 'tramitacao', 'usuario',
111
+ 'processo_administrativo', 'classificacao', 'especializada', 'acervo',
112
+ 'processo_judicial', 'parte', 'pessoa', 'tipo_polo'];
113
+
114
+ const connection = await new Promise((resolve, reject) => {
115
+ const conn = new Connection({
116
+ server: PGE_DIGITAL_HOST,
117
+ authentication: {
118
+ type: 'default',
119
+ options: {
120
+ userName: PGE_DIGITAL_USER,
121
+ password: PGE_DIGITAL_PASSWORD,
122
+ },
123
+ },
124
+ options: {
125
+ database,
126
+ encrypt,
127
+ trustServerCertificate,
128
+ port: Number.isFinite(port) ? port : 1433,
129
+ },
130
+ });
131
+
132
+ conn.on('connect', (err) => (err ? reject(err) : resolve(conn)));
133
+ conn.connect();
134
+ });
135
+
136
+ console.log(`Connected to ${database} on ${PGE_DIGITAL_HOST}\n`);
137
+
138
+ for (const tableName of tablesToInspect) {
139
+ try {
140
+ console.log(`\n=== Table: ${tableName} ===`);
141
+
142
+ const columns = await queryTableSchema(connection, tableName);
143
+ if (columns.length === 0) {
144
+ console.log(' Table not found or no columns');
145
+ continue;
146
+ }
147
+
148
+ console.log('Columns:');
149
+ columns.forEach(col => {
150
+ const nullable = col.IS_NULLABLE === 'YES' ? 'NULL' : 'NOT NULL';
151
+ const pk = col.IS_PRIMARY_KEY ? ' [PK]' : '';
152
+ const identity = col.IS_IDENTITY ? ' [IDENTITY]' : '';
153
+ let dataType = col.DATA_TYPE;
154
+ if (col.CHARACTER_MAXIMUM_LENGTH && col.CHARACTER_MAXIMUM_LENGTH > 0) {
155
+ dataType += `(${col.CHARACTER_MAXIMUM_LENGTH})`;
156
+ } else if (col.NUMERIC_PRECISION) {
157
+ dataType += `(${col.NUMERIC_PRECISION},${col.NUMERIC_SCALE || 0})`;
158
+ }
159
+ console.log(` ${col.COLUMN_NAME}: ${dataType} ${nullable}${pk}${identity}`);
160
+ });
161
+
162
+ const fks = await queryForeignKeys(connection, tableName);
163
+ if (fks.length > 0) {
164
+ console.log('Foreign Keys:');
165
+ fks.forEach(fk => {
166
+ console.log(` ${fk.PARENT_TABLE}.${fk.PARENT_COLUMN} -> ${fk.REFERENCED_TABLE}.${fk.REFERENCED_COLUMN}`);
167
+ });
168
+ }
169
+ } catch (err) {
170
+ console.error(`Error inspecting ${tableName}:`, err.message);
171
+ }
172
+ }
173
+
174
+ connection.close();
175
+ console.log('\n=== Done ===');
176
+ }
177
+
178
+ main().catch(err => {
179
+ console.error('Fatal error:', err);
180
+ process.exit(1);
181
+ });
@@ -1,11 +1,12 @@
1
1
  import { resolveInflector } from './inflection/index.mjs';
2
2
 
3
3
  export class BaseNamingStrategy {
4
- constructor(irregulars = {}, inflector = resolveInflector('en'), relationOverrides = {}) {
4
+ constructor(irregulars = {}, inflector = resolveInflector('en'), relationOverrides = {}, classNameOverrides = {}) {
5
5
  this.irregulars = new Map();
6
6
  this.inverseIrregulars = new Map();
7
7
  this.inflector = inflector;
8
8
  this.relationOverrides = new Map();
9
+ this.classNameOverrides = new Map();
9
10
 
10
11
  for (const [singular, plural] of Object.entries(irregulars)) {
11
12
  if (!singular || !plural) continue;
@@ -21,6 +22,12 @@ export class BaseNamingStrategy {
21
22
  if (!overrides || typeof overrides !== 'object') continue;
22
23
  this.relationOverrides.set(className, new Map(Object.entries(overrides)));
23
24
  }
25
+
26
+ // Build class name overrides map: tableName -> className
27
+ for (const [tableName, className] of Object.entries(classNameOverrides)) {
28
+ if (!tableName || !className) continue;
29
+ this.classNameOverrides.set(tableName, className);
30
+ }
24
31
  }
25
32
 
26
33
  applyRelationOverride(className, propertyName) {
@@ -80,6 +87,9 @@ export class BaseNamingStrategy {
80
87
  }
81
88
 
82
89
  classNameFromTable(tableName) {
90
+ if (this.classNameOverrides.has(tableName)) {
91
+ return this.classNameOverrides.get(tableName);
92
+ }
83
93
  return this.toPascalCase(this.singularize(tableName));
84
94
  }
85
95
 
@@ -113,20 +123,20 @@ export class BaseNamingStrategy {
113
123
  }
114
124
 
115
125
  export class EnglishNamingStrategy extends BaseNamingStrategy {
116
- constructor(irregulars = {}) {
117
- super(irregulars, resolveInflector('en'));
126
+ constructor(irregulars = {}, relationOverrides = {}, classNameOverrides = {}) {
127
+ super(irregulars, resolveInflector('en'), relationOverrides, classNameOverrides);
118
128
  }
119
129
  }
120
130
 
121
131
  export class PortugueseNamingStrategy extends BaseNamingStrategy {
122
- constructor(irregulars = {}) {
132
+ constructor(irregulars = {}, relationOverrides = {}, classNameOverrides = {}) {
123
133
  const inflector = resolveInflector('pt-BR');
124
- super({ ...inflector.defaultIrregulars, ...irregulars }, inflector);
134
+ super({ ...inflector.defaultIrregulars, ...irregulars }, inflector, relationOverrides, classNameOverrides);
125
135
  }
126
136
  }
127
137
 
128
- export const createNamingStrategy = (locale = 'en', irregulars, relationOverrides = {}) => {
138
+ export const createNamingStrategy = (locale = 'en', irregulars, relationOverrides = {}, classNameOverrides = {}) => {
129
139
  const inflector = resolveInflector(locale);
130
140
  const mergedIrregulars = { ...(inflector.defaultIrregulars || {}), ...(irregulars || {}) };
131
- return new BaseNamingStrategy(mergedIrregulars, inflector, relationOverrides);
141
+ return new BaseNamingStrategy(mergedIrregulars, inflector, relationOverrides, classNameOverrides);
132
142
  };