@massu/core 1.10.6 → 1.10.8

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.
@@ -0,0 +1,71 @@
1
+ // Copyright (c) 2026 Massu. All rights reserved.
2
+ // Licensed under BSL 1.1 - see LICENSE file for details.
3
+
4
+ /**
5
+ * P-H032 (plan-stage-c-high-batch / 1.10.7): SoT for SQL table names that
6
+ * are prefixed with the customer's configured `toolPrefix`.
7
+ *
8
+ * **Bug class closed**: 20 distinct SQL table names (`massu_imports`,
9
+ * `massu_trpc_procedures`, `massu_sentinel`, etc.) were hardcoded with the
10
+ * `massu_` prefix in ~145 SQL string sites across 21 source files. Tool
11
+ * names were already config-driven via `getConfig().toolPrefix` but
12
+ * persistence wasn't — split-brain where a non-default-prefix customer
13
+ * (e.g. `toolPrefix: 'foo'`) would have tools named `foo_*` but tables
14
+ * still named `massu_*`. Default-prefix customers (100% of current
15
+ * installs) see ZERO behavior change.
16
+ *
17
+ * **Structural fix**: every SQL string that references a prefix-scoped
18
+ * table now goes through `t(suffix)` which returns
19
+ * `\`${getConfig().toolPrefix}_${suffix}\``. The ESLint rule
20
+ * `no-hardcoded-massu-tables` bans bare `'massu_X'` literals so future
21
+ * regressions are impossible. The drift-guard test
22
+ * `sql-table-names-source-grep.test.ts` scans the source tree and asserts
23
+ * zero bare `massu_<table>` literals outside this module + the test files.
24
+ *
25
+ * **20 canonical table suffixes** (registered here as a typed const so
26
+ * typos at callsites are caught at compile time):
27
+ */
28
+
29
+ import { getConfig } from '../config.ts';
30
+
31
+ /**
32
+ * The complete set of suffixes for prefix-scoped SQL tables. Keep this in
33
+ * sync with `packages/core/src/db.ts` (CodeGraph schema) +
34
+ * `sentinel-db.ts` (sentinel schema) + Python indexer DDL in
35
+ * `packages/core/src/python/*.ts`.
36
+ */
37
+ export type MassuTableSuffix =
38
+ | 'imports'
39
+ | 'meta'
40
+ | 'middleware_tree'
41
+ | 'page_deps'
42
+ | 'py_fk_edges'
43
+ | 'py_imports'
44
+ | 'py_meta'
45
+ | 'py_migrations'
46
+ | 'py_models'
47
+ | 'py_route_callers'
48
+ | 'py_routes'
49
+ | 'sentinel'
50
+ | 'sentinel_changelog'
51
+ | 'sentinel_components'
52
+ | 'sentinel_deps'
53
+ | 'sentinel_fts'
54
+ | 'sentinel_pages'
55
+ | 'sentinel_procedures'
56
+ | 'trpc_call_sites'
57
+ | 'trpc_procedures';
58
+
59
+ /**
60
+ * Resolve a prefix-scoped table name to its concrete identifier using the
61
+ * current `getConfig().toolPrefix`. For default-prefix installs returns
62
+ * `massu_<suffix>` (current behavior). For custom-prefix installs returns
63
+ * `<customPrefix>_<suffix>`.
64
+ *
65
+ * Use this in EVERY SQL string that references a prefix-scoped table.
66
+ * The ESLint rule bans bare `'massu_X'` literals; the only legitimate
67
+ * exit is this function.
68
+ */
69
+ export function t(suffix: MassuTableSuffix): string {
70
+ return `${getConfig().toolPrefix}_${suffix}`;
71
+ }
@@ -5,6 +5,7 @@ import type Database from 'better-sqlite3';
5
5
  import { getConfig } from './config.ts';
6
6
  import { existsSync } from 'fs';
7
7
  import { resolve } from 'path';
8
+ import { t } from './lib/sql-table-names.ts';
8
9
 
9
10
  /**
10
11
  * Build the middleware import tree by tracing all transitive imports
@@ -13,7 +14,7 @@ import { resolve } from 'path';
13
14
  */
14
15
  export function buildMiddlewareTree(dataDb: Database.Database): number {
15
16
  // Clear existing data
16
- dataDb.exec('DELETE FROM massu_middleware_tree');
17
+ dataDb.exec(`DELETE FROM ${t('middleware_tree')}`);
17
18
 
18
19
  const config = getConfig();
19
20
  const middlewareFile = config.paths.middleware ?? 'src/middleware.ts';
@@ -30,7 +31,7 @@ export function buildMiddlewareTree(dataDb: Database.Database): number {
30
31
  const current = queue.shift()!;
31
32
 
32
33
  const imports = dataDb.prepare(
33
- 'SELECT target_file FROM massu_imports WHERE source_file = ?'
34
+ `SELECT target_file FROM ${t('imports')} WHERE source_file = ?`
34
35
  ).all(current) as { target_file: string }[];
35
36
 
36
37
  for (const imp of imports) {
@@ -42,7 +43,7 @@ export function buildMiddlewareTree(dataDb: Database.Database): number {
42
43
  }
43
44
 
44
45
  // Store the tree
45
- const insertStmt = dataDb.prepare('INSERT INTO massu_middleware_tree (file) VALUES (?)');
46
+ const insertStmt = dataDb.prepare(`INSERT INTO ${t('middleware_tree')} (file) VALUES (?)`);
46
47
  const insertAll = dataDb.transaction(() => {
47
48
  for (const file of visited) {
48
49
  insertStmt.run(file);
@@ -57,7 +58,7 @@ export function buildMiddlewareTree(dataDb: Database.Database): number {
57
58
  * Check if a file is in the middleware import tree.
58
59
  */
59
60
  export function isInMiddlewareTree(dataDb: Database.Database, file: string): boolean {
60
- const result = dataDb.prepare('SELECT 1 FROM massu_middleware_tree WHERE file = ?').get(file);
61
+ const result = dataDb.prepare(`SELECT 1 FROM ${t('middleware_tree')} WHERE file = ?`).get(file);
61
62
  return result !== undefined;
62
63
  }
63
64
 
@@ -65,6 +66,6 @@ export function isInMiddlewareTree(dataDb: Database.Database, file: string): boo
65
66
  * Get all files in the middleware import tree.
66
67
  */
67
68
  export function getMiddlewareTree(dataDb: Database.Database): string[] {
68
- const rows = dataDb.prepare('SELECT file FROM massu_middleware_tree ORDER BY file').all() as { file: string }[];
69
+ const rows = dataDb.prepare(`SELECT file FROM ${t('middleware_tree')} ORDER BY file`).all() as { file: string }[];
69
70
  return rows.map(r => r.file);
70
71
  }
package/src/page-deps.ts CHANGED
@@ -6,6 +6,7 @@ import { resolve } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { getConfig, getProjectRoot } from './config.ts';
8
8
  import { ensureWithinRoot } from './security-utils.ts';
9
+ import { t } from './lib/sql-table-names.ts';
9
10
 
10
11
  export interface PageChain {
11
12
  page: string;
@@ -61,7 +62,7 @@ function traceImports(
61
62
  visited.add(startFile);
62
63
 
63
64
  const imports = dataDb.prepare(
64
- 'SELECT target_file, imported_names FROM massu_imports WHERE source_file = ?'
65
+ `SELECT target_file, imported_names FROM ${t('imports')} WHERE source_file = ?`
65
66
  ).all(startFile) as { target_file: string; imported_names: string }[];
66
67
 
67
68
  for (const imp of imports) {
@@ -117,7 +118,7 @@ function findTablesFromRouters(routerNames: string[], dataDb: Database.Database)
117
118
  // Look up router files from the tRPC index
118
119
  for (const routerName of routerNames) {
119
120
  const procs = dataDb.prepare(
120
- 'SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?'
121
+ `SELECT DISTINCT router_file FROM ${t('trpc_procedures')} WHERE router_name = ?`
121
122
  ).all(routerName) as { router_file: string }[];
122
123
 
123
124
  for (const proc of procs) {
@@ -150,7 +151,7 @@ function findTablesFromRouters(routerNames: string[], dataDb: Database.Database)
150
151
  */
151
152
  export function buildPageDeps(dataDb: Database.Database, codegraphDb: Database.Database): number {
152
153
  // Clear existing data
153
- dataDb.exec('DELETE FROM massu_page_deps');
154
+ dataDb.exec(`DELETE FROM ${t('page_deps')}`);
154
155
 
155
156
  // Find all page.tsx files from CodeGraph
156
157
  const pages = codegraphDb.prepare(
@@ -158,7 +159,7 @@ export function buildPageDeps(dataDb: Database.Database, codegraphDb: Database.D
158
159
  ).all() as { path: string }[];
159
160
 
160
161
  const insertStmt = dataDb.prepare(
161
- 'INSERT INTO massu_page_deps (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)'
162
+ `INSERT INTO ${t('page_deps')} (page_file, route, portal, components, hooks, routers, tables_touched) VALUES (?, ?, ?, ?, ?, ?, ?)`
162
163
  );
163
164
 
164
165
  let count = 0;
@@ -200,7 +201,7 @@ export function buildPageDeps(dataDb: Database.Database, codegraphDb: Database.D
200
201
  * Get the dependency chain for a specific page.
201
202
  */
202
203
  export function getPageChain(dataDb: Database.Database, pageFile: string): PageChain | null {
203
- const row = dataDb.prepare('SELECT * FROM massu_page_deps WHERE page_file = ?').get(pageFile) as {
204
+ const row = dataDb.prepare(`SELECT * FROM ${t('page_deps')} WHERE page_file = ?`).get(pageFile) as {
204
205
  page_file: string; route: string; portal: string;
205
206
  components: string; hooks: string; routers: string; tables_touched: string;
206
207
  } | undefined;
@@ -229,7 +230,7 @@ export function findAffectedPages(dataDb: Database.Database, file: string): Page
229
230
  // Find all pages that import this file (directly or transitively)
230
231
  // First, find who imports this file
231
232
  const importers = dataDb.prepare(
232
- 'SELECT source_file FROM massu_imports WHERE target_file = ?'
233
+ `SELECT source_file FROM ${t('imports')} WHERE target_file = ?`
233
234
  ).all(file) as { source_file: string }[];
234
235
 
235
236
  const affectedFiles = new Set<string>([file, ...importers.map(i => i.source_file)]);
@@ -244,7 +245,7 @@ export function findAffectedPages(dataDb: Database.Database, file: string): Page
244
245
  const next: string[] = [];
245
246
  for (const f of frontier) {
246
247
  const upstreamImporters = dataDb.prepare(
247
- 'SELECT source_file FROM massu_imports WHERE target_file = ?'
248
+ `SELECT source_file FROM ${t('imports')} WHERE target_file = ?`
248
249
  ).all(f) as { source_file: string }[];
249
250
 
250
251
  for (const imp of upstreamImporters) {
@@ -260,7 +261,7 @@ export function findAffectedPages(dataDb: Database.Database, file: string): Page
260
261
  }
261
262
 
262
263
  // Now find which pages are in the affected set
263
- const allPages = dataDb.prepare('SELECT * FROM massu_page_deps').all() as {
264
+ const allPages = dataDb.prepare(`SELECT * FROM ${t('page_deps')}`).all() as {
264
265
  page_file: string; route: string; portal: string;
265
266
  components: string; hooks: string; routers: string; tables_touched: string;
266
267
  }[];
@@ -5,6 +5,7 @@ import { readFileSync, readdirSync } from 'fs';
5
5
  import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { getProjectRoot, getConfig } from '../config.ts';
8
+ import { t } from '../lib/sql-table-names.ts';
8
9
 
9
10
  interface CouplingMatch {
10
11
  frontendFile: string;
@@ -23,20 +24,20 @@ export function buildPythonCouplingIndex(dataDb: Database.Database): number {
23
24
  const srcDir = join(projectRoot, config.paths.source);
24
25
 
25
26
  // Get all routes from DB
26
- const routes = dataDb.prepare('SELECT id, method, path FROM massu_py_routes').all() as {
27
+ const routes = dataDb.prepare(`SELECT id, method, path FROM ${t('py_routes')}`).all() as {
27
28
  id: number; method: string; path: string;
28
29
  }[];
29
30
 
30
31
  if (routes.length === 0) return 0;
31
32
 
32
33
  // Clear existing callers
33
- dataDb.exec('DELETE FROM massu_py_route_callers');
34
+ dataDb.exec(`DELETE FROM ${t('py_route_callers')}`);
34
35
 
35
36
  // Walk frontend files (TS/TSX/JS/JSX)
36
37
  const frontendFiles = walkFrontendFiles(srcDir);
37
38
 
38
39
  const insertStmt = dataDb.prepare(
39
- 'INSERT INTO massu_py_route_callers (route_id, frontend_file, line, call_pattern) VALUES (?, ?, ?, ?)'
40
+ `INSERT INTO ${t('py_route_callers')} (route_id, frontend_file, line, call_pattern) VALUES (?, ?, ?, ?)`
40
41
  );
41
42
 
42
43
  let count = 0;
@@ -3,6 +3,7 @@
3
3
 
4
4
  import type Database from 'better-sqlite3';
5
5
  import { getConfig } from '../config.ts';
6
+ import { t } from '../lib/sql-table-names.ts';
6
7
 
7
8
  export interface DomainViolation {
8
9
  sourceFile: string;
@@ -44,7 +45,7 @@ export function findPythonDomainViolations(dataDb: Database.Database): DomainVio
44
45
  if (domains.length === 0) return [];
45
46
 
46
47
  const imports = dataDb.prepare(
47
- 'SELECT source_file, target_file FROM massu_py_imports'
48
+ `SELECT source_file, target_file FROM ${t('py_imports')}`
48
49
  ).all() as { source_file: string; target_file: string }[];
49
50
 
50
51
  const violations: DomainViolation[] = [];
@@ -74,7 +75,7 @@ export function findPythonDomainViolations(dataDb: Database.Database): DomainVio
74
75
  */
75
76
  export function getPythonFilesInDomain(dataDb: Database.Database, domainName: string): string[] {
76
77
  const allFiles = dataDb.prepare(
77
- 'SELECT DISTINCT source_file as f FROM massu_py_imports UNION SELECT DISTINCT target_file as f FROM massu_py_imports'
78
+ `SELECT DISTINCT source_file as f FROM ${t('py_imports')} UNION SELECT DISTINCT target_file as f FROM ${t('py_imports')}`
78
79
  ).all() as { f: string }[];
79
80
 
80
81
  return allFiles
@@ -3,6 +3,7 @@
3
3
 
4
4
  import type Database from 'better-sqlite3';
5
5
  import { classifyPythonFileDomain } from './domain-enforcer.ts';
6
+ import { t } from '../lib/sql-table-names.ts';
6
7
 
7
8
  export interface PythonImpactReport {
8
9
  file: string;
@@ -26,28 +27,28 @@ export function analyzePythonImpact(dataDb: Database.Database, file: string): Py
26
27
 
27
28
  // 2. Routes defined in this file
28
29
  const routes = dataDb.prepare(
29
- 'SELECT method, path, function_name FROM massu_py_routes WHERE file = ?'
30
+ `SELECT method, path, function_name FROM ${t('py_routes')} WHERE file = ?`
30
31
  ).all(file) as { method: string; path: string; function_name: string }[];
31
32
 
32
33
  // 3. Models defined in this file
33
34
  const models = dataDb.prepare(
34
- 'SELECT class_name, table_name FROM massu_py_models WHERE file = ?'
35
+ `SELECT class_name, table_name FROM ${t('py_models')} WHERE file = ?`
35
36
  ).all(file) as { class_name: string; table_name: string | null }[];
36
37
 
37
38
  // 4. Frontend callers (via routes in this file)
38
- const routeIds = dataDb.prepare('SELECT id FROM massu_py_routes WHERE file = ?').all(file) as { id: number }[];
39
+ const routeIds = dataDb.prepare(`SELECT id FROM ${t('py_routes')} WHERE file = ?`).all(file) as { id: number }[];
39
40
  const frontendCallers: string[] = [];
40
41
  if (routeIds.length > 0) {
41
42
  const placeholders = routeIds.map(() => '?').join(',');
42
43
  const callers = dataDb.prepare(
43
- `SELECT DISTINCT frontend_file FROM massu_py_route_callers WHERE route_id IN (${placeholders})`
44
+ `SELECT DISTINCT frontend_file FROM ${t('py_route_callers')} WHERE route_id IN (${placeholders})`
44
45
  ).all(...routeIds.map(r => r.id)) as { frontend_file: string }[];
45
46
  frontendCallers.push(...callers.map(c => c.frontend_file));
46
47
  }
47
48
 
48
49
  // 5. Domain crossings
49
50
  const imports = dataDb.prepare(
50
- 'SELECT target_file FROM massu_py_imports WHERE source_file = ?'
51
+ `SELECT target_file FROM ${t('py_imports')} WHERE source_file = ?`
51
52
  ).all(file) as { target_file: string }[];
52
53
 
53
54
  const domainCrossings = imports
@@ -71,7 +72,7 @@ function collectTransitiveImporters(dataDb: Database.Database, file: string, max
71
72
  let depth = 0;
72
73
 
73
74
  const importerStmt = dataDb.prepare(
74
- 'SELECT source_file FROM massu_py_imports WHERE target_file = ?'
75
+ `SELECT source_file FROM ${t('py_imports')} WHERE target_file = ?`
75
76
  );
76
77
 
77
78
  while (queue.length > 0 && depth < maxDepth) {
@@ -6,6 +6,7 @@ import { resolve, join, relative, dirname } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parsePythonImports } from './import-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  /**
11
12
  * Resolve a Python module path to a file path.
@@ -82,10 +83,10 @@ export function buildPythonImportIndex(dataDb: Database.Database, pythonRoot: st
82
83
  const absRoot = resolve(projectRoot, pythonRoot);
83
84
 
84
85
  // Clear existing Python import edges
85
- dataDb.exec('DELETE FROM massu_py_imports');
86
+ dataDb.exec(`DELETE FROM ${t('py_imports')}`);
86
87
 
87
88
  const insertStmt = dataDb.prepare(
88
- 'INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)'
89
+ `INSERT INTO ${t('py_imports')} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
89
90
  );
90
91
 
91
92
  const files = walkPythonFiles(absRoot, excludeDirs);
@@ -6,11 +6,12 @@ import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parseAlembicMigration } from './migration-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  export function buildPythonMigrationIndex(dataDb: Database.Database, alembicDir: string): number {
11
12
  const projectRoot = getProjectRoot();
12
13
  const absDir = join(projectRoot, alembicDir);
13
- dataDb.exec('DELETE FROM massu_py_migrations');
14
+ dataDb.exec(`DELETE FROM ${t('py_migrations')}`);
14
15
 
15
16
  // Look for version files in versions/ subdirectory
16
17
  const versionsDir = join(absDir, 'versions');
@@ -28,7 +29,7 @@ export function buildPythonMigrationIndex(dataDb: Database.Database, alembicDir:
28
29
  }
29
30
 
30
31
  const insertStmt = dataDb.prepare(
31
- 'INSERT INTO massu_py_migrations (revision, down_revision, file, description, operations, is_head) VALUES (?, ?, ?, ?, ?, ?)'
32
+ `INSERT INTO ${t('py_migrations')} (revision, down_revision, file, description, operations, is_head) VALUES (?, ?, ?, ?, ?, ?)`
32
33
  );
33
34
 
34
35
  let count = 0;
@@ -80,11 +81,11 @@ export interface DriftReport {
80
81
  }
81
82
 
82
83
  export function detectMigrationDrift(dataDb: Database.Database): DriftReport {
83
- const models = dataDb.prepare('SELECT class_name, table_name, columns FROM massu_py_models WHERE table_name IS NOT NULL').all() as {
84
+ const models = dataDb.prepare(`SELECT class_name, table_name, columns FROM ${t('py_models')} WHERE table_name IS NOT NULL`).all() as {
84
85
  class_name: string; table_name: string; columns: string;
85
86
  }[];
86
87
 
87
- const migrations = dataDb.prepare('SELECT operations FROM massu_py_migrations').all() as { operations: string }[];
88
+ const migrations = dataDb.prepare(`SELECT operations FROM ${t('py_migrations')}`).all() as { operations: string }[];
88
89
 
89
90
  // Collect all tables and columns mentioned in migrations
90
91
  const migratedTables = new Set<string>();
@@ -6,6 +6,7 @@ import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parsePythonModels } from './model-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
11
12
  const files: string[] = [];
@@ -26,14 +27,14 @@ function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
26
27
  export function buildPythonModelIndex(dataDb: Database.Database, pythonRoot: string, excludeDirs: string[] = ['__pycache__', '.venv', 'venv', '.mypy_cache', '.pytest_cache']): number {
27
28
  const projectRoot = getProjectRoot();
28
29
  const absRoot = join(projectRoot, pythonRoot);
29
- dataDb.exec('DELETE FROM massu_py_models');
30
- dataDb.exec('DELETE FROM massu_py_fk_edges');
30
+ dataDb.exec(`DELETE FROM ${t('py_models')}`);
31
+ dataDb.exec(`DELETE FROM ${t('py_fk_edges')}`);
31
32
 
32
33
  const insertModel = dataDb.prepare(
33
- 'INSERT INTO massu_py_models (class_name, table_name, file, line, columns, relationships, foreign_keys) VALUES (?, ?, ?, ?, ?, ?, ?)'
34
+ `INSERT INTO ${t('py_models')} (class_name, table_name, file, line, columns, relationships, foreign_keys) VALUES (?, ?, ?, ?, ?, ?, ?)`
34
35
  );
35
36
  const insertFk = dataDb.prepare(
36
- 'INSERT INTO massu_py_fk_edges (source_table, source_column, target_table, target_column) VALUES (?, ?, ?, ?)'
37
+ `INSERT INTO ${t('py_fk_edges')} (source_table, source_column, target_table, target_column) VALUES (?, ?, ?, ?)`
37
38
  );
38
39
 
39
40
  const files = walkPyFiles(absRoot, excludeDirs);
@@ -6,6 +6,7 @@ import { join, relative } from 'path';
6
6
  import type Database from 'better-sqlite3';
7
7
  import { parsePythonRoutes } from './route-parser.ts';
8
8
  import { getProjectRoot } from '../config.ts';
9
+ import { t } from '../lib/sql-table-names.ts';
9
10
 
10
11
  function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
11
12
  const files: string[] = [];
@@ -26,11 +27,11 @@ function walkPyFiles(dir: string, excludeDirs: string[]): string[] {
26
27
  export function buildPythonRouteIndex(dataDb: Database.Database, pythonRoot: string, excludeDirs: string[] = ['__pycache__', '.venv', 'venv', '.mypy_cache', '.pytest_cache']): number {
27
28
  const projectRoot = getProjectRoot();
28
29
  const absRoot = join(projectRoot, pythonRoot);
29
- dataDb.exec('DELETE FROM massu_py_routes');
30
- dataDb.exec('DELETE FROM massu_py_route_callers');
30
+ dataDb.exec(`DELETE FROM ${t('py_routes')}`);
31
+ dataDb.exec(`DELETE FROM ${t('py_route_callers')}`);
31
32
 
32
33
  const insertStmt = dataDb.prepare(
33
- 'INSERT INTO massu_py_routes (file, method, path, function_name, dependencies, request_model, response_model, is_authenticated, line) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
34
+ `INSERT INTO ${t('py_routes')} (file, method, path, function_name, dependencies, request_model, response_model, is_authenticated, line) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
34
35
  );
35
36
 
36
37
  const files = walkPyFiles(absRoot, excludeDirs);