@dypai-ai/mcp 1.5.16 → 1.5.17
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/package.json +1 -1
- package/src/tools/sync/validate.js +49 -0
package/package.json
CHANGED
|
@@ -316,6 +316,53 @@ async function readSchemaColumns(rootDir) {
|
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
/** Extract referenced table names from a SQL string: `FROM public.X`, `JOIN public.X`, `INTO public.X`, `UPDATE public.X`. */
|
|
319
|
+
function skipSqlBalancedParens(sql, start) {
|
|
320
|
+
let depth = 0
|
|
321
|
+
for (let i = start; i < sql.length; i++) {
|
|
322
|
+
const ch = sql[i]
|
|
323
|
+
if (ch === "\"") {
|
|
324
|
+
i++
|
|
325
|
+
while (i < sql.length && sql[i] !== "\"") i++
|
|
326
|
+
continue
|
|
327
|
+
}
|
|
328
|
+
if (ch === "(") depth++
|
|
329
|
+
else if (ch === ")") {
|
|
330
|
+
depth--
|
|
331
|
+
if (depth === 0) return i + 1
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return sql.length
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function extractSqlCteNames(cleanSql) {
|
|
338
|
+
const names = new Set()
|
|
339
|
+
const withMatch = /^\s*WITH\s+(?:RECURSIVE\s+)?/i.exec(cleanSql)
|
|
340
|
+
if (!withMatch) return names
|
|
341
|
+
let i = withMatch[0].length
|
|
342
|
+
while (i < cleanSql.length) {
|
|
343
|
+
while (/\s|,/.test(cleanSql[i] || "")) i++
|
|
344
|
+
const nameMatch = /^"?([A-Za-z_][A-Za-z0-9_]*)"?/.exec(cleanSql.slice(i))
|
|
345
|
+
if (!nameMatch) break
|
|
346
|
+
const cteName = nameMatch[1]
|
|
347
|
+
if (SQL_KEYWORDS_AFTER_FROM.has(cteName.toUpperCase())) break
|
|
348
|
+
i += nameMatch[0].length
|
|
349
|
+
while (/\s/.test(cleanSql[i] || "")) i++
|
|
350
|
+
if (cleanSql[i] === "(") {
|
|
351
|
+
i = skipSqlBalancedParens(cleanSql, i)
|
|
352
|
+
while (/\s/.test(cleanSql[i] || "")) i++
|
|
353
|
+
}
|
|
354
|
+
if (!/^AS\b/i.test(cleanSql.slice(i))) break
|
|
355
|
+
i += 2
|
|
356
|
+
while (/\s/.test(cleanSql[i] || "")) i++
|
|
357
|
+
if (cleanSql[i] !== "(") break
|
|
358
|
+
names.add(cteName)
|
|
359
|
+
i = skipSqlBalancedParens(cleanSql, i)
|
|
360
|
+
while (/\s/.test(cleanSql[i] || "")) i++
|
|
361
|
+
if (cleanSql[i] !== ",") break
|
|
362
|
+
}
|
|
363
|
+
return names
|
|
364
|
+
}
|
|
365
|
+
|
|
319
366
|
function extractSqlTables(sql) {
|
|
320
367
|
const tables = new Set()
|
|
321
368
|
if (typeof sql !== "string" || sql.length === 0) return tables
|
|
@@ -325,6 +372,7 @@ function extractSqlTables(sql) {
|
|
|
325
372
|
.replace(/--[^\n]*/g, " ")
|
|
326
373
|
.replace(/\/\*[\s\S]*?\*\//g, " ")
|
|
327
374
|
.replace(/'(?:[^']|'')*'/g, "''")
|
|
375
|
+
const cteNames = extractSqlCteNames(clean)
|
|
328
376
|
// Single regex that captures BOTH a possible schema and the table name,
|
|
329
377
|
// optionally preceded by `ONLY` (Postgres inheritance modifier). Splitting
|
|
330
378
|
// schema/table into separate groups makes filtering by schema trivial.
|
|
@@ -339,6 +387,7 @@ function extractSqlTables(sql) {
|
|
|
339
387
|
// when the regex was greedy enough (defensive — the optional `ONLY` above
|
|
340
388
|
// already handles the common case).
|
|
341
389
|
if (SQL_KEYWORDS_AFTER_FROM.has(tableName.toUpperCase())) continue
|
|
390
|
+
if (cteNames.has(tableName)) continue
|
|
342
391
|
// Only validate tables in the user-managed `public` schema. System
|
|
343
392
|
// schemas (auth, system, ext, pg_catalog, information_schema) are
|
|
344
393
|
// managed by the engine and not present in dypai/schema.sql.
|