langaro-api 1.0.8 → 1.0.9
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/lib/cli/init.js +2 -1
- package/lib/cli/migrate.js +1 -1
- package/lib/generators/crud.js +10 -0
- package/lib/inject.js +9 -1
- package/lib/loaders/models.js +3 -1
- package/lib/sourcemap.js +6 -7
- package/lib/utils.js +20 -3
- package/package.json +1 -1
package/lib/cli/init.js
CHANGED
|
@@ -320,6 +320,7 @@ require('dotenv').config({
|
|
|
320
320
|
});
|
|
321
321
|
|
|
322
322
|
const Sentry = require('@/config/instrument');
|
|
323
|
+
const RedisCache = require('@/database/redis-cache');
|
|
323
324
|
const express = require('express');
|
|
324
325
|
const cors = require('cors');
|
|
325
326
|
const morgan = require('morgan');
|
|
@@ -434,7 +435,7 @@ class App {
|
|
|
434
435
|
if (userId) { socket.join(userId); }
|
|
435
436
|
});
|
|
436
437
|
|
|
437
|
-
const { models } = await loadModels(this.knex);
|
|
438
|
+
const { models } = await loadModels(this.knex, undefined, { redis: RedisCache.redis });
|
|
438
439
|
const services = this.services || loadServices(models, io);
|
|
439
440
|
|
|
440
441
|
const Queue = await this.queues(services, io);
|
package/lib/cli/migrate.js
CHANGED
|
@@ -79,7 +79,7 @@ function run() {
|
|
|
79
79
|
console.log('');
|
|
80
80
|
console.log(' With:');
|
|
81
81
|
console.log(" \x1b[32m+ const { loadModels, loadServices, loadControllers, attachRouters, loadTasks } = require('langaro-api');\x1b[0m");
|
|
82
|
-
console.log(' \x1b[32m+ const { models } = await loadModels(this.knex);\x1b[0m');
|
|
82
|
+
console.log(' \x1b[32m+ const { models } = await loadModels(this.knex, undefined, { redis: RedisCache.redis });\x1b[0m');
|
|
83
83
|
console.log(' \x1b[32m+ const services = this.services || loadServices(models, io);\x1b[0m');
|
|
84
84
|
console.log(' \x1b[32m+ const controllers = loadControllers(services, Queue, io);\x1b[0m');
|
|
85
85
|
console.log(' \x1b[32m+ loadTasks(services, Queue);\x1b[0m');
|
package/lib/generators/crud.js
CHANGED
|
@@ -149,6 +149,8 @@ module.exports = function generateCrudDts(projectRoot, outputDir) {
|
|
|
149
149
|
lines.push(' enableCache(): void;');
|
|
150
150
|
addMethodMapping(lines, 'setCacheTimeout');
|
|
151
151
|
lines.push(' setCacheTimeout(timeout: number): void;');
|
|
152
|
+
addMethodMapping(lines, 'clearQueryCache');
|
|
153
|
+
lines.push(' clearQueryCache(): Promise<void>;');
|
|
152
154
|
lines.push('}');
|
|
153
155
|
lines.push('');
|
|
154
156
|
|
|
@@ -169,6 +171,10 @@ module.exports = function generateCrudDts(projectRoot, outputDir) {
|
|
|
169
171
|
' sortBy?: string;',
|
|
170
172
|
" sort?: 'asc' | 'desc';",
|
|
171
173
|
' where?: (query: any) => any;',
|
|
174
|
+
' /** Attempt to read from Redis cache (default: false) */',
|
|
175
|
+
' cache?: boolean;',
|
|
176
|
+
' /** Write result to Redis cache with this TTL in seconds */',
|
|
177
|
+
' cacheTime?: number;',
|
|
172
178
|
' [key: string]: any;',
|
|
173
179
|
'}',
|
|
174
180
|
'',
|
|
@@ -178,6 +184,8 @@ module.exports = function generateCrudDts(projectRoot, outputDir) {
|
|
|
178
184
|
' where?: (query: any) => any;',
|
|
179
185
|
' andWhere?: Array<[string, string, any]>;',
|
|
180
186
|
' whenSuccess?: (data: any) => void;',
|
|
187
|
+
' /** Skip automatic Redis query cache invalidation */',
|
|
188
|
+
' skipCacheInvalidation?: boolean;',
|
|
181
189
|
' [key: string]: any;',
|
|
182
190
|
'}',
|
|
183
191
|
'',
|
|
@@ -190,6 +198,8 @@ module.exports = function generateCrudDts(projectRoot, outputDir) {
|
|
|
190
198
|
'declare interface CRUDDeleteOptions {',
|
|
191
199
|
' where?: (query: any) => any;',
|
|
192
200
|
' andWhere?: Array<[string, string, any]>;',
|
|
201
|
+
' /** Skip automatic Redis query cache invalidation */',
|
|
202
|
+
' skipCacheInvalidation?: boolean;',
|
|
193
203
|
' [key: string]: any;',
|
|
194
204
|
'}',
|
|
195
205
|
'',
|
package/lib/inject.js
CHANGED
|
@@ -5,6 +5,12 @@ const { scanControllerEntries } = require('./generators/controllers');
|
|
|
5
5
|
|
|
6
6
|
function injectJSDoc(filePath, jsdocComment, targetLineRegex) {
|
|
7
7
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
8
|
+
|
|
9
|
+
// Quick check: if target pattern not in file, skip parsing
|
|
10
|
+
if (!targetLineRegex.test(content)) return false;
|
|
11
|
+
// Reset regex lastIndex after test
|
|
12
|
+
targetLineRegex.lastIndex = 0;
|
|
13
|
+
|
|
8
14
|
const lines = content.split('\n');
|
|
9
15
|
|
|
10
16
|
const targetIdx = lines.findIndex((line) => targetLineRegex.test(line));
|
|
@@ -27,7 +33,9 @@ function injectJSDoc(filePath, jsdocComment, targetLineRegex) {
|
|
|
27
33
|
lines.splice(targetIdx, 0, jsdocComment);
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
const newContent = lines.join('\n');
|
|
37
|
+
if (newContent === content) return true; // no changes needed
|
|
38
|
+
fs.writeFileSync(filePath, newContent);
|
|
31
39
|
return true;
|
|
32
40
|
}
|
|
33
41
|
|
package/lib/loaders/models.js
CHANGED
|
@@ -3,9 +3,10 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
const query = 'SELECT table_name FROM information_schema.tables WHERE table_schema = ?';
|
|
5
5
|
|
|
6
|
-
module.exports = async function loadModels(knexInstance, modelsDir) {
|
|
6
|
+
module.exports = async function loadModels(knexInstance, modelsDir, extraConfig) {
|
|
7
7
|
const dir = modelsDir || path.resolve(process.cwd(), 'src/database/models');
|
|
8
8
|
const bindings = [knexInstance.client.database()];
|
|
9
|
+
const redis = extraConfig && extraConfig.redis ? extraConfig.redis : null;
|
|
9
10
|
|
|
10
11
|
const results = await knexInstance.raw(query, bindings);
|
|
11
12
|
const tableNames = results[0].map((row) => row.TABLE_NAME);
|
|
@@ -29,6 +30,7 @@ module.exports = async function loadModels(knexInstance, modelsDir) {
|
|
|
29
30
|
table,
|
|
30
31
|
...modelConfig,
|
|
31
32
|
modelsPath: dir.split(process.cwd())[1],
|
|
33
|
+
redis,
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
};
|
package/lib/sourcemap.js
CHANGED
|
@@ -28,12 +28,12 @@ function generateSourceMap(generatedFile, sources, mappings) {
|
|
|
28
28
|
let prevSourceLine = 0;
|
|
29
29
|
let prevSourceCol = 0;
|
|
30
30
|
|
|
31
|
-
const lineSegments = [];
|
|
31
|
+
const lineSegments = []; // each element is an array of segment strings
|
|
32
32
|
|
|
33
33
|
sorted.forEach((m) => {
|
|
34
|
-
// Fill empty lines
|
|
34
|
+
// Fill empty lines
|
|
35
35
|
while (prevGenLine < m.genLine) {
|
|
36
|
-
lineSegments.push(
|
|
36
|
+
lineSegments.push([]);
|
|
37
37
|
prevGenLine++;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -45,10 +45,9 @@ function generateSourceMap(generatedFile, sources, mappings) {
|
|
|
45
45
|
|
|
46
46
|
// Append to current line
|
|
47
47
|
if (lineSegments.length === 0 || prevGenLine > lineSegments.length - 1) {
|
|
48
|
-
lineSegments.push(segment);
|
|
48
|
+
lineSegments.push([segment]);
|
|
49
49
|
} else {
|
|
50
|
-
|
|
51
|
-
lineSegments[lineSegments.length - 1] = existing ? existing + ',' + segment : segment;
|
|
50
|
+
lineSegments[lineSegments.length - 1].push(segment);
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
prevSourceIndex = m.sourceIndex;
|
|
@@ -60,7 +59,7 @@ function generateSourceMap(generatedFile, sources, mappings) {
|
|
|
60
59
|
version: 3,
|
|
61
60
|
file: generatedFile,
|
|
62
61
|
sources,
|
|
63
|
-
mappings: lineSegments.join(';'),
|
|
62
|
+
mappings: lineSegments.map((segs) => segs.join(',')).join(';'),
|
|
64
63
|
});
|
|
65
64
|
}
|
|
66
65
|
|
package/lib/utils.js
CHANGED
|
@@ -31,6 +31,24 @@ function extractMethods(filePath) {
|
|
|
31
31
|
|
|
32
32
|
function extractMethodsWithLocations(filePath) {
|
|
33
33
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
34
|
+
const contentLines = content.split('\n');
|
|
35
|
+
|
|
36
|
+
// Pre-compute line start offsets for O(1) line lookup by character index
|
|
37
|
+
const lineOffsets = [0];
|
|
38
|
+
for (let i = 0; i < content.length; i++) {
|
|
39
|
+
if (content[i] === '\n') lineOffsets.push(i + 1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getLineFromOffset(offset) {
|
|
43
|
+
let lo = 0;
|
|
44
|
+
let hi = lineOffsets.length - 1;
|
|
45
|
+
while (lo < hi) {
|
|
46
|
+
const mid = (lo + hi + 1) >> 1;
|
|
47
|
+
if (lineOffsets[mid] <= offset) lo = mid;
|
|
48
|
+
else hi = mid - 1;
|
|
49
|
+
}
|
|
50
|
+
return lo;
|
|
51
|
+
}
|
|
34
52
|
|
|
35
53
|
const patterns = [
|
|
36
54
|
/^\s+(?:async\s+)?([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\([^)]*\)\s*\{/gm,
|
|
@@ -47,9 +65,8 @@ function extractMethodsWithLocations(filePath) {
|
|
|
47
65
|
if (name === 'constructor' || name.startsWith('_') || JS_RESERVED.has(name)) return;
|
|
48
66
|
if (seen.has(name)) return;
|
|
49
67
|
seen.add(name);
|
|
50
|
-
const line =
|
|
51
|
-
const
|
|
52
|
-
const col = lineText.indexOf(name);
|
|
68
|
+
const line = getLineFromOffset(m.index);
|
|
69
|
+
const col = contentLines[line].indexOf(name);
|
|
53
70
|
results.push({ name, line, col: col >= 0 ? col : 0 });
|
|
54
71
|
});
|
|
55
72
|
});
|