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
|
@@ -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
|
-
|
|
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
|
};
|