@runa-ai/runa-cli 0.10.0 → 0.10.1
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/dist/{chunk-ZWDWFMOX.js → chunk-HWR5NUUZ.js} +24 -3
- package/dist/{chunk-JQXOVCOP.js → chunk-NIS77243.js} +8 -5
- package/dist/{chunk-URWDB7YL.js → chunk-O3M7A73M.js} +58 -2
- package/dist/{chunk-QDOR3GTD.js → chunk-XRLIZKB2.js} +80 -12
- package/dist/{chunk-IEKYTCYA.js → chunk-YTQS2O4H.js} +59 -0
- package/dist/{chunk-OXQISY3J.js → chunk-ZPE52NEK.js} +1 -1
- package/dist/{ci-FLTJ2UXB.js → ci-3HZWUQFN.js} +4 -4
- package/dist/{cli-THEA6T7N.js → cli-RES5QRC2.js} +12 -12
- package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +6 -0
- package/dist/commands/db/commands/db-sync/production-precheck.d.ts +0 -8
- package/dist/commands/db/sync/schema-guardrail-graph-guidance.d.ts +18 -1
- package/dist/commands/db/sync/schema-guardrail-graph-nodes.d.ts +1 -1
- package/dist/commands/db/sync/schema-guardrail-graph-sql-helpers.d.ts +1 -1
- package/dist/commands/db/sync/schema-guardrail-types.d.ts +4 -2
- package/dist/commands/db/utils/changed-files-detector.d.ts +21 -0
- package/dist/commands/db/utils/schema-sync.d.ts +12 -0
- package/dist/commands/db/utils/sql-boundary-parser.d.ts +13 -0
- package/dist/commands/db/utils/sql-file-collector.d.ts +2 -0
- package/dist/constants/versions.d.ts +9 -0
- package/dist/{db-IDKQ44VX.js → db-PRGL7PBX.js} +587 -76
- package/dist/{dev-LGSMDFJN.js → dev-QR55VDNZ.js} +1 -1
- package/dist/{error-handler-YRQWRDEF.js → error-handler-XUQOP4TU.js} +1 -2
- package/dist/{hotfix-RJIAPLAM.js → hotfix-JYHDY2M6.js} +1 -2
- package/dist/index.js +4 -4
- package/dist/{init-2O6ODG5Z.js → init-4UAWYY75.js} +1 -1
- package/dist/{license-OB7GVJQ2.js → license-M6ODBV4X.js} +140 -154
- package/dist/pg-schema-diff-helpers-JZO4GAQG.js +7 -0
- package/dist/{upgrade-QZKEI3NJ.js → upgrade-LBO3Z3J7.js} +1 -1
- package/dist/utils/license/index.d.ts +15 -24
- package/dist/utils/license/types.d.ts +3 -4
- package/dist/utils/template-access.d.ts +20 -0
- package/dist/utils/template-fetcher.d.ts +10 -7
- package/dist/{vuln-check-JRPMUHLF.js → vuln-check-5JJ2YAJW.js} +1 -1
- package/dist/{vuln-checker-Q7LSHUHJ.js → vuln-checker-JF5234BL.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZZOXM6Q4.js +0 -8
- package/dist/pg-schema-diff-helpers-7377FS2D.js +0 -7
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { blankDollarQuotedBodies, stripSqlComments, psqlSyncQuery, parsePostgresUrl, buildPsqlArgs, buildPsqlEnv } from './chunk-A6A7JIRD.js';
|
|
4
4
|
import { init_esm_shims } from './chunk-VRXHCR5K.js';
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
5
6
|
import path from 'path';
|
|
6
7
|
import { execFileSync, spawnSync, spawn } from 'child_process';
|
|
7
8
|
import { createCLILogger } from '@runa-ai/runa';
|
|
8
|
-
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
9
9
|
|
|
10
10
|
createRequire(import.meta.url);
|
|
11
11
|
|
|
@@ -473,6 +473,7 @@ function collectWarningsForStatement(statement, file, line, knownFunctionNames)
|
|
|
473
473
|
|
|
474
474
|
// src/commands/db/utils/sql-boundary-parser.ts
|
|
475
475
|
init_esm_shims();
|
|
476
|
+
var ALLOW_DYNAMIC_SQL_ANNOTATION = /--\s*runa:allow-dynamic-sql\b/;
|
|
476
477
|
var DOLLAR_QUOTE_PATTERN = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/;
|
|
477
478
|
function countNewlines2(text) {
|
|
478
479
|
let count = 0;
|
|
@@ -1204,6 +1205,20 @@ function detectMissingExtensionType(errorOutput) {
|
|
|
1204
1205
|
suggestedExtensions: [...extensions]
|
|
1205
1206
|
};
|
|
1206
1207
|
}
|
|
1208
|
+
function detectExtensionFilePath() {
|
|
1209
|
+
const defaultPath = "supabase/schemas/idempotent/00_extensions.sql";
|
|
1210
|
+
const idempotentDir = path.join(process.cwd(), "supabase", "schemas", "idempotent");
|
|
1211
|
+
if (!existsSync(idempotentDir)) return defaultPath;
|
|
1212
|
+
try {
|
|
1213
|
+
const files = readdirSync(idempotentDir).filter((f) => f.endsWith(".sql"));
|
|
1214
|
+
const extensionFile = files.find((f) => /extension/i.test(f));
|
|
1215
|
+
if (extensionFile) {
|
|
1216
|
+
return path.posix.join("supabase", "schemas", "idempotent", extensionFile);
|
|
1217
|
+
}
|
|
1218
|
+
} catch {
|
|
1219
|
+
}
|
|
1220
|
+
return defaultPath;
|
|
1221
|
+
}
|
|
1207
1222
|
function formatExtensionErrorHint(detection) {
|
|
1208
1223
|
if (!detection.detected) return "";
|
|
1209
1224
|
const lines = [];
|
|
@@ -1214,8 +1229,14 @@ function formatExtensionErrorHint(detection) {
|
|
|
1214
1229
|
lines.push(` type "${type}" \u2192 extension: ${ext ?? "unknown"}`);
|
|
1215
1230
|
}
|
|
1216
1231
|
if (detection.suggestedExtensions.length > 0) {
|
|
1232
|
+
const extensionFile = detectExtensionFilePath();
|
|
1233
|
+
const fileExists = existsSync(path.join(process.cwd(), extensionFile));
|
|
1217
1234
|
lines.push("");
|
|
1218
|
-
|
|
1235
|
+
if (fileExists) {
|
|
1236
|
+
lines.push(`Fix 1: Add to ${extensionFile}:`);
|
|
1237
|
+
} else {
|
|
1238
|
+
lines.push(`Fix 1: Create ${extensionFile} (or add to your existing extension file):`);
|
|
1239
|
+
}
|
|
1219
1240
|
for (const ext of detection.suggestedExtensions) {
|
|
1220
1241
|
lines.push(` CREATE EXTENSION IF NOT EXISTS ${ext};`);
|
|
1221
1242
|
}
|
|
@@ -1511,4 +1532,4 @@ function executePgSchemaDiffPlan(dbUrl, schemasDir, includeSchemas, verbose, opt
|
|
|
1511
1532
|
throw new Error("pg-schema-diff plan failed after all retries");
|
|
1512
1533
|
}
|
|
1513
1534
|
|
|
1514
|
-
export { DB_APPLY_CHECK_MODE_CONTRACT_NOTE, FUNCTION_DEFINITION_RE, HAZARD_REGEX, MAX_DETAILED_DECLARATIVE_WARNINGS, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, PUBLIC_SCHEMA, STATEMENT_IDX_REGEX, analyzeDeclarativeDependencyContract, blankQuotedStrings, buildIdleConnectionCleanupSql, collectSqlFiles, countNewlines, detectDropTableStatements, detectMissingExtensionType, detectMissingQualifiedFunction, detectPartitionPrivilegeError, detectPgSchemaDiffVersion, executePgSchemaDiffPlan, extractDdlObject, extractFirstDollarBody, formatDeclarativeDependencyBoundaryHint, formatDeclarativeDependencyViolation, formatDeclarativeDependencyWarning, formatExtensionErrorHint, formatPartitionPrivilegeHint, freeConnectionSlotsForPgSchemaDiff, hasUnparsedHazardHints, isNonDdlMaintenanceStatement, isNonSchemaOperation, parseSqlFilename, sanitizeExecutableCode, sanitizeExecutableCodePreserveStrings, shouldReviewUnknownDeclarativeDdl, shouldReviewUnknownIdempotentDdl, splitSqlStatements, startConnectionCleanupDaemon, stopConnectionCleanupDaemon, summarizeDeclarativeDependencyWarnings, verifyDatabaseConnection, verifyPgSchemaDiffBinary };
|
|
1535
|
+
export { ALLOW_DYNAMIC_SQL_ANNOTATION, DB_APPLY_CHECK_MODE_CONTRACT_NOTE, FUNCTION_DEFINITION_RE, HAZARD_REGEX, MAX_DETAILED_DECLARATIVE_WARNINGS, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, PUBLIC_SCHEMA, STATEMENT_IDX_REGEX, analyzeDeclarativeDependencyContract, blankQuotedStrings, buildIdleConnectionCleanupSql, collectSqlFiles, countNewlines, detectDropTableStatements, detectExtensionFilePath, detectMissingExtensionType, detectMissingQualifiedFunction, detectPartitionPrivilegeError, detectPgSchemaDiffVersion, executePgSchemaDiffPlan, extractDdlObject, extractFirstDollarBody, formatDeclarativeDependencyBoundaryHint, formatDeclarativeDependencyViolation, formatDeclarativeDependencyWarning, formatExtensionErrorHint, formatPartitionPrivilegeHint, freeConnectionSlotsForPgSchemaDiff, hasUnparsedHazardHints, isNonDdlMaintenanceStatement, isNonSchemaOperation, parseSqlFilename, sanitizeExecutableCode, sanitizeExecutableCodePreserveStrings, shouldReviewUnknownDeclarativeDdl, shouldReviewUnknownIdempotentDdl, splitSqlStatements, startConnectionCleanupDaemon, stopConnectionCleanupDaemon, summarizeDeclarativeDependencyWarnings, verifyDatabaseConnection, verifyPgSchemaDiffBinary };
|
|
@@ -132,12 +132,12 @@ var ERROR_CATALOG = {
|
|
|
132
132
|
LICENSE_UNAUTHORIZED: {
|
|
133
133
|
code: "ERR_RUNA_LICENSE_UNAUTHORIZED",
|
|
134
134
|
exitCode: EXIT_CODES.AUTH_ERROR,
|
|
135
|
-
title: "CI access
|
|
136
|
-
template: '
|
|
135
|
+
title: "Legacy CI access policy denied command",
|
|
136
|
+
template: 'Legacy CI access policy denied runa execution for "{owner}"',
|
|
137
137
|
suggestions: [
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
138
|
+
"Upgrade to the latest @runa-ai/runa-cli",
|
|
139
|
+
"Public runa CLI/SDK/plugin commands should not require CI allowlist approval",
|
|
140
|
+
"Template operations use separate repository-access checks"
|
|
141
141
|
],
|
|
142
142
|
docUrl: "https://runa.dev/docs/errors/license-unauthorized"
|
|
143
143
|
},
|
|
@@ -571,4 +571,7 @@ function interpolateTemplate(template, params) {
|
|
|
571
571
|
});
|
|
572
572
|
}
|
|
573
573
|
|
|
574
|
+
// src/errors/index.ts
|
|
575
|
+
init_esm_shims();
|
|
576
|
+
|
|
574
577
|
export { ERROR_CATALOG, createError };
|
|
@@ -231,6 +231,32 @@ function extractTablesFromIdempotentSql(idempotentDir, projectRoot = process.cwd
|
|
|
231
231
|
}
|
|
232
232
|
return [...new Set(tables)].sort();
|
|
233
233
|
}
|
|
234
|
+
function extractDynamicTablePatternsFromIdempotentSql(idempotentDir, projectRoot = process.cwd()) {
|
|
235
|
+
const fullPath = path.resolve(projectRoot, idempotentDir);
|
|
236
|
+
if (!existsSync(fullPath)) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const patterns = [];
|
|
240
|
+
const prefixInFormat = /EXECUTE\s+format\s*\(\s*'CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"?([a-zA-Z_][a-zA-Z0-9_]*)"?\.)?([a-zA-Z_][a-zA-Z0-9_]*)%[sI]/gi;
|
|
241
|
+
try {
|
|
242
|
+
const files = readdirSync(fullPath).filter((f) => f.endsWith(".sql"));
|
|
243
|
+
for (const file of files) {
|
|
244
|
+
const filePath = path.join(fullPath, file);
|
|
245
|
+
const content = readFileSync(filePath, "utf-8");
|
|
246
|
+
const cleaned = content.replace(/--.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
247
|
+
for (const match of cleaned.matchAll(prefixInFormat)) {
|
|
248
|
+
const schema = match[1] || "public";
|
|
249
|
+
const prefix = match[2];
|
|
250
|
+
if (prefix && prefix.length >= 2) {
|
|
251
|
+
patterns.push(`${schema}.${prefix}*`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
return [...new Set(patterns)].sort();
|
|
259
|
+
}
|
|
234
260
|
|
|
235
261
|
// src/commands/db/utils/table-registry.ts
|
|
236
262
|
init_esm_shims();
|
|
@@ -1686,8 +1712,16 @@ function resolveSourceConfig(projectRoot, options) {
|
|
|
1686
1712
|
}
|
|
1687
1713
|
} catch {
|
|
1688
1714
|
}
|
|
1715
|
+
const resolvedIdempotentDir = isAbsolute(idempotentSqlDir) ? idempotentSqlDir : join(projectRoot, idempotentSqlDir);
|
|
1716
|
+
const dynamicPatterns = extractDynamicTablePatternsFromIdempotentSql(
|
|
1717
|
+
resolvedIdempotentDir,
|
|
1718
|
+
projectRoot
|
|
1719
|
+
);
|
|
1720
|
+
for (const pattern of dynamicPatterns) {
|
|
1721
|
+
exclusions.add(pattern);
|
|
1722
|
+
}
|
|
1689
1723
|
return {
|
|
1690
|
-
idempotentSqlDir:
|
|
1724
|
+
idempotentSqlDir: resolvedIdempotentDir,
|
|
1691
1725
|
excludeFromOrphanDetection: [...exclusions].sort((a, b) => a.localeCompare(b))
|
|
1692
1726
|
};
|
|
1693
1727
|
}
|
|
@@ -1918,9 +1952,31 @@ async function generateTablesManifest(projectRoot, options = {}) {
|
|
|
1918
1952
|
}
|
|
1919
1953
|
writeFileSync(outputPath, `${JSON.stringify(manifest, null, 2)}
|
|
1920
1954
|
`);
|
|
1955
|
+
validateManifestSourceFiles(manifest, projectRoot);
|
|
1921
1956
|
logManifestSummary(manifest, mappingResult.conflicts);
|
|
1922
1957
|
return manifest;
|
|
1923
1958
|
}
|
|
1959
|
+
function validateManifestSourceFiles(manifest, projectRoot) {
|
|
1960
|
+
const staleEntries = [];
|
|
1961
|
+
for (const table of manifest.tables) {
|
|
1962
|
+
if (!table.sourceFile) continue;
|
|
1963
|
+
const absolutePath = join(projectRoot, table.sourceFile);
|
|
1964
|
+
if (!existsSync(absolutePath)) {
|
|
1965
|
+
staleEntries.push({ table: table.qualifiedName, sourceFile: table.sourceFile });
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
if (staleEntries.length === 0) return;
|
|
1969
|
+
console.warn(
|
|
1970
|
+
`[tables-manifest] warn: ${staleEntries.length} table(s) reference non-existent sourceFile:`
|
|
1971
|
+
);
|
|
1972
|
+
for (const entry of staleEntries.slice(0, 5)) {
|
|
1973
|
+
console.warn(` ${entry.table} \u2192 ${entry.sourceFile} (file not found)`);
|
|
1974
|
+
}
|
|
1975
|
+
if (staleEntries.length > 5) {
|
|
1976
|
+
console.warn(` ... and ${staleEntries.length - 5} more`);
|
|
1977
|
+
}
|
|
1978
|
+
console.warn(" \u2192 If files were moved, re-run `runa db sync` to update sourceFile references.");
|
|
1979
|
+
}
|
|
1924
1980
|
function logManifestSummary(manifest, conflicts) {
|
|
1925
1981
|
const tableCount = manifest.tables.length;
|
|
1926
1982
|
const schemas = [...new Set(manifest.tables.map((t) => t.schema))];
|
|
@@ -1975,4 +2031,4 @@ function logManifestSummary(manifest, conflicts) {
|
|
|
1975
2031
|
console.log(" Path: .runa/manifests/tables.json\n");
|
|
1976
2032
|
}
|
|
1977
2033
|
|
|
1978
|
-
export { buildTablePatternMatcher, diffSchema, extractSchemaTablesAndEnums, extractTablesFromIdempotentSql, fetchDbTablesAndEnums, generateTablesManifest, getSqlParserUtils };
|
|
2034
|
+
export { buildTablePatternMatcher, diffSchema, extractDynamicTablePatternsFromIdempotentSql, extractSchemaTablesAndEnums, extractTablesFromIdempotentSql, fetchDbTablesAndEnums, generateTablesManifest, getSqlParserUtils };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { resolveDatabaseUrl, resolveDatabaseTarget } from './chunk-WGRVAGSR.js';
|
|
4
|
-
import { verifyPgSchemaDiffBinary, verifyDatabaseConnection, freeConnectionSlotsForPgSchemaDiff, executePgSchemaDiffPlan, detectDropTableStatements, analyzeDeclarativeDependencyContract, DB_APPLY_CHECK_MODE_CONTRACT_NOTE, formatDeclarativeDependencyViolation, summarizeDeclarativeDependencyWarnings, MAX_DETAILED_DECLARATIVE_WARNINGS, formatDeclarativeDependencyWarning, detectPgSchemaDiffVersion, STATEMENT_IDX_REGEX, hasUnparsedHazardHints, collectSqlFiles, splitSqlStatements, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, HAZARD_REGEX, PUBLIC_SCHEMA, FUNCTION_DEFINITION_RE } from './chunk-
|
|
4
|
+
import { verifyPgSchemaDiffBinary, verifyDatabaseConnection, freeConnectionSlotsForPgSchemaDiff, executePgSchemaDiffPlan, detectDropTableStatements, analyzeDeclarativeDependencyContract, DB_APPLY_CHECK_MODE_CONTRACT_NOTE, formatDeclarativeDependencyViolation, summarizeDeclarativeDependencyWarnings, MAX_DETAILED_DECLARATIVE_WARNINGS, formatDeclarativeDependencyWarning, detectPgSchemaDiffVersion, STATEMENT_IDX_REGEX, hasUnparsedHazardHints, collectSqlFiles, splitSqlStatements, PG_SCHEMA_DIFF_APPLY_TIMEOUT_MS, HAZARD_REGEX, PUBLIC_SCHEMA, FUNCTION_DEFINITION_RE } from './chunk-HWR5NUUZ.js';
|
|
5
5
|
import { splitPlpgsqlStatementsWithOffsets, extractExecuteExpressions, extractStaticSqlFromExpression } from './chunk-Y5ANTCKE.js';
|
|
6
6
|
import { loadEnvFiles } from './chunk-IWVXI5O4.js';
|
|
7
|
-
import { CLI_VERSION } from './chunk-
|
|
8
|
-
import { generateTablesManifest } from './chunk-
|
|
7
|
+
import { CLI_VERSION } from './chunk-ZPE52NEK.js';
|
|
8
|
+
import { generateTablesManifest } from './chunk-O3M7A73M.js';
|
|
9
9
|
import { parsePostgresUrl, buildPsqlEnv, buildPsqlArgs, stripSqlComments, psqlSyncQuery, blankDollarQuotedBodies, psqlSyncBatch, psqlSyncFile } from './chunk-A6A7JIRD.js';
|
|
10
10
|
import { init_local_supabase, buildLocalDatabaseUrl } from './chunk-QSEF4T3Y.js';
|
|
11
11
|
import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
|
|
@@ -2309,7 +2309,7 @@ function extractQualifiedNameAfterKeyword(sql, keywordPattern) {
|
|
|
2309
2309
|
const rest = sql.slice(keywordMatch.index + keywordMatch[0].length);
|
|
2310
2310
|
const nameMatch = rest.match(/^\s*["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?/);
|
|
2311
2311
|
if (!nameMatch) return null;
|
|
2312
|
-
return `${nameMatch[1]
|
|
2312
|
+
return `${nameMatch[1]?.replace(/"/g, "").toLowerCase()}.${nameMatch[2]?.replace(/"/g, "").toLowerCase()}`;
|
|
2313
2313
|
}
|
|
2314
2314
|
function extractFkReferences(sql) {
|
|
2315
2315
|
const refs = /* @__PURE__ */ new Set();
|
|
@@ -2317,7 +2317,7 @@ function extractFkReferences(sql) {
|
|
|
2317
2317
|
/REFERENCES\s+["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?\s*\(/gi
|
|
2318
2318
|
)) {
|
|
2319
2319
|
refs.add(
|
|
2320
|
-
`${match[1]
|
|
2320
|
+
`${match[1]?.replace(/"/g, "").toLowerCase()}.${match[2]?.replace(/"/g, "").toLowerCase()}`
|
|
2321
2321
|
);
|
|
2322
2322
|
}
|
|
2323
2323
|
return [...refs];
|
|
@@ -2327,12 +2327,12 @@ function extractRelationRefsFromBody(sql) {
|
|
|
2327
2327
|
for (const m of sql.matchAll(
|
|
2328
2328
|
/\bFROM\s+["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?/gi
|
|
2329
2329
|
)) {
|
|
2330
|
-
refs.add(`${m[1]
|
|
2330
|
+
refs.add(`${m[1]?.replace(/"/g, "").toLowerCase()}.${m[2]?.replace(/"/g, "").toLowerCase()}`);
|
|
2331
2331
|
}
|
|
2332
2332
|
for (const m of sql.matchAll(
|
|
2333
2333
|
/["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?\s*%ROWTYPE/gi
|
|
2334
2334
|
)) {
|
|
2335
|
-
refs.add(`${m[1]
|
|
2335
|
+
refs.add(`${m[1]?.replace(/"/g, "").toLowerCase()}.${m[2]?.replace(/"/g, "").toLowerCase()}`);
|
|
2336
2336
|
}
|
|
2337
2337
|
return [...refs];
|
|
2338
2338
|
}
|
|
@@ -2404,7 +2404,7 @@ function analyzeWithRegexFallback(strippedSql) {
|
|
|
2404
2404
|
const match = strippedSql.match(
|
|
2405
2405
|
/(?:CREATE|ALTER|DROP)\s+POLICY\s+.*?\bON\s+["']?([A-Za-z_]\w*)["']?\s*\.\s*["']?([A-Za-z_]\w*)["']?/i
|
|
2406
2406
|
);
|
|
2407
|
-
const key = match ? `${match[1]
|
|
2407
|
+
const key = match ? `${match[1]?.replace(/"/g, "").toLowerCase()}.${match[2]?.replace(/"/g, "").toLowerCase()}` : null;
|
|
2408
2408
|
return {
|
|
2409
2409
|
...buildBaseStatementAnalysis(strippedSql, "relation"),
|
|
2410
2410
|
targetKey: key,
|
|
@@ -2565,7 +2565,7 @@ function stabilizeAnalyzedPlanOrder(analyzedPlan) {
|
|
|
2565
2565
|
const groupedStatements = /* @__PURE__ */ new Set();
|
|
2566
2566
|
const deferredTriggerStatements = [];
|
|
2567
2567
|
const createdFunctionKeys = new Set(
|
|
2568
|
-
analyzedPlan.statements.filter((s) => s.objectKind === "function" && s.targetFunctionKey).map((s) => s.targetFunctionKey
|
|
2568
|
+
analyzedPlan.statements.filter((s) => s.objectKind === "function" && s.targetFunctionKey).map((s) => s.targetFunctionKey?.toLowerCase())
|
|
2569
2569
|
);
|
|
2570
2570
|
for (const statement of analyzedPlan.statements) {
|
|
2571
2571
|
if (statement.phase === 50 && statement.functionDependencies.some((fn) => createdFunctionKeys.has(fn.toLowerCase()))) {
|
|
@@ -3322,7 +3322,30 @@ async function createShadowDbWithExtensions(config) {
|
|
|
3322
3322
|
// Extensions like PostGIS can take time
|
|
3323
3323
|
});
|
|
3324
3324
|
if (extResult.status !== 0) {
|
|
3325
|
-
|
|
3325
|
+
const stderr = extResult.stderr?.trim() ?? "";
|
|
3326
|
+
logger7.error(`Shadow DB extension installation failed: ${stderr}`);
|
|
3327
|
+
logger7.error("Configured extensions may not be available in the target PostgreSQL server.");
|
|
3328
|
+
logger7.error("Check that the extensions are installed on the server:");
|
|
3329
|
+
for (const ext of extensions) {
|
|
3330
|
+
logger7.error(` SELECT * FROM pg_available_extensions WHERE name = '${ext}';`);
|
|
3331
|
+
}
|
|
3332
|
+
logger7.error(
|
|
3333
|
+
"If running locally with Supabase, ensure extensions are enabled in the dashboard."
|
|
3334
|
+
);
|
|
3335
|
+
}
|
|
3336
|
+
const verifyResult = psqlSyncQuery({
|
|
3337
|
+
databaseUrl: shadowDsn,
|
|
3338
|
+
sql: `SELECT extname FROM pg_extension WHERE extname = ANY(ARRAY[${extensions.map((e) => `'${e}'`).join(",")}])`,
|
|
3339
|
+
timeout: 1e4
|
|
3340
|
+
});
|
|
3341
|
+
if (verifyResult.status === 0) {
|
|
3342
|
+
const installed = (verifyResult.stdout ?? "").split("\n").map((l) => l.trim()).filter(Boolean);
|
|
3343
|
+
const missing = extensions.filter((ext) => !installed.includes(ext));
|
|
3344
|
+
if (missing.length > 0) {
|
|
3345
|
+
logger7.warn(
|
|
3346
|
+
`Shadow DB missing extensions: ${missing.join(", ")}. pg-schema-diff may fail on extension-defined types (geometry, vector, etc.).`
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3326
3349
|
}
|
|
3327
3350
|
bootstrapShadowDbSchemaPrerequisites(sourceDbUrl, shadowDsn);
|
|
3328
3351
|
logger7.debug(`Shadow database ready with extensions: ${extensions.join(", ")}`);
|
|
@@ -7203,11 +7226,54 @@ async function applyWithRetry(params) {
|
|
|
7203
7226
|
retryWaitMs: result.totalWaitMs
|
|
7204
7227
|
};
|
|
7205
7228
|
}
|
|
7229
|
+
function autoDetectRequiredExtensions(targetDir) {
|
|
7230
|
+
const EXTENSION_TYPE_KEYWORDS = {
|
|
7231
|
+
geometry: "postgis",
|
|
7232
|
+
geography: "postgis",
|
|
7233
|
+
box2d: "postgis",
|
|
7234
|
+
box3d: "postgis",
|
|
7235
|
+
raster: "postgis_raster",
|
|
7236
|
+
vector: "vector",
|
|
7237
|
+
halfvec: "vector",
|
|
7238
|
+
sparsevec: "vector",
|
|
7239
|
+
citext: "citext",
|
|
7240
|
+
hstore: "hstore",
|
|
7241
|
+
ltree: "ltree"
|
|
7242
|
+
};
|
|
7243
|
+
const declarativeDir = join(targetDir, "supabase", "schemas", "declarative");
|
|
7244
|
+
if (!existsSync(declarativeDir)) return [];
|
|
7245
|
+
const detected = /* @__PURE__ */ new Set();
|
|
7246
|
+
try {
|
|
7247
|
+
const files = readdirSync(declarativeDir).filter((f) => f.endsWith(".sql"));
|
|
7248
|
+
for (const file of files) {
|
|
7249
|
+
const content = readFileSync(join(declarativeDir, file), "utf-8").toLowerCase();
|
|
7250
|
+
for (const [typeName, extName] of Object.entries(EXTENSION_TYPE_KEYWORDS)) {
|
|
7251
|
+
if (content.includes(typeName)) {
|
|
7252
|
+
detected.add(extName);
|
|
7253
|
+
}
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
} catch {
|
|
7257
|
+
}
|
|
7258
|
+
return [...detected].sort();
|
|
7259
|
+
}
|
|
7260
|
+
function resolveEffectiveShadowExtensions(configured, targetDir, verbose) {
|
|
7261
|
+
if (configured && configured.length > 0) return configured;
|
|
7262
|
+
const autoDetected = autoDetectRequiredExtensions(targetDir);
|
|
7263
|
+
if (autoDetected.length > 0 && verbose) {
|
|
7264
|
+
logger13.debug(`Auto-detected shadow DB extensions from SQL: ${autoDetected.join(", ")}`);
|
|
7265
|
+
}
|
|
7266
|
+
return autoDetected.length > 0 ? autoDetected : void 0;
|
|
7267
|
+
}
|
|
7206
7268
|
function loadPgSchemaDiffConfigState(targetDir, verbose) {
|
|
7207
7269
|
try {
|
|
7208
7270
|
const config = loadRunaConfig(targetDir);
|
|
7209
7271
|
return {
|
|
7210
|
-
shadowExtensions:
|
|
7272
|
+
shadowExtensions: resolveEffectiveShadowExtensions(
|
|
7273
|
+
config.database?.pgSchemaDiff?.shadowDbExtensions,
|
|
7274
|
+
targetDir,
|
|
7275
|
+
verbose
|
|
7276
|
+
),
|
|
7211
7277
|
configExclusions: config.database?.pgSchemaDiff?.excludeFromOrphanDetection ? [...config.database.pgSchemaDiff.excludeFromOrphanDetection] : void 0,
|
|
7212
7278
|
schemaCheckExclusions: config.database?.pgSchemaDiff?.excludeFromSchemaCheck ? [...config.database.pgSchemaDiff.excludeFromSchemaCheck] : void 0
|
|
7213
7279
|
};
|
|
@@ -7215,7 +7281,9 @@ function loadPgSchemaDiffConfigState(targetDir, verbose) {
|
|
|
7215
7281
|
if (verbose) {
|
|
7216
7282
|
logger13.debug("Could not load runa.config.ts - continuing without extension support");
|
|
7217
7283
|
}
|
|
7218
|
-
return {
|
|
7284
|
+
return {
|
|
7285
|
+
shadowExtensions: resolveEffectiveShadowExtensions(void 0, targetDir, verbose)
|
|
7286
|
+
};
|
|
7219
7287
|
}
|
|
7220
7288
|
}
|
|
7221
7289
|
function createPrefilterState(schemasDir, verbose, configExclusions) {
|
|
@@ -19,6 +19,64 @@ init_esm_shims();
|
|
|
19
19
|
var COMPATIBLE_TEMPLATES_VERSION = "0.7.3";
|
|
20
20
|
var TEMPLATES_PACKAGE_NAME = "@r06-dev/runa-templates";
|
|
21
21
|
var GITHUB_PACKAGES_REGISTRY = "https://npm.pkg.github.com";
|
|
22
|
+
var TEMPLATES_SOURCE_REPOSITORY = "r06-dev/runa";
|
|
23
|
+
var TEMPLATES_SOURCE_REPOSITORY_API_URL = `https://api.github.com/repos/${TEMPLATES_SOURCE_REPOSITORY}`;
|
|
24
|
+
|
|
25
|
+
// src/utils/template-access.ts
|
|
26
|
+
init_esm_shims();
|
|
27
|
+
var TEMPLATE_ACCESS_TIMEOUT_MS = 5e3;
|
|
28
|
+
var TEMPLATE_ACCESS_USER_AGENT = "runa-cli/template-access-check";
|
|
29
|
+
async function hasRepoAccessWithToken(token) {
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timeoutId = setTimeout(() => controller.abort(), TEMPLATE_ACCESS_TIMEOUT_MS);
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(TEMPLATES_SOURCE_REPOSITORY_API_URL, {
|
|
34
|
+
method: "GET",
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Bearer ${token}`,
|
|
37
|
+
Accept: "application/vnd.github+json",
|
|
38
|
+
"User-Agent": TEMPLATE_ACCESS_USER_AGENT
|
|
39
|
+
},
|
|
40
|
+
signal: controller.signal
|
|
41
|
+
});
|
|
42
|
+
return response.ok;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
} finally {
|
|
46
|
+
clearTimeout(timeoutId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function hasRepoAccessWithGh() {
|
|
50
|
+
try {
|
|
51
|
+
const result = await secureGh(["api", `repos/${TEMPLATES_SOURCE_REPOSITORY}`, "--silent"], {
|
|
52
|
+
reject: false,
|
|
53
|
+
timeout: TEMPLATE_ACCESS_TIMEOUT_MS,
|
|
54
|
+
stdio: "pipe"
|
|
55
|
+
});
|
|
56
|
+
return result.exitCode === 0;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function verifyTemplateRepoAccess() {
|
|
62
|
+
const explicitToken = process.env.NODE_AUTH_TOKEN?.trim();
|
|
63
|
+
if (explicitToken && await hasRepoAccessWithToken(explicitToken)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (await hasRepoAccessWithGh()) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
throw new CLIError(
|
|
70
|
+
`Template operations require access to ${TEMPLATES_SOURCE_REPOSITORY}.`,
|
|
71
|
+
"TEMPLATE_REPO_ACCESS_REQUIRED",
|
|
72
|
+
[
|
|
73
|
+
`Use a GitHub account that can access https://github.com/${TEMPLATES_SOURCE_REPOSITORY}`,
|
|
74
|
+
"Authenticate with GitHub CLI: gh auth login",
|
|
75
|
+
"Or export NODE_AUTH_TOKEN from an authorized account",
|
|
76
|
+
"Normal runa CLI/SDK/plugin commands do not require this access"
|
|
77
|
+
]
|
|
78
|
+
);
|
|
79
|
+
}
|
|
22
80
|
|
|
23
81
|
// src/utils/template-fetcher.ts
|
|
24
82
|
var SAFE_VERSION_PATTERN = /^[a-zA-Z0-9._-]+$/;
|
|
@@ -273,6 +331,7 @@ async function fetchTemplates(options = {}) {
|
|
|
273
331
|
const version = options.version ?? COMPATIBLE_TEMPLATES_VERSION;
|
|
274
332
|
const fresh = options.fresh ?? false;
|
|
275
333
|
const verbose = options.verbose ?? false;
|
|
334
|
+
await verifyTemplateRepoAccess();
|
|
276
335
|
const workspaceResult = getWorkspaceTemplatesResult(resolveWorkspaceTemplates(), verbose);
|
|
277
336
|
if (workspaceResult) {
|
|
278
337
|
return workspaceResult;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql, getDbPlanArtifactPath, runDbApply } from './chunk-
|
|
3
|
+
import { normalizeDatabaseUrlForDdl, parseBoolish, enhanceConnectionError, isIdempotentRoleHazard, detectAppSchemas, formatSchemasForSql, getDbPlanArtifactPath, runDbApply } from './chunk-XRLIZKB2.js';
|
|
4
4
|
import './chunk-WGRVAGSR.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-HWR5NUUZ.js';
|
|
6
6
|
import './chunk-UHDAYPHH.js';
|
|
7
7
|
import './chunk-Y5ANTCKE.js';
|
|
8
8
|
import './chunk-IWVXI5O4.js';
|
|
9
|
-
import './chunk-
|
|
9
|
+
import './chunk-ZPE52NEK.js';
|
|
10
10
|
import './chunk-B7C7CLW2.js';
|
|
11
11
|
import './chunk-QDF7QXBL.js';
|
|
12
12
|
import { getSnapshotStateName, getSnapshotStatePaths, isSnapshotComplete } from './chunk-XVNDDHAF.js';
|
|
13
13
|
import { createInitialSummary, resolveMode, appendGithubStepSummary, buildCiProdApplyStepSummaryMarkdown, setSummaryErrorFromUnknown, writeEnvLocal, startAppBackground, waitForAppReady, executePrSetupBase, createErrorOutput, requireCiAutoApprove, resolveProdApplyInputs, parseIntOr, classifyCiProdApplyError, addGithubMask } from './chunk-EXR4J2JT.js';
|
|
14
|
-
import './chunk-
|
|
14
|
+
import './chunk-O3M7A73M.js';
|
|
15
15
|
import { parsePostgresUrl, buildPsqlArgs, buildPsqlEnv, psqlSyncQuery } from './chunk-A6A7JIRD.js';
|
|
16
16
|
import { ensureRunaTmpDir, runLogged } from './chunk-ELXXQIGW.js';
|
|
17
17
|
import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { enableNonInteractiveMode } from './chunk-6Y3LAUGL.js';
|
|
4
4
|
import { getRequestedCommandNameFromArgv } from './chunk-UWWSAPDR.js';
|
|
5
|
-
import { CLI_VERSION, HAS_ADMIN_COMMAND } from './chunk-
|
|
5
|
+
import { CLI_VERSION, HAS_ADMIN_COMMAND } from './chunk-ZPE52NEK.js';
|
|
6
6
|
import { emitDefaultSuccessIfNeeded } from './chunk-WJXC4MVY.js';
|
|
7
7
|
import { parseOutputFormat, setOutputFormat, getOutputFormatFromEnv } from './chunk-HKUWEGUX.js';
|
|
8
8
|
import { init_esm_shims } from './chunk-VRXHCR5K.js';
|
|
@@ -145,7 +145,7 @@ function isTestCommand(requested) {
|
|
|
145
145
|
async function registerProjectLifecycleCommands(program, requested, loadAllCommands) {
|
|
146
146
|
if (!loadAllCommands && requested) {
|
|
147
147
|
if (requested === "init") {
|
|
148
|
-
const { initCommand: initCommand2 } = await import('./init-
|
|
148
|
+
const { initCommand: initCommand2 } = await import('./init-4UAWYY75.js');
|
|
149
149
|
program.addCommand(initCommand2);
|
|
150
150
|
return;
|
|
151
151
|
}
|
|
@@ -155,7 +155,7 @@ async function registerProjectLifecycleCommands(program, requested, loadAllComma
|
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
157
|
if (requested === "upgrade") {
|
|
158
|
-
const { upgradeCommand: upgradeCommand2 } = await import('./upgrade-
|
|
158
|
+
const { upgradeCommand: upgradeCommand2 } = await import('./upgrade-LBO3Z3J7.js');
|
|
159
159
|
program.addCommand(upgradeCommand2);
|
|
160
160
|
return;
|
|
161
161
|
}
|
|
@@ -170,7 +170,7 @@ async function registerProjectLifecycleCommands(program, requested, loadAllComma
|
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
172
|
if (requested === "dev") {
|
|
173
|
-
const { devCommand: devCommand2 } = await import('./dev-
|
|
173
|
+
const { devCommand: devCommand2 } = await import('./dev-QR55VDNZ.js');
|
|
174
174
|
program.addCommand(devCommand2);
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
@@ -183,12 +183,12 @@ async function registerProjectLifecycleCommands(program, requested, loadAllComma
|
|
|
183
183
|
{ buildCommand },
|
|
184
184
|
{ devCommand }
|
|
185
185
|
] = await Promise.all([
|
|
186
|
-
import('./init-
|
|
186
|
+
import('./init-4UAWYY75.js'),
|
|
187
187
|
import('./prepare-32DOVHTE.js'),
|
|
188
|
-
import('./upgrade-
|
|
188
|
+
import('./upgrade-LBO3Z3J7.js'),
|
|
189
189
|
import('./validate-CAAW4Y44.js'),
|
|
190
190
|
import('./build-P2A6345N.js'),
|
|
191
|
-
import('./dev-
|
|
191
|
+
import('./dev-QR55VDNZ.js')
|
|
192
192
|
]);
|
|
193
193
|
program.addCommand(initCommand);
|
|
194
194
|
program.addCommand(prepareCommand);
|
|
@@ -462,11 +462,11 @@ async function registerFocusedStatusUtilityCommand(program, requested) {
|
|
|
462
462
|
return false;
|
|
463
463
|
}
|
|
464
464
|
async function registerCiCommand(program) {
|
|
465
|
-
const { ciCommand } = await import('./ci-
|
|
465
|
+
const { ciCommand } = await import('./ci-3HZWUQFN.js');
|
|
466
466
|
program.addCommand(ciCommand);
|
|
467
467
|
}
|
|
468
468
|
async function registerDbCommand(program) {
|
|
469
|
-
const { dbCommand } = await import('./db-
|
|
469
|
+
const { dbCommand } = await import('./db-PRGL7PBX.js');
|
|
470
470
|
program.addCommand(dbCommand);
|
|
471
471
|
}
|
|
472
472
|
async function registerServicesCommand(program) {
|
|
@@ -478,7 +478,7 @@ async function registerEnvCommand(program) {
|
|
|
478
478
|
program.addCommand(envCommand);
|
|
479
479
|
}
|
|
480
480
|
async function registerHotfixCommand(program) {
|
|
481
|
-
const { hotfixCommand } = await import('./hotfix-
|
|
481
|
+
const { hotfixCommand } = await import('./hotfix-JYHDY2M6.js');
|
|
482
482
|
program.addCommand(hotfixCommand);
|
|
483
483
|
}
|
|
484
484
|
async function registerSdkCommand(program) {
|
|
@@ -498,7 +498,7 @@ async function registerWorkflowCommand(program) {
|
|
|
498
498
|
program.addCommand(workflowCommand);
|
|
499
499
|
}
|
|
500
500
|
async function registerVulnCheckCommand(program) {
|
|
501
|
-
const { vulnCheckCommand } = await import('./vuln-check-
|
|
501
|
+
const { vulnCheckCommand } = await import('./vuln-check-5JJ2YAJW.js');
|
|
502
502
|
program.addCommand(vulnCheckCommand);
|
|
503
503
|
}
|
|
504
504
|
async function registerTemplateCheckCommand(program) {
|
|
@@ -708,7 +708,7 @@ async function executeProgram(program) {
|
|
|
708
708
|
if (!isHelpOrVersion && shouldRunRuntimeBootstrap(argv)) {
|
|
709
709
|
const [{ loadEnvFiles }, { enforceLicenseInCI }] = await Promise.all([
|
|
710
710
|
import('./env-files-ONBC47I6.js'),
|
|
711
|
-
import('./license-
|
|
711
|
+
import('./license-M6ODBV4X.js')
|
|
712
712
|
]);
|
|
713
713
|
loadEnvFiles({
|
|
714
714
|
silent: true
|
|
@@ -42,8 +42,14 @@ export interface MissingExtensionDetection {
|
|
|
42
42
|
* and map them to the required PostgreSQL extensions.
|
|
43
43
|
*/
|
|
44
44
|
export declare function detectMissingExtensionType(errorOutput: string): MissingExtensionDetection;
|
|
45
|
+
/**
|
|
46
|
+
* Detect the actual extension management SQL file in the project.
|
|
47
|
+
* Falls back to the default path if no extension file is found.
|
|
48
|
+
*/
|
|
49
|
+
export declare function detectExtensionFilePath(): string;
|
|
45
50
|
/**
|
|
46
51
|
* Format actionable hint for missing extension type errors.
|
|
52
|
+
* Auto-detects the actual extension file path in the project.
|
|
47
53
|
*/
|
|
48
54
|
export declare function formatExtensionErrorHint(detection: MissingExtensionDetection): string;
|
|
49
55
|
export interface PartitionPrivilegeDetection {
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI HINT: Production apply precheck orchestration
|
|
3
|
-
*
|
|
4
|
-
* Purpose: Run full production precheck pipeline: local risk checks + plan boundary reconciliation.
|
|
5
|
-
* Collects findings, applies strict mode, logs results, and throws on blockers.
|
|
6
|
-
*
|
|
7
|
-
* Used by: db-sync.ts (maybeRunProductionApplyPrecheck)
|
|
8
|
-
*/
|
|
9
1
|
import { type createCLILogger } from '@runa-ai/runa';
|
|
10
2
|
import type { DbApplyOutput } from '../../apply/contract.js';
|
|
11
3
|
import type { RunaDbEnv } from '../../utils/db-target.js';
|
|
@@ -4,12 +4,29 @@
|
|
|
4
4
|
* Purpose: Generate guidance warnings for schema boundary violations,
|
|
5
5
|
* security definer issues, trigger ownership, and managed schema access
|
|
6
6
|
*/
|
|
7
|
-
import type { SchemaGuardrailReport, SchemaGraphFunctionClaim, SchemaGraphFileNode, SchemaGraphSchemaNode } from './schema-guardrail-types.js';
|
|
7
|
+
import type { BoundaryGuidanceWarning, SchemaGuardrailReport, SchemaGraphFunctionClaim, SchemaGraphFileNode, SchemaGraphSchemaNode, SchemaGraphTableNode, LoadedSqlSources } from './schema-guardrail-types.js';
|
|
8
8
|
declare function buildBoundaryGuidanceWarnings(params: {
|
|
9
9
|
fileNodes: SchemaGraphFileNode[];
|
|
10
10
|
schemaNodes: SchemaGraphSchemaNode[];
|
|
11
11
|
functionClaims: SchemaGraphFunctionClaim[];
|
|
12
12
|
ownerFileByTable: Map<string, string>;
|
|
13
13
|
}): SchemaGuardrailReport['boundaryGuidanceWarnings'];
|
|
14
|
+
/**
|
|
15
|
+
* Detect trigger dispatch coverage gaps.
|
|
16
|
+
*
|
|
17
|
+
* When a trigger calls a dispatch function (e.g., check_org_id_matches_parent)
|
|
18
|
+
* with literal arguments (e.g., 'parent_table'), and the function body has
|
|
19
|
+
* CASE/WHEN branches that don't cover those arguments, report a warning.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildTriggerDispatchGapWarnings(params: {
|
|
22
|
+
tableNodes: Map<string, SchemaGraphTableNode>;
|
|
23
|
+
sources: LoadedSqlSources;
|
|
24
|
+
}): BoundaryGuidanceWarning[];
|
|
25
|
+
/**
|
|
26
|
+
* Detect index names that appear in both declarative and idempotent layers.
|
|
27
|
+
* This is typically redundant — declarative indexes are pg-schema-diff managed,
|
|
28
|
+
* and idempotent IF NOT EXISTS copies create silent redundancy.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildCrossLayerDuplicateIndexWarnings(sources: LoadedSqlSources): BoundaryGuidanceWarning[];
|
|
14
31
|
export { buildBoundaryGuidanceWarnings };
|
|
15
32
|
//# sourceMappingURL=schema-guardrail-graph-guidance.d.ts.map
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Includes: Trigger extraction, declarative file parsing,
|
|
6
6
|
* table/FK/dependency/file node construction
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import type { SqlFile } from '../utils/declarative-dependency-sql-utils.js';
|
|
9
9
|
import { type DeclarativeFileRecord, type DefinedFunctionMetadata, type ManagedBoundaryMetadata, type IdempotentTouchMetadata } from './schema-guardrail-graph-types.js';
|
|
10
10
|
import type { SchemaGuardrailConfig, SchemaGraphFileDependencyEdge, SchemaGraphFileNode, SchemaGraphFunctionClaim, SchemaGraphPolicyClaim, SchemaGraphPolicyRef, SchemaGraphTableNode, SchemaGraphTriggerRef, SchemaGuardrailReport } from './schema-guardrail-types.js';
|
|
11
11
|
declare function parseCreateTriggerStatement(statement: string): {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Purpose: SQL string sanitization, pattern matching, and metadata extraction
|
|
5
5
|
* for idempotent file analysis
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import type { SqlFile } from '../utils/declarative-dependency-sql-utils.js';
|
|
8
8
|
import type { IdempotentTouchMetadata } from './schema-guardrail-graph-types.js';
|
|
9
9
|
declare function normalizeSqlIdentifier(value: string): string;
|
|
10
10
|
declare function consumeSingleQuotedLiteral(params: {
|
|
@@ -3,8 +3,8 @@ export type SchemaManagedBlockKind = 'file-header' | 'table-header';
|
|
|
3
3
|
export type SchemaGuardrailMode = 'check' | 'sync';
|
|
4
4
|
export type SchemaGraphFileLayer = 'declarative' | 'idempotent';
|
|
5
5
|
export type SchemaGraphFileAuthoringRole = 'declarative-owner' | 'operational';
|
|
6
|
-
export type BoundaryGuidanceWarningKind = 'schema' | 'function' | 'policy' | 'security_definer' | 'trigger_function' | 'managed_boundary';
|
|
7
|
-
export type LocalBlindSpotBlockerKind = 'cross-schema-rls' | 'dynamic-sql' | 'extension-placement';
|
|
6
|
+
export type BoundaryGuidanceWarningKind = 'schema' | 'function' | 'policy' | 'security_definer' | 'trigger_function' | 'trigger_dispatch_gap' | 'managed_boundary';
|
|
7
|
+
export type LocalBlindSpotBlockerKind = 'cross-schema-rls' | 'dynamic-sql' | 'dynamic-sql-infra' | 'extension-placement';
|
|
8
8
|
export type SchemaGuardrailPhaseId = 'load_sources' | 'build_static_graph' | 'validate_ownership' | 'compare_generated_headers' | 'refresh_generated_headers' | 'handoff_db_sync' | 'runtime_reconcile' | 'publish_report';
|
|
9
9
|
export type SchemaGuardrailFailureCode = 'source_load_failed' | 'duplicate_table_owner' | 'duplicate_function_owner' | 'policy_ownership_conflict' | 'raw_cross_schema_rls_blocked' | 'dynamic_sql_blocked' | 'extension_placement_blocked' | 'stale_generated_header' | 'generated_header_validation_failed' | 'generated_header_rewrite_failed' | 'static_graph_build_failed' | 'critical_runtime_graph_contradiction' | 'sync_apply_failed';
|
|
10
10
|
export interface SchemaGraphFileNode {
|
|
@@ -58,6 +58,8 @@ export interface SchemaGraphTriggerRef {
|
|
|
58
58
|
timing: 'BEFORE' | 'AFTER' | 'INSTEAD OF';
|
|
59
59
|
event: string;
|
|
60
60
|
functionName?: string;
|
|
61
|
+
/** Literal string arguments passed to the trigger function (e.g., 'parent_table') */
|
|
62
|
+
functionArgs?: string[];
|
|
61
63
|
}
|
|
62
64
|
export interface SchemaGraphTableNode {
|
|
63
65
|
schema: string;
|