@devmoods/express-extras 0.60.0 → 0.61.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/dist/manage.js CHANGED
@@ -71,9 +71,9 @@ export function registerDefaultCommands(app) {
71
71
  path: openApiFile,
72
72
  },
73
73
  output: {
74
- format: 'prettier',
74
+ format: false,
75
75
  path: options.output,
76
- lint: 'eslint',
76
+ lint: false,
77
77
  tsConfigPath: resolve('./tsconfig.json'),
78
78
  },
79
79
  plugins: [
@@ -91,4 +91,22 @@ export function registerDefaultCommands(app) {
91
91
  });
92
92
  }));
93
93
  });
94
+ registerManagementCommand((command, cli) => {
95
+ command('generate-db')
96
+ .option('--output <string>', 'Output file path', 'src/types/db.gen.ts')
97
+ .option('--connection <string>', 'Database connection string')
98
+ .action(cli.wrap(async (options) => {
99
+ const { generateDatabaseTypes } = await import('./postgres-ts-generator.js');
100
+ try {
101
+ await generateDatabaseTypes({
102
+ outputPath: options.output,
103
+ connectionString: options.connection || config.value.POSTGRES_URL,
104
+ });
105
+ cli.writeLine(`✅ Database types generated successfully at: ${options.output}\n`);
106
+ }
107
+ catch (error) {
108
+ cli.writeLine(`❌ Failed to generate database types: ${error}\n`);
109
+ }
110
+ }));
111
+ });
94
112
  }
@@ -0,0 +1,11 @@
1
+ export interface GenerateDatabaseTypesOptions {
2
+ outputPath: string;
3
+ connectionString: string;
4
+ schema?: string;
5
+ excludeTables?: string[];
6
+ includeTables?: string[];
7
+ }
8
+ /**
9
+ * Generates complete TypeScript definitions from database schema
10
+ */
11
+ export declare function generateDatabaseTypes(options: GenerateDatabaseTypesOptions): Promise<void>;
@@ -0,0 +1,114 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { resolve, dirname } from 'node:path';
3
+ import {} from 'pg-structure';
4
+ import { pgStructure } from 'pg-structure/dist/main.js';
5
+ /**
6
+ * Converts PostgreSQL data type to TypeScript type
7
+ */
8
+ function pgTypeToTsType(pgType, isNullable) {
9
+ const nullableSuffix = isNullable ? ' | null' : '';
10
+ switch (pgType.name.toLowerCase()) {
11
+ case 'uuid':
12
+ return `string${nullableSuffix}`;
13
+ case 'text':
14
+ case 'varchar':
15
+ case 'char':
16
+ case 'character varying':
17
+ return `string${nullableSuffix}`;
18
+ case 'integer':
19
+ case 'int':
20
+ case 'int2':
21
+ case 'int4':
22
+ case 'int8':
23
+ case 'serial':
24
+ case 'smallint':
25
+ return `number${nullableSuffix}`;
26
+ case 'bigint':
27
+ case 'bigserial':
28
+ case 'decimal':
29
+ case 'numeric':
30
+ return `string${nullableSuffix}`;
31
+ case 'real':
32
+ case 'double precision':
33
+ case 'float':
34
+ case 'float4':
35
+ case 'float8':
36
+ return `number${nullableSuffix}`;
37
+ case 'boolean':
38
+ case 'bool':
39
+ return `boolean${nullableSuffix}`;
40
+ case 'timestamp':
41
+ case 'timestamp with time zone':
42
+ case 'timestamptz':
43
+ return `Date${nullableSuffix}`;
44
+ case 'date':
45
+ return `Date${nullableSuffix}`;
46
+ case 'time':
47
+ case 'time with time zone':
48
+ return `string${nullableSuffix}`;
49
+ case 'json':
50
+ case 'jsonb':
51
+ return `Record<string, any>${nullableSuffix}`;
52
+ case 'bytea':
53
+ return `Buffer${nullableSuffix}`;
54
+ case 'inet':
55
+ case 'cidr':
56
+ return `string${nullableSuffix}`;
57
+ case 'array':
58
+ return `any[]${nullableSuffix}`;
59
+ default:
60
+ return `any${nullableSuffix}`;
61
+ }
62
+ }
63
+ /**
64
+ * Generates TypeScript interface for a table
65
+ */
66
+ function generateTableInterface(table, tableName) {
67
+ const interfaceName = tableName.charAt(0).toUpperCase() + tableName.slice(1);
68
+ let interfaceCode = `\n/**\n * ${interfaceName} table interface\n */\nexport interface ${interfaceName} {\n`;
69
+ for (const column of table.columns) {
70
+ const tsType = pgTypeToTsType(column.type, !column.notNull);
71
+ const comment = column.comment ? ` /** ${column.comment} */` : '';
72
+ interfaceCode += ` /** ${column.name} */\n ${column.name}: ${tsType};${comment}\n`;
73
+ }
74
+ interfaceCode += '}\n';
75
+ return interfaceCode;
76
+ }
77
+ /**
78
+ * Generates complete TypeScript definitions from database schema
79
+ */
80
+ export async function generateDatabaseTypes(options) {
81
+ const { outputPath, connectionString, schema = 'public', excludeTables = ['migrations', 'migration_lock'], includeTables, } = options;
82
+ // Connect to database and get schema
83
+ const db = await pgStructure(connectionString);
84
+ let output = `/**
85
+ * TypeScript definitions for database tables
86
+ * Auto-generated from database schema
87
+ */
88
+
89
+ `;
90
+ const tables = db.schemas.get(schema)?.tables || [];
91
+ const filteredTables = tables.filter((table) => {
92
+ if (includeTables && includeTables.length > 0) {
93
+ return includeTables.includes(table.name);
94
+ }
95
+ return !excludeTables.includes(table.name);
96
+ });
97
+ // Generate table interfaces
98
+ for (const table of filteredTables) {
99
+ output += generateTableInterface(table, table.name);
100
+ }
101
+ // Generate DB interface
102
+ output +=
103
+ '\n/**\n * Database schema interface\n * Maps table names to their respective interfaces\n */\nexport interface DB {\n';
104
+ for (const table of filteredTables) {
105
+ const interfaceName = table.name.charAt(0).toUpperCase() + table.name.slice(1);
106
+ output += ` ${table.name}: ${interfaceName};\n`;
107
+ }
108
+ output += '}\n';
109
+ // Ensure output directory exists
110
+ const outputDir = dirname(resolve(outputPath));
111
+ await fs.mkdir(outputDir, { recursive: true });
112
+ // Write the generated types
113
+ await fs.writeFile(resolve(outputPath), output, 'utf-8');
114
+ }
package/dist/routes.d.ts CHANGED
@@ -21,6 +21,8 @@ export interface Key {
21
21
  optional: boolean;
22
22
  offset: number;
23
23
  }
24
+ export declare function extractTagsFromPath(path: string): string[];
25
+ export declare function convertPathToOperationId(method: string, path: string): string;
24
26
  export declare function toOpenAPISpec(app: Express, info?: {
25
27
  title: string;
26
28
  version: string;
package/dist/routes.js CHANGED
@@ -143,9 +143,9 @@ const mapKeysToPath = (layerRegexPath, keys) => {
143
143
  .replace(/\\/gi, '')
144
144
  .replace(/\/{2,}/gi, '/');
145
145
  };
146
- function extractTagsFromPath(path) {
146
+ export function extractTagsFromPath(path) {
147
147
  const segments = path
148
- .replace(/^\/api\/v\d+\//, '') // Remove /api/v1, /api/v2, etc.
148
+ .replace(/^\/api(\/v\d+)?\//, '') // Remove /api, /api/v1, /api/v2, etc.
149
149
  .split('/')
150
150
  .filter(Boolean);
151
151
  if (segments.length === 0) {
@@ -155,12 +155,12 @@ function extractTagsFromPath(path) {
155
155
  const primaryTag = segments[0].charAt(0).toUpperCase() + segments[0].slice(1);
156
156
  return [primaryTag];
157
157
  }
158
- function convertPathToOperationId(method, path) {
158
+ export function convertPathToOperationId(method, path) {
159
159
  // Clean the path and split into segments
160
160
  const segments = path
161
161
  .replace(/[{}]/g, '') // Remove OpenAPI parameter braces
162
162
  .replace(/[^a-zA-Z0-9/-]/g, '') // Keep hyphens, remove other non-alphanumeric chars
163
- .replace(/^\/api\/v\d+\//, '') // Remove /api/v1, /api/v2, etc. prefixes
163
+ .replace(/^\/api(\/v\d+)?\//, '') // Remove /api, /api/v1, /api/v2, etc. prefixes
164
164
  .split('/') // Split by slashes
165
165
  .filter(Boolean) // Remove empty strings
166
166
  .map((segment) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devmoods/express-extras",
3
- "version": "0.60.0",
3
+ "version": "0.61.1",
4
4
  "description": "Kubernetes-ready utilities for rapid Node.js application development",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -92,6 +92,7 @@
92
92
  "jsonwebtoken": "^9.0.2",
93
93
  "nodemailer": "^7.0.6",
94
94
  "pg": "^8.16.3",
95
+ "pg-structure": "^7.15.3",
95
96
  "redis": "^5.8.2",
96
97
  "sonda": "^0.9.0",
97
98
  "sql-formatter": "^15.6.6",