langaro-api 1.0.8 → 1.1.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/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);
@@ -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');
@@ -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
- fs.writeFileSync(filePath, lines.join('\n'));
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
 
@@ -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 with ;
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
- const existing = lineSegments[lineSegments.length - 1];
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 = content.substring(0, m.index).split('\n').length - 1;
51
- const lineText = content.split('\n')[line];
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langaro-api",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Auto-generate TypeScript types, JSDoc annotations, and boilerplate loaders for knex-extended-crud projects",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  ],
23
23
  "peerDependencies": {
24
24
  "cron": ">=3.0.0",
25
- "knex-extended-crud": ">=2.0.31"
25
+ "knex-extended-crud": ">=2.0.34"
26
26
  },
27
27
  "peerDependenciesMeta": {
28
28
  "cron": {