langaro-api 1.2.5 → 1.2.6
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/langaro-api.js +10 -10
- package/lib/cli/new.js +2 -39
- package/lib/db.js +41 -0
- package/lib/generators/services.js +17 -1
- package/lib/index.js +6 -2
- package/package.json +1 -1
package/bin/langaro-api.js
CHANGED
|
@@ -74,23 +74,23 @@ function confirm(question) {
|
|
|
74
74
|
|
|
75
75
|
// ── Command runners ──
|
|
76
76
|
|
|
77
|
-
function runGenerateTypes() {
|
|
77
|
+
async function runGenerateTypes() {
|
|
78
78
|
const { generateTypes } = require('../lib/index');
|
|
79
79
|
const config = loadConfig();
|
|
80
|
-
generateTypes(config);
|
|
80
|
+
await generateTypes(config);
|
|
81
81
|
console.log('[langaro-api] Types + JSDoc annotations generated.');
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function runWatch() {
|
|
84
|
+
async function runWatch() {
|
|
85
85
|
const { generateTypes, getWatchDirs } = require('../lib/index');
|
|
86
86
|
|
|
87
|
-
function generate() {
|
|
87
|
+
async function generate() {
|
|
88
88
|
const config = loadConfig();
|
|
89
|
-
generateTypes(config);
|
|
89
|
+
await generateTypes(config);
|
|
90
90
|
console.log('[langaro-api] Types + JSDoc annotations generated.');
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
generate();
|
|
93
|
+
await generate();
|
|
94
94
|
|
|
95
95
|
const config = loadConfig();
|
|
96
96
|
const dirs = getWatchDirs(config);
|
|
@@ -102,7 +102,7 @@ function runWatch() {
|
|
|
102
102
|
if (filename && filename.endsWith('.js')) {
|
|
103
103
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
104
104
|
debounceTimer = setTimeout(() => {
|
|
105
|
-
|
|
105
|
+
generate().catch((err) => console.error('[langaro-api] Error:', err.message));
|
|
106
106
|
}, 300);
|
|
107
107
|
}
|
|
108
108
|
});
|
|
@@ -153,13 +153,13 @@ async function main() {
|
|
|
153
153
|
if (command === 'update-docs') { runUpdateDocs(); return process.exit(0); }
|
|
154
154
|
if (command === '--watch') return runWatch();
|
|
155
155
|
if (command === 'generate' || command === 'types') {
|
|
156
|
-
runGenerateTypes();
|
|
156
|
+
await runGenerateTypes();
|
|
157
157
|
return process.exit(0);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
// No args + non-TTY (e.g. piped, CI): just generate types
|
|
161
161
|
if (!process.stdin.isTTY) {
|
|
162
|
-
runGenerateTypes();
|
|
162
|
+
await runGenerateTypes();
|
|
163
163
|
return process.exit(0);
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -176,7 +176,7 @@ async function main() {
|
|
|
176
176
|
|
|
177
177
|
console.log('');
|
|
178
178
|
|
|
179
|
-
if (choice === 'types') { runGenerateTypes(); process.exit(0); }
|
|
179
|
+
if (choice === 'types') { await runGenerateTypes(); process.exit(0); }
|
|
180
180
|
if (choice === 'new') return runNew();
|
|
181
181
|
if (choice === 'migrate') return runMigrate();
|
|
182
182
|
if (choice === 'init') return runInit();
|
package/lib/cli/new.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
const { pascalCase } = require('../utils');
|
|
5
|
+
const { fetchTablesFromDatabase } = require('../db');
|
|
5
6
|
|
|
6
7
|
const DEFAULTS = {
|
|
7
8
|
services: 'src/database/services',
|
|
@@ -181,44 +182,6 @@ function singleSelect(question, allOptions) {
|
|
|
181
182
|
});
|
|
182
183
|
}
|
|
183
184
|
|
|
184
|
-
async function fetchTablesFromDatabase() {
|
|
185
|
-
try {
|
|
186
|
-
// Resolve modules from the project's node_modules, not langaro-api's
|
|
187
|
-
const projectRoot = process.cwd();
|
|
188
|
-
const resolveFrom = (mod) => require(require.resolve(mod, { paths: [projectRoot] }));
|
|
189
|
-
|
|
190
|
-
resolveFrom('dotenv').config({ path: path.join(projectRoot, '.env') });
|
|
191
|
-
|
|
192
|
-
const {
|
|
193
|
-
DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT,
|
|
194
|
-
} = process.env;
|
|
195
|
-
|
|
196
|
-
if (!DB_NAME) {
|
|
197
|
-
console.log('\x1b[33m Could not connect to database. DB_NAME not found in .env\x1b[0m');
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const knex = resolveFrom('knex')({
|
|
202
|
-
client: 'mysql2',
|
|
203
|
-
connection: {
|
|
204
|
-
host: DB_HOST, user: DB_USER, password: DB_PASSWORD, database: DB_NAME, port: DB_PORT,
|
|
205
|
-
},
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const results = await knex.raw(
|
|
209
|
-
'SELECT table_name FROM information_schema.tables WHERE table_schema = ?',
|
|
210
|
-
[DB_NAME],
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
const tables = results[0].map((row) => row.TABLE_NAME).sort();
|
|
214
|
-
await knex.destroy();
|
|
215
|
-
return tables;
|
|
216
|
-
} catch (err) {
|
|
217
|
-
console.log(`\x1b[33m Could not connect to database: ${err.message}\x1b[0m`);
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
185
|
function multiSelect(rl, question, options) {
|
|
223
186
|
return new Promise((resolve) => {
|
|
224
187
|
const selected = new Set(options.map((_, i) => i));
|
|
@@ -393,7 +356,7 @@ async function run(config = {}) {
|
|
|
393
356
|
if (created.length > 0) {
|
|
394
357
|
// Auto-regenerate types so the new resource gets IntelliSense immediately
|
|
395
358
|
const { generateTypes } = require('../index');
|
|
396
|
-
generateTypes(cfg);
|
|
359
|
+
await generateTypes(cfg);
|
|
397
360
|
console.log(`\n\x1b[32mDone!\x1b[0m Created ${created.length} file(s). Types regenerated.\n`);
|
|
398
361
|
} else {
|
|
399
362
|
console.log('\nNo files created (all already exist).\n');
|
package/lib/db.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
async function fetchTablesFromDatabase() {
|
|
4
|
+
try {
|
|
5
|
+
// Resolve modules from the project's node_modules, not langaro-api's
|
|
6
|
+
const projectRoot = process.cwd();
|
|
7
|
+
const resolveFrom = (mod) => require(require.resolve(mod, { paths: [projectRoot] }));
|
|
8
|
+
|
|
9
|
+
resolveFrom('dotenv').config({ path: path.join(projectRoot, '.env') });
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT,
|
|
13
|
+
} = process.env;
|
|
14
|
+
|
|
15
|
+
if (!DB_NAME) {
|
|
16
|
+
console.log('\x1b[33m Could not connect to database. DB_NAME not found in .env\x1b[0m');
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const knex = resolveFrom('knex')({
|
|
21
|
+
client: 'mysql2',
|
|
22
|
+
connection: {
|
|
23
|
+
host: DB_HOST, user: DB_USER, password: DB_PASSWORD, database: DB_NAME, port: DB_PORT,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const results = await knex.raw(
|
|
28
|
+
'SELECT table_name FROM information_schema.tables WHERE table_schema = ?',
|
|
29
|
+
[DB_NAME],
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const tables = results[0].map((row) => row.TABLE_NAME).sort();
|
|
33
|
+
await knex.destroy();
|
|
34
|
+
return tables;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.log(`\x1b[33m Could not connect to database: ${err.message}\x1b[0m`);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = { fetchTablesFromDatabase };
|
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const { pascalCase, extractMethodsWithLocations, isCustomService } = require('../utils');
|
|
4
4
|
const { generateSourceMap } = require('../sourcemap');
|
|
5
5
|
|
|
6
|
-
module.exports = function generateServicesDts(servicesDir, modelsDir, outputDir) {
|
|
6
|
+
module.exports = function generateServicesDts(servicesDir, modelsDir, outputDir, dbTableNames = []) {
|
|
7
7
|
const lines = ['// Auto-generated by langaro-api — DO NOT EDIT', ''];
|
|
8
8
|
const serviceEntries = [];
|
|
9
9
|
const mappings = [];
|
|
@@ -89,6 +89,22 @@ module.exports = function generateServicesDts(servicesDir, modelsDir, outputDir)
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// DB tables discovered via introspection — pure CRUD (no file on disk)
|
|
93
|
+
dbTableNames
|
|
94
|
+
.sort()
|
|
95
|
+
.forEach((tableName) => {
|
|
96
|
+
const serviceName = `${pascalCase(tableName)}Services`;
|
|
97
|
+
if (serviceEntries.find((e) => e.serviceName === serviceName)) return;
|
|
98
|
+
const interfaceName = `I${serviceName}`;
|
|
99
|
+
lines.push(`declare interface ${interfaceName} extends CRUD {}`, '');
|
|
100
|
+
serviceEntries.push({
|
|
101
|
+
interfaceName,
|
|
102
|
+
serviceName,
|
|
103
|
+
tableName,
|
|
104
|
+
custom: false,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
92
108
|
// ServicesMap
|
|
93
109
|
lines.push('declare interface ServicesMap {');
|
|
94
110
|
serviceEntries
|
package/lib/index.js
CHANGED
|
@@ -27,7 +27,7 @@ const DEFAULTS = {
|
|
|
27
27
|
middlewares: 'src/middlewares',
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
function generateTypes(userConfig = {}) {
|
|
30
|
+
async function generateTypes(userConfig = {}) {
|
|
31
31
|
const config = { ...DEFAULTS, ...userConfig };
|
|
32
32
|
const root = config.root;
|
|
33
33
|
|
|
@@ -51,7 +51,11 @@ function generateTypes(userConfig = {}) {
|
|
|
51
51
|
fs.writeFileSync(path.join(outputDir, 'crud.d.ts.map'), crud.sourceMap);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
// Discover DB tables via introspection (same as loadModels does at runtime)
|
|
55
|
+
const { fetchTablesFromDatabase } = require('./db');
|
|
56
|
+
const dbTableNames = await fetchTablesFromDatabase() || [];
|
|
57
|
+
|
|
58
|
+
const services = generateServicesDts(servicesDir, modelsDir, outputDir, dbTableNames);
|
|
55
59
|
fs.writeFileSync(path.join(outputDir, 'services.d.ts'), services.content);
|
|
56
60
|
fs.writeFileSync(path.join(outputDir, 'services.d.ts.map'), services.sourceMap);
|
|
57
61
|
|