@andymic/pigeon 1.2.0 → 1.3.0

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/bin/pigeon.js CHANGED
@@ -1,18 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  /*
3
- * Copyright (c) 2024 Andreas Michael
3
+ * Copyright (c) 2025 Andreas Michael
4
4
  * This software is under the Apache 2.0 License
5
5
  */
6
6
  import { cli, run } from "../src/cli.js";
7
+ import { PigeonError } from "../src/index.js";
7
8
  try {
8
- const { exitCode, message, error } = await run(cli.flags);
9
- if (message) {
10
- console.log(message);
9
+ const result = await run(cli.flags);
10
+ if (result instanceof PigeonError) {
11
+ if (result.message !== "")
12
+ console.log(result.message);
13
+ console.error(result.error);
14
+ process.exit(result.exitCode);
11
15
  }
12
- if (error) {
13
- console.error(error);
16
+ else {
17
+ console.log("Generation Completed Successfully");
18
+ process.exit(0);
14
19
  }
15
- process.exit(exitCode);
16
20
  }
17
21
  catch (error) {
18
22
  console.error(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andymic/pigeon",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "author": "Andreas Michael <ateasm03@gmail.com>",
5
5
  "description": "Pigeon is a TypeScript-based tool for generating TypeScript classes and methods from PostgreSQL database schemas.",
6
6
  "keywords": [
@@ -32,8 +32,8 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "meow": "^13.2.0",
35
- "pg": "^8.14.1",
35
+ "pg": "^8.15.6",
36
36
  "prompt-sync": "^4.2.0"
37
37
  },
38
- "gitHead": "b163d7dac6444fad09fa7bf9bc2b812ed3f486ad"
38
+ "gitHead": "40d793e0267631471bde05ffddc53ba908e07512"
39
39
  }
package/src/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /*
2
- * Copyright (c) 2024 Andreas Michael
2
+ * Copyright (c) 2025 Andreas Michael
3
3
  * This software is under the Apache 2.0 License
4
4
  */
5
5
  import meow from "meow";
6
6
  import { createConfig } from "./config.js";
7
+ import { Database, deleteDir, guided, PigeonError, queryDB, runGeneration } from "./index.js";
7
8
  import * as path from "node:path";
8
- import { deleteDir, guided, runPigeon } from "./index.js";
9
9
  import fs from "node:fs";
10
10
  export const cli = meow(`
11
11
  Usage
@@ -69,14 +69,19 @@ export async function run(flags) {
69
69
  deleteDir(flags.output);
70
70
  if (flags.guided) {
71
71
  const params = guided();
72
- return await runPigeon(flags.output, params.host, params.port, params.db, params.user, params.pass);
72
+ const database = new Database(params.host, String(params.port), params.db, params.user, params.pass);
73
+ const result = await queryDB(database);
74
+ if (result instanceof PigeonError)
75
+ return result;
73
76
  }
74
77
  if (!fs.existsSync(path.join(process.cwd(), ".pigeon.json")))
75
- return {
76
- exitCode: 1,
77
- message: null,
78
- error: new Error("The configuration file does not exist. Generate one using the \"pigeon --init\" command"),
79
- };
78
+ return new PigeonError(1, "", new Error("The configuration file does not exist. Generate one using the \"pigeon --init\" command"));
80
79
  const params = JSON.parse(fs.readFileSync(flags.config).toString());
81
- return await runPigeon(flags.output, params.host, params.port, params.database, params.username, params.password);
80
+ const database = new Database(params.host, params.port, params.database, params.username, params.password);
81
+ const queryResult = await queryDB(database);
82
+ if (queryResult instanceof PigeonError)
83
+ return queryResult;
84
+ const generationResult = runGeneration(flags.output, database, queryResult.tables, queryResult.enums);
85
+ if (generationResult instanceof PigeonError)
86
+ return generationResult;
82
87
  }
package/src/config.js CHANGED
@@ -1,21 +1,13 @@
1
1
  /*
2
- * Copyright (c) 2024 Andreas Michael
2
+ * Copyright (c) 2025 Andreas Michael
3
3
  * This software is under the Apache 2.0 License
4
4
  */
5
+ import { PigeonError } from "./index.js";
5
6
  import fs from "node:fs";
6
7
  import * as path from "node:path";
7
8
  export function createConfig(dir) {
8
- if (fs.existsSync(path.join(dir, ".pigeon.json"))) {
9
- return {
10
- exitCode: 1,
11
- message: null,
12
- error: new Error("A Pigeon configuration file already exists."),
13
- };
14
- }
9
+ if (fs.existsSync(path.join(dir, ".pigeon.json")))
10
+ return new PigeonError(1, "", new Error("A Pigeon configuration file already exists."));
15
11
  fs.writeFileSync(path.join(dir, ".pigeon.json"), "{\n\t\"host\": \"localhost\",\n\t\"port\": 5432,\n\t\"database\": \"postgres\",\n\t\"username\": \"postgres\",\n\t\"password\": \"xxx\"\n}");
16
- return {
17
- exitCode: 0,
18
- message: "Configuration file successfully created!",
19
- error: null,
20
- };
12
+ return new PigeonError(0, "Configuration file successfully created!", null);
21
13
  }
package/src/index.js CHANGED
@@ -1,33 +1,110 @@
1
1
  /*
2
- * Copyright (c) 2024 Andreas Michael
2
+ * Copyright (c) 2025 Andreas Michael
3
3
  * This software is under the Apache 2.0 License
4
4
  */
5
- import { arrayMaker, getCombinations, nameBeautifier, queryMaker, runQuery, singularize, sleep, tabsInserter } from "./utils.js";
6
- import { types } from "./maps.js";
5
+ import { arrayMaker, consoleMessage, getCombinations, getType, nameBeautifier, queryMaker, runQuery, singularize, sleep, tabsInserter } from "./utils.js";
6
+ import prompt from "prompt-sync";
7
7
  import fs from "node:fs";
8
8
  import * as path from "node:path";
9
- import prompt from "prompt-sync";
9
+ export class PigeonError {
10
+ exitCode;
11
+ message;
12
+ error;
13
+ constructor(exitCode, message, error) {
14
+ this.exitCode = exitCode;
15
+ this.message = message;
16
+ this.error = error;
17
+ }
18
+ }
19
+ export class Database {
20
+ host;
21
+ port;
22
+ db;
23
+ user;
24
+ pass;
25
+ constructor(host, port, db, user, pass) {
26
+ this.host = host;
27
+ this.port = port;
28
+ this.db = db;
29
+ this.user = user;
30
+ this.pass = pass;
31
+ }
32
+ }
33
+ class Table {
34
+ table_schema;
35
+ table_name;
36
+ columns = [];
37
+ primaryKey;
38
+ foreignKeys;
39
+ unique;
40
+ constructor(table_schema, table_name, columns, primaryKey, foreignKeys, unique) {
41
+ this.table_schema = table_schema;
42
+ this.table_name = table_name;
43
+ this.columns = columns;
44
+ this.primaryKey = primaryKey;
45
+ this.foreignKeys = foreignKeys;
46
+ this.unique = unique;
47
+ }
48
+ }
49
+ class Enum {
50
+ name;
51
+ labels;
52
+ constructor(name, labels) {
53
+ this.name = name;
54
+ this.labels = labels;
55
+ }
56
+ }
57
+ class ColumnQueryRow {
58
+ column_name;
59
+ ordinal_position;
60
+ column_default;
61
+ is_nullable;
62
+ data_type;
63
+ udt_name;
64
+ is_identity;
65
+ identity_generation;
66
+ constructor(column_name, ordinal_position, column_default, is_nullable, data_type, udt_name, is_identity, identity_generation) {
67
+ this.column_name = column_name;
68
+ this.ordinal_position = ordinal_position;
69
+ this.column_default = column_default;
70
+ this.is_nullable = is_nullable;
71
+ this.data_type = data_type;
72
+ this.udt_name = udt_name;
73
+ this.is_identity = is_identity;
74
+ this.identity_generation = identity_generation;
75
+ }
76
+ }
77
+ class PrimaryKeyQueryRow {
78
+ column_name;
79
+ constructor(column_name) {
80
+ this.column_name = column_name;
81
+ }
82
+ }
83
+ class ForeignKeyQueryRow {
84
+ local_table;
85
+ local_column;
86
+ foreign_schema;
87
+ foreign_table;
88
+ foreign_column;
89
+ constructor(local_table, local_column, foreign_schema, foreign_table, foreign_column) {
90
+ this.local_table = local_table;
91
+ this.local_column = local_column;
92
+ this.foreign_schema = foreign_schema;
93
+ this.foreign_table = foreign_table;
94
+ this.foreign_column = foreign_column;
95
+ }
96
+ }
97
+ class UniqueQueryRow {
98
+ columns;
99
+ constructor(columns) {
100
+ this.columns = columns;
101
+ }
102
+ }
10
103
  function createDir(dirPath) {
11
104
  if (fs.existsSync(dirPath))
12
- return {
13
- exitCode: 1,
14
- message: null,
15
- error: new Error("Generation directory already exists. Add the --force flag if you want to overwrite it.")
16
- };
105
+ return new PigeonError(1, "", new Error("Generation directory already exists. Add the --force flag if you want to overwrite it."));
17
106
  else
18
- fs.mkdir(dirPath, (err) => {
19
- if (err)
20
- return {
21
- exitCode: 1,
22
- message: null,
23
- error: err
24
- };
25
- });
26
- return {
27
- exitCode: 0,
28
- message: null,
29
- error: null
30
- };
107
+ fs.mkdirSync(dirPath);
31
108
  }
32
109
  export function deleteDir(dirPath) {
33
110
  if (fs.existsSync(dirPath)) {
@@ -75,94 +152,65 @@ export function guided() {
75
152
  const pass = input("Database Password: ");
76
153
  return { host, port, db, user, pass };
77
154
  }
78
- export async function runPigeon(dir, host, port, db, user, pass) {
79
- const dirResult = createDir(dir);
80
- if (dirResult.exitCode !== 0)
81
- return dirResult;
155
+ export async function queryDB(db) {
82
156
  const tableQuery = await runQuery(`SELECT table_schema, table_name
83
157
  FROM information_schema.tables
84
158
  WHERE table_type = 'BASE TABLE'
85
159
  AND table_schema NOT IN
86
- ('pg_catalog', 'information_schema');`, [], host, port, db, user, pass);
160
+ ('pg_catalog', 'information_schema');`, [], db);
87
161
  if (typeof tableQuery === "undefined")
88
- return {
89
- exitCode: 1,
90
- message: null,
91
- error: new Error("An SQL error has occurred.")
92
- };
93
- let schemas = [];
94
- for (const table of tableQuery.rows) {
95
- if (schemas.includes(table.table_schema))
96
- continue;
97
- schemas.push(table.table_schema);
98
- }
99
- for (const schema of schemas)
100
- createDir(path.join(dir, schema));
162
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
101
163
  const customTypeQuery = await runQuery(`SELECT t.oid, t.typname
102
164
  FROM pg_type t
103
165
  WHERE (t.typrelid = 0 OR t.typrelid IN (SELECT oid FROM pg_class WHERE relkind = 'c'))
104
166
  AND t.typelem = 0
105
167
  AND t.typnamespace NOT IN
106
- (SELECT oid FROM pg_namespace WHERE nspname IN ('pg_catalog', 'information_schema'));`, [], host, port, db, user, pass);
168
+ (SELECT oid FROM pg_namespace WHERE nspname IN ('pg_catalog', 'information_schema'));`, [], db);
107
169
  if (typeof customTypeQuery === "undefined")
108
- return {
109
- exitCode: 1,
110
- message: null,
111
- error: new Error("An SQL error has occurred.")
112
- };
113
- const customTypes = [];
170
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
171
+ const enums = [];
114
172
  for (const type of customTypeQuery.rows) {
115
173
  const enumQuery = await runQuery(`SELECT enumlabel
116
174
  FROM pg_enum
117
175
  WHERE enumtypid = $1::oid
118
- ORDER BY enumsortorder;`, [type.oid], host, port, db, user, pass);
176
+ ORDER BY enumsortorder;`, [type.oid], db);
119
177
  if (typeof enumQuery === "undefined")
120
- return {
121
- exitCode: 1,
122
- message: null,
123
- error: new Error("An SQL error has occurred.")
124
- };
178
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
125
179
  let labels = [];
126
180
  for (const enumLabel of enumQuery.rows)
127
181
  labels.push(enumLabel.enumlabel);
128
- customTypes.push({
129
- name: type.typname,
130
- labels: labels
131
- });
182
+ enums.push(new Enum(type.typname, labels));
132
183
  }
184
+ const tables = [];
133
185
  for (const table of tableQuery.rows) {
134
- const columnQuery = await runQuery(`SELECT *
186
+ const columnQuery = await runQuery(`SELECT column_name,
187
+ ordinal_position,
188
+ column_default,
189
+ is_nullable,
190
+ data_type,
191
+ udt_name,
192
+ is_identity,
193
+ identity_generation
135
194
  FROM information_schema.columns
136
195
  WHERE table_name = $1::varchar
137
- AND table_schema = $2::varchar;`, [table.table_name, table.table_schema], host, port, db, user, pass);
196
+ AND table_schema = $2::varchar;`, [table.table_name, table.table_schema], db);
138
197
  if (typeof columnQuery === "undefined")
139
- return {
140
- exitCode: 1,
141
- message: null,
142
- error: new Error("An SQL error has occurred.")
143
- };
198
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
144
199
  const pKeyQuery = await runQuery(`SELECT ku.column_name
145
200
  FROM information_schema.table_constraints AS tc
146
201
  INNER JOIN information_schema.key_column_usage AS ku
147
202
  ON tc.constraint_type = 'PRIMARY KEY'
148
203
  AND tc.constraint_name = ku.constraint_name
149
204
  WHERE tc.table_schema = $1::varchar
150
- AND tc.table_name = $2::varchar;`, [table.table_schema, table.table_name], host, port, db, user, pass);
205
+ AND tc.table_name = $2::varchar;`, [table.table_schema, table.table_name], db);
151
206
  if (typeof pKeyQuery === "undefined")
152
- return {
153
- exitCode: 1,
154
- message: null,
155
- error: new Error("An SQL error has occurred.")
156
- };
157
- let pKeys = [];
158
- for (let pKey of pKeyQuery.rows)
159
- pKeys.push(pKey.column_name);
207
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
160
208
  const fKeyQuery = await runQuery(`SELECT kcu1.table_schema AS local_schema,
161
209
  kcu1.table_name AS local_table,
162
210
  kcu1.column_name AS local_column,
163
- kcu2.table_schema AS referenced_schema,
164
- kcu2.table_name AS referenced_table,
165
- kcu2.column_name AS referenced_column
211
+ kcu2.table_schema AS foreign_schema,
212
+ kcu2.table_name AS foreign_table,
213
+ kcu2.column_name AS foreign_column
166
214
  FROM information_schema.referential_constraints AS rc
167
215
  INNER JOIN information_schema.key_column_usage AS kcu1
168
216
  ON kcu1.constraint_catalog = rc.constraint_catalog
@@ -175,13 +223,9 @@ export async function runPigeon(dir, host, port, db, user, pass) {
175
223
  AND kcu2.constraint_name = rc.unique_constraint_name
176
224
  AND kcu2.ordinal_position = kcu1.ordinal_position
177
225
  WHERE kcu1.table_schema = $1::varchar
178
- AND kcu1.table_name = $2::varchar;`, [table.table_schema, table.table_name], host, port, db, user, pass);
226
+ AND kcu1.table_name = $2::varchar;`, [table.table_schema, table.table_name], db);
179
227
  if (typeof fKeyQuery === "undefined")
180
- return {
181
- exitCode: 1,
182
- message: null,
183
- error: new Error("An SQL error has occurred.")
184
- };
228
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
185
229
  const uniqueQuery = await runQuery(`SELECT array_agg(a.attname) AS columns
186
230
  FROM pg_constraint AS c
187
231
  CROSS JOIN LATERAL unnest(c.conkey) AS k(c)
@@ -190,61 +234,88 @@ export async function runPigeon(dir, host, port, db, user, pass) {
190
234
  AND c.connamespace = $1::regnamespace
191
235
  AND c.conrelid = $2::regclass
192
236
  GROUP BY c.conrelid;
193
- `, [table.table_schema, table.table_name], host, port, db, user, pass);
237
+ `, [table.table_schema, table.table_name], db);
194
238
  if (typeof uniqueQuery === "undefined")
195
- return {
196
- exitCode: 1,
197
- message: null,
198
- error: new Error("An SQL error has occurred.")
199
- };
239
+ return new PigeonError(1, "", new Error("An SQL error has occurred."));
200
240
  let uniques = [];
201
241
  if (uniqueQuery.rowCount > 0)
202
242
  uniques = uniqueQuery.rows[0].columns.slice(1, -1).split(",");
203
- let ts = clientMaker(0, host, port, db, user, pass);
243
+ tables.push(new Table(table.table_schema, table.table_name, columnQuery.rows, pKeyQuery.rows[0], fKeyQuery.rows, { columns: uniques }));
244
+ }
245
+ return {
246
+ tables: tables,
247
+ enums: enums
248
+ };
249
+ }
250
+ export function runGeneration(dir, db, tables, enums) {
251
+ if (!tables)
252
+ return new PigeonError(1, "", new Error("No tables were found."));
253
+ const dirResult = createDir(dir);
254
+ if (dirResult instanceof PigeonError)
255
+ return dirResult;
256
+ let schemas = [];
257
+ for (const table of tables) {
258
+ if (schemas.includes(table.table_schema))
259
+ continue;
260
+ schemas.push(table.table_schema);
261
+ }
262
+ for (const schema of schemas) {
263
+ const dirResult = createDir(path.join(dir, schema));
264
+ if (dirResult instanceof PigeonError)
265
+ return dirResult;
266
+ }
267
+ for (const table of tables) {
268
+ let ts = clientMaker(0, db);
204
269
  ts += "\n\n";
205
- for (const customType of customTypes) {
206
- for (const column of columnQuery.rows) {
207
- if (customType.name === column.udt_name) {
208
- const enumName = nameBeautifier(customType.name).replaceAll(" ", "");
209
- ts += "/**\n An Enum representing the " + nameBeautifier(customType.name).toLowerCase() + ".\n * @readonly\n * @enum {string}\n */\n";
210
- ts += "class " + enumName + " {\n";
211
- let longestLabel = 0;
212
- for (const label of customType.labels)
213
- if (label.length > longestLabel)
214
- longestLabel = label.length;
215
- for (const label of customType.labels)
216
- ts += "\tstatic " + label.toUpperCase().replaceAll(/[^a-zA-Z0-9$]/g, "_") + ": string" + " ".repeat(longestLabel - label.length + 1) + "= \"" + label + "\";\n";
217
- ts += "}\n\n";
270
+ if (enums) {
271
+ for (const cEnum of enums) {
272
+ for (const column of table.columns) {
273
+ if (cEnum.name === column.udt_name) {
274
+ const enumName = nameBeautifier(cEnum.name).replaceAll(" ", "");
275
+ ts += "/**\n An Enum representing the " + nameBeautifier(cEnum.name).toLowerCase() + ".\n * @readonly\n * @enum {string}\n */\n";
276
+ ts += "class " + enumName + " {\n";
277
+ let longestLabel = 0;
278
+ for (const label of cEnum.labels)
279
+ if (label.length > longestLabel)
280
+ longestLabel = label.length;
281
+ for (const label of cEnum.labels)
282
+ ts += "\tstatic " + label.toUpperCase().replaceAll(/[^a-zA-Z0-9$]/g, "_") + ": string" + " ".repeat(longestLabel - label.length + 1) + "= \"" + label + "\";\n";
283
+ ts += "}\n\n";
284
+ }
218
285
  }
219
286
  }
220
287
  }
221
- ts += createClass(table.table_name, columnQuery.rows, pKeys, fKeyQuery.rows);
288
+ ts += createClass(table.table_name, table.columns, table.primaryKey?.column_name, table.foreignKeys);
222
289
  ts += "\n\n";
223
- ts += createGetAll(table.table_schema, table.table_name, columnQuery.rows);
290
+ ts += createGetAll(table.table_schema, table.table_name, table.columns);
224
291
  ts += "\n\n";
225
- let keys = [...pKeys];
226
- for (const fKey of fKeyQuery.rows)
227
- keys.push(fKey.local_column.replaceAll(" ", ""));
228
- keys = keys.concat(uniques);
292
+ let keys = [];
293
+ if (table.primaryKey)
294
+ keys.push(table.primaryKey.column_name);
295
+ if (table.foreignKeys)
296
+ for (const fKey of table.foreignKeys)
297
+ keys.push(fKey.local_column.replaceAll(" ", ""));
298
+ if (table.unique)
299
+ keys = keys.concat(table.unique.columns);
229
300
  keys = [...new Set(keys)];
230
301
  for (const keyCombination of getCombinations(keys)) {
231
- ts += createGet(table.table_schema, table.table_name, columnQuery.rows, keyCombination);
302
+ ts += createGet(table.table_schema, table.table_name, table.columns, keyCombination);
232
303
  ts += "\n\n";
233
304
  }
234
305
  let nonDefaults = [];
235
306
  let softDefaults = [];
236
307
  let hardDefaults = [];
237
- for (const column of columnQuery.rows) {
308
+ for (const column of table.columns) {
238
309
  if (column.column_default === null && column.is_identity === "NO")
239
310
  nonDefaults.push(column);
240
- if ((column.column_default !== null && !column.column_default.includes("nextval")) || (column.is_identity === "YES" && column.identity_generation === "BY DEFAULT"))
311
+ else if ((column.column_default !== null && !column.column_default.includes("nextval")) || (column.is_identity === "YES" && column.identity_generation === "BY DEFAULT"))
241
312
  softDefaults.push(column);
242
- if ((column.column_default !== null && column.column_default.includes("nextval")) || (column.is_identity === "YES" && column.identity_generation === "ALWAYS"))
313
+ else if ((column.column_default !== null && column.column_default.includes("nextval")) || (column.is_identity === "YES" && column.identity_generation === "ALWAYS"))
243
314
  hardDefaults.push(column);
244
315
  }
245
- ts += createAdd(table.table_schema, table.table_name, nonDefaults, [], hardDefaults.concat(softDefaults), fKeyQuery.rows) + "\n\n";
316
+ ts += createAdd(table.table_schema, table.table_name, nonDefaults, [], hardDefaults.concat(softDefaults), table.foreignKeys) + "\n\n";
246
317
  for (const softCombination of getCombinations(softDefaults))
247
- ts += createAdd(table.table_schema, table.table_name, nonDefaults, softCombination, hardDefaults.concat(softDefaults.filter(n => !getCombinations(softDefaults).includes(n))), fKeyQuery.rows) + "\n\n";
318
+ ts += createAdd(table.table_schema, table.table_name, nonDefaults, softCombination, hardDefaults.concat(softDefaults.filter(n => !getCombinations(softDefaults).includes([n]))), table.foreignKeys) + "\n\n";
248
319
  ts = ts.slice(0, -2);
249
320
  const regex = /import ({?.*?}?) from "(.*?)";\n/g;
250
321
  let importObjects = [];
@@ -291,34 +362,29 @@ export async function runPigeon(dir, host, port, db, user, pass) {
291
362
  ts = importString + ts;
292
363
  fs.writeFileSync(path.join(dir, table.table_schema, table.table_name + ".ts"), ts);
293
364
  }
294
- return {
295
- exitCode: 0,
296
- message: "Generation Completed Successfully",
297
- error: null
298
- };
299
365
  }
300
- function createClass(tableName, columns, primaryKeys, foreignKeys) {
366
+ function createClass(tableName, columns, primaryKey, foreignKeys) {
301
367
  let text = "";
302
368
  text += "export class " + singularize(nameBeautifier(tableName)).replaceAll(" ", "") + " {\n";
303
369
  for (const column of columns) {
304
- let dataType = types.get(column.data_type);
305
- if (dataType === undefined)
306
- dataType = nameBeautifier(column.udt_name).replaceAll(" ", "");
307
- if (column.is_nullable == "YES")
370
+ let dataType = getType(column.data_type, column.udt_name).replaceAll(" ", "");
371
+ if (column.is_nullable === "YES")
308
372
  dataType += " | undefined";
309
373
  let isPrimaryKey = false;
310
- for (const pKey of primaryKeys)
311
- if (pKey === column.column_name)
312
- isPrimaryKey = true;
313
- let foreignKeyIndex = -1;
314
- for (let i = 0; i < foreignKeys.length; i++)
315
- if (foreignKeys[i].local_column === column.column_name)
316
- foreignKeyIndex = i;
374
+ if (column.column_name === primaryKey)
375
+ isPrimaryKey = true;
376
+ let foreignKeyIndex;
377
+ if (foreignKeys)
378
+ for (let i = 0; i < foreignKeys.length; i++)
379
+ if (foreignKeys[i].local_column === column.column_name)
380
+ foreignKeyIndex = i;
317
381
  text += "\t/**\n";
318
382
  if (isPrimaryKey)
319
383
  text += "\t * A primary key representing the " + nameBeautifier(column.column_name) + " for the " + nameBeautifier(tableName) + " table.\n";
320
- else if (foreignKeyIndex !== -1)
321
- text += "\t * A foreign key representing the " + nameBeautifier(column.column_name) + " for the " + nameBeautifier(tableName) + " table and referencing the " + nameBeautifier(foreignKeys[foreignKeyIndex].referenced_column) + " in the " + nameBeautifier(foreignKeys[foreignKeyIndex].referenced_table) + " table in the " + nameBeautifier(foreignKeys[foreignKeyIndex].referenced_schema) + " schema.\n";
384
+ else if (foreignKeys && foreignKeyIndex)
385
+ text += "\t * A foreign key representing the " + nameBeautifier(column.column_name) + " for the " + nameBeautifier(tableName) + " table and referencing the " + nameBeautifier(foreignKeys[foreignKeyIndex].foreign_column) + " in the " + nameBeautifier(foreignKeys[foreignKeyIndex].foreign_table) + " table in the " + nameBeautifier(foreignKeys[foreignKeyIndex].foreign_schema) + " schema.\n";
386
+ else if (column.column_name.toLowerCase().startsWith('is_'))
387
+ text += "\t * Indicates whether this record in the table " + nameBeautifier(tableName) + " is currently " + nameBeautifier(column.column_name.slice(3)).toLowerCase() + ".\n";
322
388
  else
323
389
  text += "\t * The " + nameBeautifier(column.column_name) + " for the " + nameBeautifier(tableName) + " table.\n";
324
390
  text += "\t * @type {" + dataType + "}\n";
@@ -332,7 +398,7 @@ function createClass(tableName, columns, primaryKeys, foreignKeys) {
332
398
  else
333
399
  text += " = new Date(" + column.column_default.replace(' ', 'T') + ")";
334
400
  }
335
- else if (dataType === "number")
401
+ else if (dataType === "number" || dataType === "boolean")
336
402
  text += " = " + column.column_default;
337
403
  else
338
404
  text += " = \"" + column.column_default + "\"";
@@ -345,24 +411,23 @@ function createClass(tableName, columns, primaryKeys, foreignKeys) {
345
411
  text += "\t * Creates a new object for the " + nameBeautifier(tableName) + " table.\n";
346
412
  text += "\t * \n";
347
413
  for (const column of columns) {
348
- let dataType = types.get(column.data_type);
349
- if (dataType === undefined)
350
- dataType = nameBeautifier(column.udt_name).replaceAll(" ", "");
414
+ let dataType = getType(column.data_type, column.udt_name).replaceAll(" ", "");
351
415
  text += "\t * ";
352
416
  text += "@param {" + dataType;
353
- if (column.is_nullable == "YES")
417
+ if (column.is_nullable === "YES")
354
418
  text += " | undefined";
355
419
  text += "} " + column.column_name;
356
- text += " - The " + nameBeautifier(column.column_name) + " of the " + nameBeautifier(tableName) + " table. \n";
420
+ if (!column.column_name.toLowerCase().startsWith('is_'))
421
+ text += " - The " + nameBeautifier(column.column_name) + " of the " + nameBeautifier(tableName) + " table. \n";
422
+ else
423
+ text += " - Indicates whether this record in the table " + nameBeautifier(tableName) + " is currently " + nameBeautifier(column.column_name.slice(3)).toLowerCase() + ".\n";
357
424
  }
358
425
  text += "\t */\n";
359
426
  text += "\tconstructor(";
360
427
  for (const column of columns) {
361
- let dataType = types.get(column.data_type);
362
- if (dataType === undefined)
363
- dataType = nameBeautifier(column.udt_name).replaceAll(" ", "");
428
+ let dataType = getType(column.data_type, column.udt_name).replaceAll(" ", "");
364
429
  text += column.column_name + ": " + dataType;
365
- if (column.is_nullable == "YES")
430
+ if (column.is_nullable === "YES")
366
431
  text += " | undefined";
367
432
  text += ", ";
368
433
  }
@@ -374,14 +439,14 @@ function createClass(tableName, columns, primaryKeys, foreignKeys) {
374
439
  text += "}";
375
440
  return text;
376
441
  }
377
- export function clientMaker(baseTabs, host, port, db, user, pass) {
442
+ export function clientMaker(baseTabs, db) {
378
443
  let text = "";
379
444
  text += tabsInserter(baseTabs) + "const client = new Client({\n";
380
- text += tabsInserter(baseTabs + 1) + "host: \"" + host + "\",\n";
381
- text += tabsInserter(baseTabs + 1) + "port: " + port + ",\n";
382
- text += tabsInserter(baseTabs + 1) + "database: \"" + db + "\",\n";
383
- text += tabsInserter(baseTabs + 1) + "user: \"" + user + "\",\n";
384
- text += tabsInserter(baseTabs + 1) + "password: \"" + pass + "\"\n";
445
+ text += tabsInserter(baseTabs + 1) + "host: \"" + db.host + "\",\n";
446
+ text += tabsInserter(baseTabs + 1) + "port: " + db.port + ",\n";
447
+ text += tabsInserter(baseTabs + 1) + "database: \"" + db.db + "\",\n";
448
+ text += tabsInserter(baseTabs + 1) + "user: \"" + db.user + "\",\n";
449
+ text += tabsInserter(baseTabs + 1) + "password: \"" + db.pass + "\"\n";
385
450
  text += tabsInserter(baseTabs) + "});";
386
451
  return text;
387
452
  }
@@ -413,10 +478,12 @@ function createGet(tableSchema, tableName, columns, keys) {
413
478
  text = text.slice(0, -5) + ".\n";
414
479
  text += " *\n";
415
480
  for (const key of keys) {
416
- const column = columns.find(column => column.column_name == key);
417
- let dataType = types.get(column.data_type);
418
- if (dataType === undefined)
419
- dataType = nameBeautifier(column.udt_name).replaceAll(" ", "");
481
+ const column = columns.find(column => column.column_name === key);
482
+ if (!column) {
483
+ consoleMessage("WRN", `Key ${key} was not found in the columns of table ${tableName}.`);
484
+ continue;
485
+ }
486
+ let dataType = getType(column.data_type, column.udt_name).replaceAll(" ", "");
420
487
  text += " * ";
421
488
  text += "@param {" + dataType;
422
489
  text += "} " + column.column_name;
@@ -429,8 +496,14 @@ function createGet(tableSchema, tableName, columns, keys) {
429
496
  text += nameBeautifier(key).replaceAll(" ", "") + "And";
430
497
  text = text.slice(0, -3);
431
498
  text += "(";
432
- for (const key of keys)
433
- text += key + ": " + (types.get(columns.find(column => column.column_name == key).data_type) || nameBeautifier(columns.find(column => column.column_name == key).udt_name).replaceAll(" ", "")) + ", ";
499
+ for (const key of keys) {
500
+ const column = columns.find(column => column.column_name === key);
501
+ if (!column) {
502
+ consoleMessage("WRN", `Key ${key} was not found in the columns of table ${tableName}.`);
503
+ continue;
504
+ }
505
+ text += key + ": " + getType(column.data_type, column.udt_name) + ", ";
506
+ }
434
507
  text = text.slice(0, -2);
435
508
  text += "): Promise<" + className + "[]> {\n";
436
509
  text += "\tif (";
@@ -441,7 +514,12 @@ function createGet(tableSchema, tableName, columns, keys) {
441
514
  let query = "SELECT * FROM " + tableSchema + "." + tableName + " WHERE ";
442
515
  let parameters = "";
443
516
  for (let i = 0; i < keys.length; i++) {
444
- query += keys[i] + " = " + "$" + (i + 1) + "::" + (columns.find(column => column.column_name == keys[i]).data_type || columns.find(column => column.column_name == keys[i]).udt_name) + " AND ";
517
+ const column = columns.find(column => column.column_name === keys[i]);
518
+ if (!column) {
519
+ consoleMessage("WRN", `Key ${keys[i]} was not found in the columns of table ${tableName}.`);
520
+ continue;
521
+ }
522
+ query += keys[i] + " = " + "$" + (i + 1) + "::" + (column.data_type || column.udt_name) + " AND ";
445
523
  parameters += keys[i] + ", ";
446
524
  }
447
525
  query = query.slice(0, -5) + ";";
@@ -456,13 +534,15 @@ function createGet(tableSchema, tableName, columns, keys) {
456
534
  function createAdd(tableSchema, tableName, nonDefaults, softDefaults, hardDefaults, foreignKeys) {
457
535
  let text = "";
458
536
  const className = singularize(nameBeautifier(tableName)).replaceAll(" ", "");
459
- for (const foreignKey of foreignKeys) {
460
- if ((tableSchema == foreignKey.referenced_schema) && (tableName == foreignKey.referenced_table))
461
- continue;
462
- text += "import {get" + nameBeautifier(foreignKey.referenced_table).replaceAll(" ", "") + "By" + nameBeautifier(foreignKey.referenced_column).replaceAll(" ", "") + "} from \".";
463
- if (tableSchema !== foreignKey.referenced_schema)
464
- text += "./" + foreignKey.referenced_schema;
465
- text += "/" + foreignKey.referenced_table + ".js\";\n";
537
+ if (foreignKeys) {
538
+ for (const foreignKey of foreignKeys) {
539
+ if ((tableSchema === foreignKey.foreign_schema) && (tableName === foreignKey.foreign_table))
540
+ continue;
541
+ text += "import {get" + nameBeautifier(foreignKey.foreign_table).replaceAll(" ", "") + "By" + nameBeautifier(foreignKey.foreign_column).replaceAll(" ", "") + "} from \".";
542
+ if (tableSchema !== foreignKey.foreign_schema)
543
+ text += "./" + foreignKey.foreign_schema;
544
+ text += "/" + foreignKey.foreign_table + ".js\";\n";
545
+ }
466
546
  }
467
547
  text += "/**\n";
468
548
  text += " * Adds the provided " + className + " object to the database.\n";
@@ -470,9 +550,7 @@ function createAdd(tableSchema, tableName, nonDefaults, softDefaults, hardDefaul
470
550
  let columns = nonDefaults.concat(softDefaults);
471
551
  columns.sort((a, b) => a.ordinal_position - b.ordinal_position);
472
552
  for (const column of columns) {
473
- let dataType = types.get(column.data_type);
474
- if (dataType === undefined)
475
- dataType = nameBeautifier(column.udt_name).replaceAll(" ", "");
553
+ let dataType = getType(column.data_type, column.udt_name).replaceAll(" ", "");
476
554
  text += " * ";
477
555
  text += "@param {" + dataType;
478
556
  if (column.is_nullable === "YES")
@@ -481,7 +559,7 @@ function createAdd(tableSchema, tableName, nonDefaults, softDefaults, hardDefaul
481
559
  text += " - The " + nameBeautifier(column.column_name) + " to be inserted into the " + nameBeautifier(tableName) + " table.\n";
482
560
  }
483
561
  text += " * @returns {Promise<" + className + ">} - A Promise object returning the inserted " + nameBeautifier(tableName) + ".\n";
484
- if (foreignKeys.length > 0) {
562
+ if (foreignKeys && foreignKeys.length > 0) {
485
563
  text += " * @throws string An exception in the case of the ";
486
564
  for (const foreignKey of foreignKeys)
487
565
  text += nameBeautifier(foreignKey.local_column) + " or the ";
@@ -498,9 +576,7 @@ function createAdd(tableSchema, tableName, nonDefaults, softDefaults, hardDefaul
498
576
  }
499
577
  text += "(";
500
578
  for (const column of columns) {
501
- let dataType = types.get(column.data_type);
502
- if (dataType === undefined)
503
- dataType = nameBeautifier(column.udt_name).replaceAll(" ", "");
579
+ let dataType = getType(column.data_type, column.udt_name);
504
580
  text += column.column_name + ": " + dataType;
505
581
  if (column.is_nullable === "YES")
506
582
  text += " | undefined";
@@ -508,18 +584,25 @@ function createAdd(tableSchema, tableName, nonDefaults, softDefaults, hardDefaul
508
584
  }
509
585
  text = text.slice(0, -2);
510
586
  text += "): Promise<" + className + "> {\n";
511
- for (const foreignKey of foreignKeys) {
512
- if (columns.find(column => column.column_name == foreignKey.local_column).is_nullable === "YES") {
513
- text += "\tif (" + foreignKey.local_column + ") {\n";
514
- text += "\t\tconst verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + " = await get" + nameBeautifier(foreignKey.referenced_table).replaceAll(" ", "") + "By" + nameBeautifier(foreignKey.referenced_column).replaceAll(" ", "") + "(" + foreignKey.local_column + ");\n";
515
- text += "\t\tif (verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + ".length === 0)\n";
516
- text += "\t\t\tthrow \"The " + nameBeautifier(foreignKey.local_column) + " provided does not exist.\";\n";
517
- text += "\t}\n\n";
518
- }
519
- else {
520
- text += "\tconst verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + " = await get" + nameBeautifier(foreignKey.referenced_table).replaceAll(" ", "") + "By" + nameBeautifier(foreignKey.referenced_column).replaceAll(" ", "") + "(" + foreignKey.local_column + ");\n";
521
- text += "\tif (verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + ".length === 0)\n";
522
- text += "\t\tthrow \"The " + nameBeautifier(foreignKey.local_column) + " provided does not exist.\";\n\n";
587
+ if (foreignKeys) {
588
+ for (const foreignKey of foreignKeys) {
589
+ const column = columns.find(column => column.column_name === foreignKey.local_column);
590
+ if (!column) {
591
+ consoleMessage("WRN", `Key ${foreignKey} was not found in the columns of table ${tableName}.`);
592
+ continue;
593
+ }
594
+ if (column.is_nullable === "YES") {
595
+ text += "\tif (" + foreignKey.local_column + ") {\n";
596
+ text += "\t\tconst verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + " = await get" + nameBeautifier(foreignKey.foreign_table).replaceAll(" ", "") + "By" + nameBeautifier(foreignKey.foreign_column).replaceAll(" ", "") + "(" + foreignKey.local_column + ");\n";
597
+ text += "\t\tif (verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + ".length === 0)\n";
598
+ text += "\t\t\tthrow \"The " + nameBeautifier(foreignKey.local_column) + " provided does not exist.\";\n";
599
+ text += "\t}\n\n";
600
+ }
601
+ else {
602
+ text += "\tconst verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + " = await get" + nameBeautifier(foreignKey.foreign_table).replaceAll(" ", "") + "By" + nameBeautifier(foreignKey.foreign_column).replaceAll(" ", "") + "(" + foreignKey.local_column + ");\n";
603
+ text += "\tif (verify" + nameBeautifier(foreignKey.local_column).replaceAll(" ", "") + ".length === 0)\n";
604
+ text += "\t\tthrow \"The " + nameBeautifier(foreignKey.local_column) + " provided does not exist.\";\n\n";
605
+ }
523
606
  }
524
607
  }
525
608
  let query = "INSERT INTO " + tableSchema + "." + tableName + " (";
@@ -529,9 +612,11 @@ function createAdd(tableSchema, tableName, nonDefaults, softDefaults, hardDefaul
529
612
  query += ") VALUES (";
530
613
  let parameters = "";
531
614
  for (let i = 0; i < columns.length; i++) {
532
- let dataType = columns[i].data_type;
533
- if (dataType === "USER-DEFINED")
534
- dataType = columns[i].udt_name;
615
+ let dataType = columns[i].udt_name;
616
+ if (dataType[0] === "_")
617
+ dataType = dataType.slice(1) + "[]";
618
+ else if (columns[i].data_type !== "USER-DEFINED")
619
+ dataType = columns[i].data_type;
535
620
  query += "$" + (i + 1) + "::" + dataType + ", ";
536
621
  parameters += columns[i].column_name + ", ";
537
622
  }
package/src/utils.js CHANGED
@@ -1,16 +1,17 @@
1
1
  /*
2
- * Copyright (c) 2024 Andreas Michael
2
+ * Copyright (c) 2025 Andreas Michael
3
3
  * This software is under the Apache 2.0 License
4
4
  */
5
5
  import pg from "pg";
6
+ import { types } from "./maps.js";
6
7
  const { Client } = pg;
7
- export async function runQuery(command, parameters, host, port, db, username, password) {
8
+ export async function runQuery(command, parameters, db) {
8
9
  const client = new Client({
9
- host: host,
10
- port: port,
11
- database: db,
12
- user: username,
13
- password: password,
10
+ host: db.host,
11
+ port: Number(db.port),
12
+ database: db.db,
13
+ user: db.user,
14
+ password: db.pass,
14
15
  });
15
16
  try {
16
17
  await client.connect();
@@ -149,3 +150,16 @@ export function getCombinations(valuesArray) {
149
150
  combinations.sort((a, b) => a.length - b.length);
150
151
  return combinations;
151
152
  }
153
+ export function getType(dataType, udtName) {
154
+ let isArray = false;
155
+ if (dataType === "ARRAY") {
156
+ dataType = udtName.slice(1);
157
+ isArray = true;
158
+ }
159
+ let foundDataType = types.get(dataType);
160
+ if (foundDataType === undefined)
161
+ foundDataType = nameBeautifier(udtName).replaceAll(" ", "");
162
+ if (isArray)
163
+ foundDataType += "[]";
164
+ return foundDataType;
165
+ }