@runa-ai/runa-cli 0.10.1 → 0.10.2
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-Y5ANTCKE.js → chunk-EZ46JIEO.js} +5 -2
- package/dist/{chunk-ZPE52NEK.js → chunk-IR7SA2ME.js} +1 -1
- package/dist/{chunk-XRLIZKB2.js → chunk-LCJNIHZY.js} +3 -3
- package/dist/{chunk-PAWNJA3N.js → chunk-XFXGFUAM.js} +1 -1
- package/dist/{ci-3HZWUQFN.js → ci-6XYG7XNX.js} +3 -3
- package/dist/{cli-RES5QRC2.js → cli-2XL3VESS.js} +8 -8
- package/dist/commands/build/contract.d.ts +2 -2
- package/dist/commands/build/machine.d.ts +6 -6
- package/dist/commands/ci/commands/ci-prod-types.d.ts +1 -1
- package/dist/commands/ci/machine/contract.d.ts +10 -10
- package/dist/commands/ci/machine/machine.d.ts +3 -3
- package/dist/commands/ci/utils/ci-summary.d.ts +3 -3
- package/dist/commands/db/apply/contract.d.ts +1 -1
- package/dist/commands/db/apply/helpers/planner-artifact.d.ts +1 -1
- package/dist/commands/db/commands/db-preview-profile.d.ts +1 -1
- package/dist/commands/db/preflight/contract.d.ts +1 -1
- package/dist/commands/db/sync/contract.d.ts +5 -5
- package/dist/commands/db/sync/machine.d.ts +2 -2
- package/dist/commands/db/sync/schema-guardrail-graph-metadata.d.ts +1 -7
- package/dist/commands/db/utils/duplicate-function-ownership-allowlist.d.ts +13 -0
- package/dist/commands/upgrade.d.ts +36 -0
- package/dist/{db-PRGL7PBX.js → db-4AGPISOW.js} +326 -283
- package/dist/index.js +3 -3
- package/dist/{risk-detector-S7XQF4I2.js → risk-detector-GDDLISVE.js} +1 -1
- package/dist/{risk-detector-core-TGFKWHRS.js → risk-detector-core-YI3M6INI.js} +1 -1
- package/dist/{risk-detector-plpgsql-O32TUR34.js → risk-detector-plpgsql-4GWEQXUG.js} +1 -1
- package/dist/{template-check-VNNQQXCX.js → template-check-D35F2GDP.js} +4 -0
- package/dist/{upgrade-LBO3Z3J7.js → upgrade-X7P6WRD5.js} +189 -19
- package/dist/{vuln-check-5JJ2YAJW.js → vuln-check-LMDYYJUE.js} +1 -1
- package/dist/{vuln-checker-JF5234BL.js → vuln-checker-NHXLNZRM.js} +1 -1
- package/dist/{watch-RFVCEQLH.js → watch-4RHXVCQ3.js} +1 -1
- package/package.json +3 -3
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { detectDatabaseStack, getStackPaths } from './chunk-MILCC3B6.js';
|
|
4
|
-
import { categorizeRisks, detectSchemaRisks } from './chunk-
|
|
5
|
-
import { isExecaError, resolveDbPreviewEnvironment, buildDbPlanCommandLabel, runDbApply, buildDbApplyCliError, DbPlanOutputSchema, parseDbPreviewProfile, DEFAULT_DB_PREVIEW_PROFILE, getDbPreviewModeLabel, buildDbPreviewCommandLabel, isCompareOnlyPreviewProfile, DbApplyOutputSchema, applyCommand, getDbPreviewIdempotentSchemaCount, classifyDbSyncCommandFailure, getDbSyncFallbackSuggestions, detectAppSchemas, normalizeDatabaseUrlForDdl, analyzeDuplicateFunctionOwnership, formatDuplicateFunctionOwnershipFinding, reviewDeclarativeDependencyWarnings, logDeclarativeDependencyWarnings, buildDeclarativeDependencyWarningFailureLines, parsePlanOutput, validateDependencyOrder, getBoundaryPolicy, resolveProductionApplyStrictMode, findDeclarativeRiskAllowlistMatch, assertBoundaryPolicyUsable, assertBoundaryPolicyQualityGate, formatSchemasForSql, findDirectoryPlacementAllowlistMatch, formatAllowlistMetadata, assessPlanSize, formatPlanSizeSummary, extractFunctionOwnershipDefinition } from './chunk-
|
|
4
|
+
import { categorizeRisks, detectSchemaRisks } from './chunk-XFXGFUAM.js';
|
|
5
|
+
import { isExecaError, resolveDbPreviewEnvironment, buildDbPlanCommandLabel, runDbApply, buildDbApplyCliError, DbPlanOutputSchema, parseDbPreviewProfile, DEFAULT_DB_PREVIEW_PROFILE, getDbPreviewModeLabel, buildDbPreviewCommandLabel, isCompareOnlyPreviewProfile, DbApplyOutputSchema, applyCommand, getDbPreviewIdempotentSchemaCount, classifyDbSyncCommandFailure, getDbSyncFallbackSuggestions, detectAppSchemas, normalizeDatabaseUrlForDdl, analyzeDuplicateFunctionOwnership, formatDuplicateFunctionOwnershipFinding, reviewDeclarativeDependencyWarnings, logDeclarativeDependencyWarnings, buildDeclarativeDependencyWarningFailureLines, parsePlanOutput, validateDependencyOrder, getBoundaryPolicy, resolveProductionApplyStrictMode, findDeclarativeRiskAllowlistMatch, assertBoundaryPolicyUsable, assertBoundaryPolicyQualityGate, formatSchemasForSql, findDirectoryPlacementAllowlistMatch, formatAllowlistMetadata, assessPlanSize, formatPlanSizeSummary, extractFunctionOwnershipDefinition } from './chunk-LCJNIHZY.js';
|
|
6
6
|
import { createError } from './chunk-NIS77243.js';
|
|
7
7
|
import { resolveDatabaseUrl, resolveDatabaseTarget, tryResolveDatabaseUrl } from './chunk-WGRVAGSR.js';
|
|
8
8
|
export { resolveDatabaseUrl, tryResolveDatabaseUrl } from './chunk-WGRVAGSR.js';
|
|
9
9
|
import { analyzeDeclarativeDependencyContract, formatDeclarativeDependencyViolation, parseSqlFilename, collectSqlFiles, splitSqlStatements, ALLOW_DYNAMIC_SQL_ANNOTATION, extractFirstDollarBody, sanitizeExecutableCode, detectExtensionFilePath, FUNCTION_DEFINITION_RE, blankQuotedStrings, sanitizeExecutableCodePreserveStrings, countNewlines, shouldReviewUnknownDeclarativeDdl, extractDdlObject, isNonSchemaOperation, isNonDdlMaintenanceStatement, shouldReviewUnknownIdempotentDdl } from './chunk-HWR5NUUZ.js';
|
|
10
10
|
import './chunk-UHDAYPHH.js';
|
|
11
|
-
import './chunk-
|
|
11
|
+
import './chunk-EZ46JIEO.js';
|
|
12
12
|
import { loadEnvFiles } from './chunk-IWVXI5O4.js';
|
|
13
|
-
import './chunk-
|
|
13
|
+
import './chunk-IR7SA2ME.js';
|
|
14
14
|
import { diagnoseSupabaseStart } from './chunk-AAIE4F2U.js';
|
|
15
15
|
import { validateUserFilePath, filterSafePaths, resolveSafePath } from './chunk-B7C7CLW2.js';
|
|
16
16
|
import { runMachine } from './chunk-QDF7QXBL.js';
|
|
@@ -1848,6 +1848,10 @@ async function collectSchemaRisks(sqlDir, sqlFiles) {
|
|
|
1848
1848
|
}
|
|
1849
1849
|
return allRisks;
|
|
1850
1850
|
}
|
|
1851
|
+
function filterAllowlistedSchemaRisks(risks) {
|
|
1852
|
+
const policy = getBoundaryPolicy();
|
|
1853
|
+
return risks.filter((risk) => !findDeclarativeRiskAllowlistMatch(risk, policy));
|
|
1854
|
+
}
|
|
1851
1855
|
function summarizeRisks(risks) {
|
|
1852
1856
|
const summaries = /* @__PURE__ */ new Map();
|
|
1853
1857
|
for (const risk of risks) {
|
|
@@ -1966,7 +1970,7 @@ async function runSqlSchemaRiskCheck(result, logger4, step) {
|
|
|
1966
1970
|
const sqlFiles = getDeclarativeSqlFiles(sqlDir, logger4);
|
|
1967
1971
|
if (!sqlFiles) return;
|
|
1968
1972
|
try {
|
|
1969
|
-
const allRisks = await collectSchemaRisks(sqlDir, sqlFiles);
|
|
1973
|
+
const allRisks = filterAllowlistedSchemaRisks(await collectSchemaRisks(sqlDir, sqlFiles));
|
|
1970
1974
|
if (allRisks.length === 0) {
|
|
1971
1975
|
logger4.success(`Scanned ${sqlFiles.length} SQL file(s) - no violations`);
|
|
1972
1976
|
return;
|
|
@@ -2653,18 +2657,326 @@ async function runDeclarativeDependencyCheck(result, logger4, step, strictOption
|
|
|
2653
2657
|
|
|
2654
2658
|
// src/commands/db/utils/preflight-checks/duplicate-function-ownership-checks.ts
|
|
2655
2659
|
init_esm_shims();
|
|
2660
|
+
|
|
2661
|
+
// src/commands/db/utils/duplicate-function-ownership-allowlist.ts
|
|
2662
|
+
init_esm_shims();
|
|
2663
|
+
|
|
2664
|
+
// src/commands/db/sync/schema-guardrail-config.ts
|
|
2665
|
+
init_esm_shims();
|
|
2666
|
+
|
|
2667
|
+
// src/commands/db/sync/schema-guardrail-config-test-support.ts
|
|
2668
|
+
init_esm_shims();
|
|
2669
|
+
function extractFirstStringMatch(content, fieldName) {
|
|
2670
|
+
const match = content.match(new RegExp(`${fieldName}\\s*:\\s*['"]([^'"]+)['"]`));
|
|
2671
|
+
return match?.[1];
|
|
2672
|
+
}
|
|
2673
|
+
function extractFirstNumberMatch(content, fieldName) {
|
|
2674
|
+
const match = content.match(new RegExp(`${fieldName}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`));
|
|
2675
|
+
if (!match?.[1]) {
|
|
2676
|
+
return void 0;
|
|
2677
|
+
}
|
|
2678
|
+
const parsed = Number(match[1]);
|
|
2679
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2680
|
+
}
|
|
2681
|
+
function extractStringArrayMatch(content, fieldName) {
|
|
2682
|
+
const match = content.match(new RegExp(`${fieldName}\\s*:\\s*\\[([\\s\\S]*?)\\]`));
|
|
2683
|
+
if (!match?.[1]) {
|
|
2684
|
+
return [];
|
|
2685
|
+
}
|
|
2686
|
+
return Array.from(match[1].matchAll(/['"]([^'"]+)['"]/g), (entry) => entry[1] ?? "").filter(
|
|
2687
|
+
(value) => value.length > 0
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2690
|
+
function extractNamedObjectBlock(content, fieldName) {
|
|
2691
|
+
const nameMatch = new RegExp(`${fieldName}\\s*:\\s*\\{`).exec(content);
|
|
2692
|
+
if (!nameMatch) {
|
|
2693
|
+
return null;
|
|
2694
|
+
}
|
|
2695
|
+
const startIndex = nameMatch.index + nameMatch[0].length - 1;
|
|
2696
|
+
let depth = 0;
|
|
2697
|
+
for (let index = startIndex; index < content.length; index += 1) {
|
|
2698
|
+
const current = content[index];
|
|
2699
|
+
if (current === "{") {
|
|
2700
|
+
depth += 1;
|
|
2701
|
+
} else if (current === "}") {
|
|
2702
|
+
depth -= 1;
|
|
2703
|
+
if (depth === 0) {
|
|
2704
|
+
return content.slice(startIndex + 1, index);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return null;
|
|
2709
|
+
}
|
|
2710
|
+
function extractAllowedDuplicateFunctions(content, normalizers) {
|
|
2711
|
+
const match = content.match(/allowedDuplicateFunctions\s*:\s*\[([\s\S]*?)\]/);
|
|
2712
|
+
if (!match?.[1]) {
|
|
2713
|
+
return [];
|
|
2714
|
+
}
|
|
2715
|
+
const entries = [];
|
|
2716
|
+
for (const objectMatch of match[1].matchAll(/\{([\s\S]*?)\}/g)) {
|
|
2717
|
+
const objectBody = objectMatch[1] ?? "";
|
|
2718
|
+
const qualifiedName = extractFirstStringMatch(objectBody, "qualifiedName");
|
|
2719
|
+
const signature = extractFirstStringMatch(objectBody, "signature");
|
|
2720
|
+
const reason = extractFirstStringMatch(objectBody, "reason");
|
|
2721
|
+
if (!qualifiedName || !signature || !reason) {
|
|
2722
|
+
continue;
|
|
2723
|
+
}
|
|
2724
|
+
entries.push({
|
|
2725
|
+
qualifiedName: normalizers.normalizeFunctionQualifiedName(qualifiedName),
|
|
2726
|
+
signature: normalizers.normalizeAllowlistSignature(signature),
|
|
2727
|
+
reason,
|
|
2728
|
+
declarativeFile: extractFirstStringMatch(objectBody, "declarativeFile"),
|
|
2729
|
+
idempotentFile: extractFirstStringMatch(objectBody, "idempotentFile"),
|
|
2730
|
+
expectedBodyHash: extractFirstStringMatch(objectBody, "expectedBodyHash")
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
return entries;
|
|
2734
|
+
}
|
|
2735
|
+
function tryLoadSchemaGuardrailConfigFromText(params) {
|
|
2736
|
+
const configPath = findRunaConfig(params.targetDir);
|
|
2737
|
+
if (!configPath || !existsSync(configPath)) {
|
|
2738
|
+
return null;
|
|
2739
|
+
}
|
|
2740
|
+
try {
|
|
2741
|
+
const content = readFileSync(configPath, "utf-8");
|
|
2742
|
+
const schemaGuardrailsBlock = extractNamedObjectBlock(content, "schemaGuardrails") ?? "";
|
|
2743
|
+
const pgSchemaDiffBlock = extractNamedObjectBlock(content, "pgSchemaDiff") ?? "";
|
|
2744
|
+
return {
|
|
2745
|
+
...params.defaults,
|
|
2746
|
+
declarativeSqlDir: extractFirstStringMatch(schemaGuardrailsBlock, "declarativeSqlDir") ?? params.defaults.declarativeSqlDir,
|
|
2747
|
+
allowedDuplicateFunctions: extractAllowedDuplicateFunctions(
|
|
2748
|
+
schemaGuardrailsBlock,
|
|
2749
|
+
params.normalizers
|
|
2750
|
+
),
|
|
2751
|
+
generatedHeaderRewriteTargets: params.normalizers.normalizeFileList(
|
|
2752
|
+
extractStringArrayMatch(schemaGuardrailsBlock, "generatedHeaderRewriteTargets").map(
|
|
2753
|
+
(value) => params.normalizers.normalizePathForMatch(value)
|
|
2754
|
+
)
|
|
2755
|
+
),
|
|
2756
|
+
semanticWarnings: {
|
|
2757
|
+
threshold: extractFirstNumberMatch(schemaGuardrailsBlock, "threshold") ?? params.defaults.semanticWarnings.threshold,
|
|
2758
|
+
maxCandidates: extractFirstNumberMatch(schemaGuardrailsBlock, "maxCandidates") ?? params.defaults.semanticWarnings.maxCandidates,
|
|
2759
|
+
ignorePairs: new Set(
|
|
2760
|
+
extractStringArrayMatch(schemaGuardrailsBlock, "ignorePairs").map(
|
|
2761
|
+
(value) => params.normalizers.normalizeSuppressionPair(value)
|
|
2762
|
+
)
|
|
2763
|
+
)
|
|
2764
|
+
},
|
|
2765
|
+
tableHeaderMaxWidth: extractFirstNumberMatch(schemaGuardrailsBlock, "tableHeaderMaxWidth") ?? params.defaults.tableHeaderMaxWidth,
|
|
2766
|
+
excludeFromOrphanDetection: extractStringArrayMatch(
|
|
2767
|
+
pgSchemaDiffBlock,
|
|
2768
|
+
"excludeFromOrphanDetection"
|
|
2769
|
+
),
|
|
2770
|
+
idempotentSqlDir: extractFirstStringMatch(pgSchemaDiffBlock, "idempotentSqlDir") ?? params.defaults.idempotentSqlDir
|
|
2771
|
+
};
|
|
2772
|
+
} catch {
|
|
2773
|
+
return null;
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
// src/commands/db/sync/schema-guardrail-config.ts
|
|
2778
|
+
var DEFAULT_TABLE_HEADER_MAX_WIDTH = 160;
|
|
2779
|
+
var DEFAULT_SEMANTIC_WARNING_THRESHOLD = 0.55;
|
|
2780
|
+
var DEFAULT_SEMANTIC_WARNING_MAX_CANDIDATES = 3;
|
|
2781
|
+
var GENERIC_SIMILARITY_COLUMNS = /* @__PURE__ */ new Set([
|
|
2782
|
+
"id",
|
|
2783
|
+
"created_at",
|
|
2784
|
+
"updated_at",
|
|
2785
|
+
"deleted_at",
|
|
2786
|
+
"scope_id"
|
|
2787
|
+
]);
|
|
2788
|
+
function normalizePathForMatch(filePath) {
|
|
2789
|
+
return filePath.replaceAll("\\", "/");
|
|
2790
|
+
}
|
|
2791
|
+
function normalizeFunctionQualifiedName(value) {
|
|
2792
|
+
return value.trim().toLowerCase();
|
|
2793
|
+
}
|
|
2794
|
+
function normalizeAllowlistSignature(value) {
|
|
2795
|
+
return value.replace(/\s+/g, " ").trim();
|
|
2796
|
+
}
|
|
2797
|
+
function normalizeSuppressionPair(value) {
|
|
2798
|
+
return value.split("::").map((part) => part.trim().toLowerCase()).filter((part) => part.length > 0).sort((left, right) => left.localeCompare(right)).join("::");
|
|
2799
|
+
}
|
|
2800
|
+
function normalizeFileList(files) {
|
|
2801
|
+
return [...new Set(files)].sort((a, b) => a.localeCompare(b));
|
|
2802
|
+
}
|
|
2803
|
+
function isSchemaGuardrailTextFallbackAllowed() {
|
|
2804
|
+
return Boolean(process.env.VITEST);
|
|
2805
|
+
}
|
|
2806
|
+
function createDefaultSchemaGuardrailConfig() {
|
|
2807
|
+
return {
|
|
2808
|
+
declarativeSqlDir: "supabase/schemas/declarative",
|
|
2809
|
+
allowedDuplicateFunctions: [],
|
|
2810
|
+
generatedHeaderRewriteTargets: [],
|
|
2811
|
+
semanticWarnings: {
|
|
2812
|
+
threshold: DEFAULT_SEMANTIC_WARNING_THRESHOLD,
|
|
2813
|
+
maxCandidates: DEFAULT_SEMANTIC_WARNING_MAX_CANDIDATES,
|
|
2814
|
+
ignorePairs: /* @__PURE__ */ new Set()
|
|
2815
|
+
},
|
|
2816
|
+
tableHeaderMaxWidth: DEFAULT_TABLE_HEADER_MAX_WIDTH,
|
|
2817
|
+
excludeFromOrphanDetection: [],
|
|
2818
|
+
idempotentSqlDir: "supabase/schemas/idempotent"
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
function loadAllowedDuplicatesFromSchemaOwnership(targetDir) {
|
|
2822
|
+
const candidates = [
|
|
2823
|
+
join(targetDir, "supabase", "schemas", "schema-ownership.json"),
|
|
2824
|
+
join(targetDir, "schema-ownership.json")
|
|
2825
|
+
];
|
|
2826
|
+
for (const filePath of candidates) {
|
|
2827
|
+
if (!existsSync(filePath)) continue;
|
|
2828
|
+
try {
|
|
2829
|
+
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
2830
|
+
const allowedDuplicates = raw?.rules?.allowed_duplicates;
|
|
2831
|
+
if (!Array.isArray(allowedDuplicates)) continue;
|
|
2832
|
+
return allowedDuplicates.filter(
|
|
2833
|
+
(entry) => typeof entry === "object" && entry !== null && "qualifiedName" in entry && typeof entry.qualifiedName === "string"
|
|
2834
|
+
).map((entry) => ({
|
|
2835
|
+
qualifiedName: normalizeFunctionQualifiedName(entry.qualifiedName),
|
|
2836
|
+
signature: normalizeAllowlistSignature(entry.signature ?? ""),
|
|
2837
|
+
reason: entry.reason ?? "Loaded from schema-ownership.json"
|
|
2838
|
+
}));
|
|
2839
|
+
} catch {
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
return [];
|
|
2843
|
+
}
|
|
2844
|
+
function loadSchemaGuardrailConfig(targetDir) {
|
|
2845
|
+
const defaults = createDefaultSchemaGuardrailConfig();
|
|
2846
|
+
try {
|
|
2847
|
+
const config = loadRunaConfig(targetDir);
|
|
2848
|
+
const databaseConfig = config.database ?? {};
|
|
2849
|
+
return {
|
|
2850
|
+
declarativeSqlDir: databaseConfig.schemaGuardrails?.declarativeSqlDir ?? defaults.declarativeSqlDir,
|
|
2851
|
+
allowedDuplicateFunctions: [
|
|
2852
|
+
...databaseConfig.schemaGuardrails?.allowedDuplicateFunctions?.map((entry) => ({
|
|
2853
|
+
...entry,
|
|
2854
|
+
qualifiedName: normalizeFunctionQualifiedName(entry.qualifiedName),
|
|
2855
|
+
signature: normalizeAllowlistSignature(entry.signature),
|
|
2856
|
+
declarativeFile: entry.declarativeFile ? normalizePathForMatch(entry.declarativeFile) : void 0,
|
|
2857
|
+
idempotentFile: entry.idempotentFile ? normalizePathForMatch(entry.idempotentFile) : void 0
|
|
2858
|
+
})) ?? [],
|
|
2859
|
+
// Fallback: merge schema-ownership.json allowed_duplicates if present
|
|
2860
|
+
...loadAllowedDuplicatesFromSchemaOwnership(targetDir)
|
|
2861
|
+
],
|
|
2862
|
+
generatedHeaderRewriteTargets: normalizeFileList(
|
|
2863
|
+
(databaseConfig.schemaGuardrails?.generatedHeaderRewriteTargets ?? []).map(
|
|
2864
|
+
(value) => normalizePathForMatch(value)
|
|
2865
|
+
)
|
|
2866
|
+
),
|
|
2867
|
+
semanticWarnings: {
|
|
2868
|
+
threshold: databaseConfig.schemaGuardrails?.semanticWarnings?.threshold ?? defaults.semanticWarnings.threshold,
|
|
2869
|
+
maxCandidates: databaseConfig.schemaGuardrails?.semanticWarnings?.maxCandidates ?? defaults.semanticWarnings.maxCandidates,
|
|
2870
|
+
ignorePairs: new Set(
|
|
2871
|
+
(databaseConfig.schemaGuardrails?.semanticWarnings?.ignorePairs ?? []).map(
|
|
2872
|
+
(value) => normalizeSuppressionPair(value)
|
|
2873
|
+
)
|
|
2874
|
+
)
|
|
2875
|
+
},
|
|
2876
|
+
tableHeaderMaxWidth: databaseConfig.schemaGuardrails?.tableHeaderMaxWidth ?? defaults.tableHeaderMaxWidth,
|
|
2877
|
+
excludeFromOrphanDetection: databaseConfig.pgSchemaDiff?.excludeFromOrphanDetection ?? [],
|
|
2878
|
+
idempotentSqlDir: databaseConfig.pgSchemaDiff?.idempotentSqlDir ?? defaults.idempotentSqlDir
|
|
2879
|
+
};
|
|
2880
|
+
} catch (error) {
|
|
2881
|
+
if (isSchemaGuardrailTextFallbackAllowed()) {
|
|
2882
|
+
return tryLoadSchemaGuardrailConfigFromText({
|
|
2883
|
+
targetDir,
|
|
2884
|
+
defaults,
|
|
2885
|
+
normalizers: {
|
|
2886
|
+
normalizeAllowlistSignature,
|
|
2887
|
+
normalizeFileList,
|
|
2888
|
+
normalizeFunctionQualifiedName,
|
|
2889
|
+
normalizePathForMatch,
|
|
2890
|
+
normalizeSuppressionPair
|
|
2891
|
+
}
|
|
2892
|
+
}) ?? defaults;
|
|
2893
|
+
}
|
|
2894
|
+
throw error;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
// src/commands/db/utils/duplicate-function-ownership-allowlist.ts
|
|
2899
|
+
function matchesDuplicateFunctionSignature(params) {
|
|
2900
|
+
return normalizeFunctionQualifiedName(params.entry.qualifiedName) === normalizeFunctionQualifiedName(params.finding.qualifiedName) && normalizeAllowlistSignature(params.entry.signature) === normalizeAllowlistSignature(params.finding.signature ?? "");
|
|
2901
|
+
}
|
|
2902
|
+
function matchesOptionalFilePath(params) {
|
|
2903
|
+
if (!params.configuredPath) {
|
|
2904
|
+
return true;
|
|
2905
|
+
}
|
|
2906
|
+
return params.definitionFiles.some(
|
|
2907
|
+
(filePath) => normalizePathForMatch(filePath) === params.configuredPath
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
function matchesExpectedBodyHash(params) {
|
|
2911
|
+
if (!params.entry.expectedBodyHash || !params.bodyHashes) {
|
|
2912
|
+
return true;
|
|
2913
|
+
}
|
|
2914
|
+
const definitions = [
|
|
2915
|
+
...params.finding.declarativeDefinitions,
|
|
2916
|
+
...params.finding.idempotentDefinitions
|
|
2917
|
+
];
|
|
2918
|
+
if (definitions.length === 0) {
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
return definitions.every((definition) => {
|
|
2922
|
+
const key = `${definition.layer}:${definition.file}:${definition.line}`;
|
|
2923
|
+
return params.bodyHashes?.get(key) === params.entry.expectedBodyHash;
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
function isAllowlistedDuplicateFunction(params) {
|
|
2927
|
+
const { finding, allowlist, bodyHashes } = params;
|
|
2928
|
+
if (!finding.signature) return false;
|
|
2929
|
+
return allowlist.some((entry) => {
|
|
2930
|
+
if (!matchesDuplicateFunctionSignature({ entry, finding })) {
|
|
2931
|
+
return false;
|
|
2932
|
+
}
|
|
2933
|
+
if (!matchesOptionalFilePath({
|
|
2934
|
+
configuredPath: entry.declarativeFile,
|
|
2935
|
+
definitionFiles: finding.declarativeDefinitions.map((definition) => definition.file)
|
|
2936
|
+
})) {
|
|
2937
|
+
return false;
|
|
2938
|
+
}
|
|
2939
|
+
if (!matchesOptionalFilePath({
|
|
2940
|
+
configuredPath: entry.idempotentFile,
|
|
2941
|
+
definitionFiles: finding.idempotentDefinitions.map((definition) => definition.file)
|
|
2942
|
+
})) {
|
|
2943
|
+
return false;
|
|
2944
|
+
}
|
|
2945
|
+
return matchesExpectedBodyHash({ entry, finding, bodyHashes });
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
function filterAllowlistedDuplicateFunctions(params) {
|
|
2949
|
+
return params.findings.filter(
|
|
2950
|
+
(finding) => !isAllowlistedDuplicateFunction({
|
|
2951
|
+
finding,
|
|
2952
|
+
allowlist: params.allowlist,
|
|
2953
|
+
bodyHashes: params.bodyHashes
|
|
2954
|
+
})
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
// src/commands/db/utils/preflight-checks/duplicate-function-ownership-checks.ts
|
|
2656
2959
|
async function runDuplicateFunctionOwnershipCheck(result, logger4, step) {
|
|
2657
2960
|
logger4.step("Checking duplicate function ownership", step.next());
|
|
2658
2961
|
const analysis = analyzeDuplicateFunctionOwnership(process.cwd());
|
|
2659
|
-
|
|
2962
|
+
let findings = analysis.findings;
|
|
2963
|
+
try {
|
|
2964
|
+
const allowlist = loadSchemaGuardrailConfig(process.cwd()).allowedDuplicateFunctions;
|
|
2965
|
+
findings = filterAllowlistedDuplicateFunctions({
|
|
2966
|
+
findings,
|
|
2967
|
+
allowlist
|
|
2968
|
+
});
|
|
2969
|
+
} catch {
|
|
2970
|
+
}
|
|
2971
|
+
if (findings.length === 0) {
|
|
2660
2972
|
logger4.success("No duplicate declarative/idempotent function ownership detected");
|
|
2661
2973
|
return;
|
|
2662
2974
|
}
|
|
2663
2975
|
result.passed = false;
|
|
2664
|
-
result.errors.push(`Found ${
|
|
2665
|
-
logger4.error(`Found ${
|
|
2976
|
+
result.errors.push(`Found ${findings.length} duplicate function ownership finding(s)`);
|
|
2977
|
+
logger4.error(`Found ${findings.length} duplicate function ownership finding(s):`);
|
|
2666
2978
|
logger4.info(` ${analysis.contractNote}`);
|
|
2667
|
-
for (const finding of
|
|
2979
|
+
for (const finding of findings) {
|
|
2668
2980
|
const formatted = formatDuplicateFunctionOwnershipFinding(finding);
|
|
2669
2981
|
logger4.info(` [ERROR] ${formatted.summary}`);
|
|
2670
2982
|
for (const location of formatted.declarativeLocations) {
|
|
@@ -3884,7 +4196,7 @@ init_esm_shims();
|
|
|
3884
4196
|
var riskDetectorLoader = null;
|
|
3885
4197
|
function loadRiskDetectorModule() {
|
|
3886
4198
|
if (!riskDetectorLoader) {
|
|
3887
|
-
riskDetectorLoader = import('./risk-detector-
|
|
4199
|
+
riskDetectorLoader = import('./risk-detector-GDDLISVE.js').then((module) => ({
|
|
3888
4200
|
detectSchemaRisks: module.detectSchemaRisks
|
|
3889
4201
|
})).catch((error) => {
|
|
3890
4202
|
riskDetectorLoader = null;
|
|
@@ -5124,240 +5436,6 @@ init_esm_shims();
|
|
|
5124
5436
|
// src/commands/db/sync/schema-guardrail.ts
|
|
5125
5437
|
init_esm_shims();
|
|
5126
5438
|
|
|
5127
|
-
// src/commands/db/sync/schema-guardrail-config.ts
|
|
5128
|
-
init_esm_shims();
|
|
5129
|
-
|
|
5130
|
-
// src/commands/db/sync/schema-guardrail-config-test-support.ts
|
|
5131
|
-
init_esm_shims();
|
|
5132
|
-
function extractFirstStringMatch(content, fieldName) {
|
|
5133
|
-
const match = content.match(new RegExp(`${fieldName}\\s*:\\s*['"]([^'"]+)['"]`));
|
|
5134
|
-
return match?.[1];
|
|
5135
|
-
}
|
|
5136
|
-
function extractFirstNumberMatch(content, fieldName) {
|
|
5137
|
-
const match = content.match(new RegExp(`${fieldName}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`));
|
|
5138
|
-
if (!match?.[1]) {
|
|
5139
|
-
return void 0;
|
|
5140
|
-
}
|
|
5141
|
-
const parsed = Number(match[1]);
|
|
5142
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
5143
|
-
}
|
|
5144
|
-
function extractStringArrayMatch(content, fieldName) {
|
|
5145
|
-
const match = content.match(new RegExp(`${fieldName}\\s*:\\s*\\[([\\s\\S]*?)\\]`));
|
|
5146
|
-
if (!match?.[1]) {
|
|
5147
|
-
return [];
|
|
5148
|
-
}
|
|
5149
|
-
return Array.from(match[1].matchAll(/['"]([^'"]+)['"]/g), (entry) => entry[1] ?? "").filter(
|
|
5150
|
-
(value) => value.length > 0
|
|
5151
|
-
);
|
|
5152
|
-
}
|
|
5153
|
-
function extractNamedObjectBlock(content, fieldName) {
|
|
5154
|
-
const nameMatch = new RegExp(`${fieldName}\\s*:\\s*\\{`).exec(content);
|
|
5155
|
-
if (!nameMatch) {
|
|
5156
|
-
return null;
|
|
5157
|
-
}
|
|
5158
|
-
const startIndex = nameMatch.index + nameMatch[0].length - 1;
|
|
5159
|
-
let depth = 0;
|
|
5160
|
-
for (let index = startIndex; index < content.length; index += 1) {
|
|
5161
|
-
const current = content[index];
|
|
5162
|
-
if (current === "{") {
|
|
5163
|
-
depth += 1;
|
|
5164
|
-
} else if (current === "}") {
|
|
5165
|
-
depth -= 1;
|
|
5166
|
-
if (depth === 0) {
|
|
5167
|
-
return content.slice(startIndex + 1, index);
|
|
5168
|
-
}
|
|
5169
|
-
}
|
|
5170
|
-
}
|
|
5171
|
-
return null;
|
|
5172
|
-
}
|
|
5173
|
-
function extractAllowedDuplicateFunctions(content, normalizers) {
|
|
5174
|
-
const match = content.match(/allowedDuplicateFunctions\s*:\s*\[([\s\S]*?)\]/);
|
|
5175
|
-
if (!match?.[1]) {
|
|
5176
|
-
return [];
|
|
5177
|
-
}
|
|
5178
|
-
const entries = [];
|
|
5179
|
-
for (const objectMatch of match[1].matchAll(/\{([\s\S]*?)\}/g)) {
|
|
5180
|
-
const objectBody = objectMatch[1] ?? "";
|
|
5181
|
-
const qualifiedName = extractFirstStringMatch(objectBody, "qualifiedName");
|
|
5182
|
-
const signature = extractFirstStringMatch(objectBody, "signature");
|
|
5183
|
-
const reason = extractFirstStringMatch(objectBody, "reason");
|
|
5184
|
-
if (!qualifiedName || !signature || !reason) {
|
|
5185
|
-
continue;
|
|
5186
|
-
}
|
|
5187
|
-
entries.push({
|
|
5188
|
-
qualifiedName: normalizers.normalizeFunctionQualifiedName(qualifiedName),
|
|
5189
|
-
signature: normalizers.normalizeAllowlistSignature(signature),
|
|
5190
|
-
reason,
|
|
5191
|
-
declarativeFile: extractFirstStringMatch(objectBody, "declarativeFile"),
|
|
5192
|
-
idempotentFile: extractFirstStringMatch(objectBody, "idempotentFile"),
|
|
5193
|
-
expectedBodyHash: extractFirstStringMatch(objectBody, "expectedBodyHash")
|
|
5194
|
-
});
|
|
5195
|
-
}
|
|
5196
|
-
return entries;
|
|
5197
|
-
}
|
|
5198
|
-
function tryLoadSchemaGuardrailConfigFromText(params) {
|
|
5199
|
-
const configPath = findRunaConfig(params.targetDir);
|
|
5200
|
-
if (!configPath || !existsSync(configPath)) {
|
|
5201
|
-
return null;
|
|
5202
|
-
}
|
|
5203
|
-
try {
|
|
5204
|
-
const content = readFileSync(configPath, "utf-8");
|
|
5205
|
-
const schemaGuardrailsBlock = extractNamedObjectBlock(content, "schemaGuardrails") ?? "";
|
|
5206
|
-
const pgSchemaDiffBlock = extractNamedObjectBlock(content, "pgSchemaDiff") ?? "";
|
|
5207
|
-
return {
|
|
5208
|
-
...params.defaults,
|
|
5209
|
-
declarativeSqlDir: extractFirstStringMatch(schemaGuardrailsBlock, "declarativeSqlDir") ?? params.defaults.declarativeSqlDir,
|
|
5210
|
-
allowedDuplicateFunctions: extractAllowedDuplicateFunctions(
|
|
5211
|
-
schemaGuardrailsBlock,
|
|
5212
|
-
params.normalizers
|
|
5213
|
-
),
|
|
5214
|
-
generatedHeaderRewriteTargets: params.normalizers.normalizeFileList(
|
|
5215
|
-
extractStringArrayMatch(schemaGuardrailsBlock, "generatedHeaderRewriteTargets").map(
|
|
5216
|
-
(value) => params.normalizers.normalizePathForMatch(value)
|
|
5217
|
-
)
|
|
5218
|
-
),
|
|
5219
|
-
semanticWarnings: {
|
|
5220
|
-
threshold: extractFirstNumberMatch(schemaGuardrailsBlock, "threshold") ?? params.defaults.semanticWarnings.threshold,
|
|
5221
|
-
maxCandidates: extractFirstNumberMatch(schemaGuardrailsBlock, "maxCandidates") ?? params.defaults.semanticWarnings.maxCandidates,
|
|
5222
|
-
ignorePairs: new Set(
|
|
5223
|
-
extractStringArrayMatch(schemaGuardrailsBlock, "ignorePairs").map(
|
|
5224
|
-
(value) => params.normalizers.normalizeSuppressionPair(value)
|
|
5225
|
-
)
|
|
5226
|
-
)
|
|
5227
|
-
},
|
|
5228
|
-
tableHeaderMaxWidth: extractFirstNumberMatch(schemaGuardrailsBlock, "tableHeaderMaxWidth") ?? params.defaults.tableHeaderMaxWidth,
|
|
5229
|
-
excludeFromOrphanDetection: extractStringArrayMatch(
|
|
5230
|
-
pgSchemaDiffBlock,
|
|
5231
|
-
"excludeFromOrphanDetection"
|
|
5232
|
-
),
|
|
5233
|
-
idempotentSqlDir: extractFirstStringMatch(pgSchemaDiffBlock, "idempotentSqlDir") ?? params.defaults.idempotentSqlDir
|
|
5234
|
-
};
|
|
5235
|
-
} catch {
|
|
5236
|
-
return null;
|
|
5237
|
-
}
|
|
5238
|
-
}
|
|
5239
|
-
|
|
5240
|
-
// src/commands/db/sync/schema-guardrail-config.ts
|
|
5241
|
-
var DEFAULT_TABLE_HEADER_MAX_WIDTH = 160;
|
|
5242
|
-
var DEFAULT_SEMANTIC_WARNING_THRESHOLD = 0.55;
|
|
5243
|
-
var DEFAULT_SEMANTIC_WARNING_MAX_CANDIDATES = 3;
|
|
5244
|
-
var GENERIC_SIMILARITY_COLUMNS = /* @__PURE__ */ new Set([
|
|
5245
|
-
"id",
|
|
5246
|
-
"created_at",
|
|
5247
|
-
"updated_at",
|
|
5248
|
-
"deleted_at",
|
|
5249
|
-
"scope_id"
|
|
5250
|
-
]);
|
|
5251
|
-
function normalizePathForMatch(filePath) {
|
|
5252
|
-
return filePath.replaceAll("\\", "/");
|
|
5253
|
-
}
|
|
5254
|
-
function normalizeFunctionQualifiedName(value) {
|
|
5255
|
-
return value.trim().toLowerCase();
|
|
5256
|
-
}
|
|
5257
|
-
function normalizeAllowlistSignature(value) {
|
|
5258
|
-
return value.replace(/\s+/g, " ").trim();
|
|
5259
|
-
}
|
|
5260
|
-
function normalizeSuppressionPair(value) {
|
|
5261
|
-
return value.split("::").map((part) => part.trim().toLowerCase()).filter((part) => part.length > 0).sort((left, right) => left.localeCompare(right)).join("::");
|
|
5262
|
-
}
|
|
5263
|
-
function normalizeFileList(files) {
|
|
5264
|
-
return [...new Set(files)].sort((a, b) => a.localeCompare(b));
|
|
5265
|
-
}
|
|
5266
|
-
function isSchemaGuardrailTextFallbackAllowed() {
|
|
5267
|
-
return Boolean(process.env.VITEST);
|
|
5268
|
-
}
|
|
5269
|
-
function createDefaultSchemaGuardrailConfig() {
|
|
5270
|
-
return {
|
|
5271
|
-
declarativeSqlDir: "supabase/schemas/declarative",
|
|
5272
|
-
allowedDuplicateFunctions: [],
|
|
5273
|
-
generatedHeaderRewriteTargets: [],
|
|
5274
|
-
semanticWarnings: {
|
|
5275
|
-
threshold: DEFAULT_SEMANTIC_WARNING_THRESHOLD,
|
|
5276
|
-
maxCandidates: DEFAULT_SEMANTIC_WARNING_MAX_CANDIDATES,
|
|
5277
|
-
ignorePairs: /* @__PURE__ */ new Set()
|
|
5278
|
-
},
|
|
5279
|
-
tableHeaderMaxWidth: DEFAULT_TABLE_HEADER_MAX_WIDTH,
|
|
5280
|
-
excludeFromOrphanDetection: [],
|
|
5281
|
-
idempotentSqlDir: "supabase/schemas/idempotent"
|
|
5282
|
-
};
|
|
5283
|
-
}
|
|
5284
|
-
function loadAllowedDuplicatesFromSchemaOwnership(targetDir) {
|
|
5285
|
-
const candidates = [
|
|
5286
|
-
join(targetDir, "supabase", "schemas", "schema-ownership.json"),
|
|
5287
|
-
join(targetDir, "schema-ownership.json")
|
|
5288
|
-
];
|
|
5289
|
-
for (const filePath of candidates) {
|
|
5290
|
-
if (!existsSync(filePath)) continue;
|
|
5291
|
-
try {
|
|
5292
|
-
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
5293
|
-
const allowedDuplicates = raw?.rules?.allowed_duplicates;
|
|
5294
|
-
if (!Array.isArray(allowedDuplicates)) continue;
|
|
5295
|
-
return allowedDuplicates.filter(
|
|
5296
|
-
(entry) => typeof entry === "object" && entry !== null && "qualifiedName" in entry && typeof entry.qualifiedName === "string"
|
|
5297
|
-
).map((entry) => ({
|
|
5298
|
-
qualifiedName: normalizeFunctionQualifiedName(entry.qualifiedName),
|
|
5299
|
-
signature: normalizeAllowlistSignature(entry.signature ?? ""),
|
|
5300
|
-
reason: entry.reason ?? "Loaded from schema-ownership.json"
|
|
5301
|
-
}));
|
|
5302
|
-
} catch {
|
|
5303
|
-
}
|
|
5304
|
-
}
|
|
5305
|
-
return [];
|
|
5306
|
-
}
|
|
5307
|
-
function loadSchemaGuardrailConfig(targetDir) {
|
|
5308
|
-
const defaults = createDefaultSchemaGuardrailConfig();
|
|
5309
|
-
try {
|
|
5310
|
-
const config = loadRunaConfig(targetDir);
|
|
5311
|
-
const databaseConfig = config.database ?? {};
|
|
5312
|
-
return {
|
|
5313
|
-
declarativeSqlDir: databaseConfig.schemaGuardrails?.declarativeSqlDir ?? defaults.declarativeSqlDir,
|
|
5314
|
-
allowedDuplicateFunctions: [
|
|
5315
|
-
...databaseConfig.schemaGuardrails?.allowedDuplicateFunctions?.map((entry) => ({
|
|
5316
|
-
...entry,
|
|
5317
|
-
qualifiedName: normalizeFunctionQualifiedName(entry.qualifiedName),
|
|
5318
|
-
signature: normalizeAllowlistSignature(entry.signature),
|
|
5319
|
-
declarativeFile: entry.declarativeFile ? normalizePathForMatch(entry.declarativeFile) : void 0,
|
|
5320
|
-
idempotentFile: entry.idempotentFile ? normalizePathForMatch(entry.idempotentFile) : void 0
|
|
5321
|
-
})) ?? [],
|
|
5322
|
-
// Fallback: merge schema-ownership.json allowed_duplicates if present
|
|
5323
|
-
...loadAllowedDuplicatesFromSchemaOwnership(targetDir)
|
|
5324
|
-
],
|
|
5325
|
-
generatedHeaderRewriteTargets: normalizeFileList(
|
|
5326
|
-
(databaseConfig.schemaGuardrails?.generatedHeaderRewriteTargets ?? []).map(
|
|
5327
|
-
(value) => normalizePathForMatch(value)
|
|
5328
|
-
)
|
|
5329
|
-
),
|
|
5330
|
-
semanticWarnings: {
|
|
5331
|
-
threshold: databaseConfig.schemaGuardrails?.semanticWarnings?.threshold ?? defaults.semanticWarnings.threshold,
|
|
5332
|
-
maxCandidates: databaseConfig.schemaGuardrails?.semanticWarnings?.maxCandidates ?? defaults.semanticWarnings.maxCandidates,
|
|
5333
|
-
ignorePairs: new Set(
|
|
5334
|
-
(databaseConfig.schemaGuardrails?.semanticWarnings?.ignorePairs ?? []).map(
|
|
5335
|
-
(value) => normalizeSuppressionPair(value)
|
|
5336
|
-
)
|
|
5337
|
-
)
|
|
5338
|
-
},
|
|
5339
|
-
tableHeaderMaxWidth: databaseConfig.schemaGuardrails?.tableHeaderMaxWidth ?? defaults.tableHeaderMaxWidth,
|
|
5340
|
-
excludeFromOrphanDetection: databaseConfig.pgSchemaDiff?.excludeFromOrphanDetection ?? [],
|
|
5341
|
-
idempotentSqlDir: databaseConfig.pgSchemaDiff?.idempotentSqlDir ?? defaults.idempotentSqlDir
|
|
5342
|
-
};
|
|
5343
|
-
} catch (error) {
|
|
5344
|
-
if (isSchemaGuardrailTextFallbackAllowed()) {
|
|
5345
|
-
return tryLoadSchemaGuardrailConfigFromText({
|
|
5346
|
-
targetDir,
|
|
5347
|
-
defaults,
|
|
5348
|
-
normalizers: {
|
|
5349
|
-
normalizeAllowlistSignature,
|
|
5350
|
-
normalizeFileList,
|
|
5351
|
-
normalizeFunctionQualifiedName,
|
|
5352
|
-
normalizePathForMatch,
|
|
5353
|
-
normalizeSuppressionPair
|
|
5354
|
-
}
|
|
5355
|
-
}) ?? defaults;
|
|
5356
|
-
}
|
|
5357
|
-
throw error;
|
|
5358
|
-
}
|
|
5359
|
-
}
|
|
5360
|
-
|
|
5361
5439
|
// src/commands/db/sync/schema-guardrail-graph.ts
|
|
5362
5440
|
init_esm_shims();
|
|
5363
5441
|
|
|
@@ -5960,37 +6038,6 @@ function buildFunctionBodyHashMap(files, layer) {
|
|
|
5960
6038
|
}
|
|
5961
6039
|
return hashes;
|
|
5962
6040
|
}
|
|
5963
|
-
function isAllowlistedDuplicateFunction(params) {
|
|
5964
|
-
const { finding, allowlist, bodyHashes } = params;
|
|
5965
|
-
if (!finding.signature) return false;
|
|
5966
|
-
return allowlist.some((entry) => {
|
|
5967
|
-
if (normalizeFunctionQualifiedName(entry.qualifiedName) !== normalizeFunctionQualifiedName(finding.qualifiedName)) {
|
|
5968
|
-
return false;
|
|
5969
|
-
}
|
|
5970
|
-
if (normalizeAllowlistSignature(entry.signature) !== normalizeAllowlistSignature(finding.signature ?? "")) {
|
|
5971
|
-
return false;
|
|
5972
|
-
}
|
|
5973
|
-
if (entry.declarativeFile && !finding.declarativeDefinitions.some(
|
|
5974
|
-
(definition) => normalizePathForMatch(definition.file) === entry.declarativeFile
|
|
5975
|
-
)) {
|
|
5976
|
-
return false;
|
|
5977
|
-
}
|
|
5978
|
-
if (entry.idempotentFile && !finding.idempotentDefinitions.some(
|
|
5979
|
-
(definition) => normalizePathForMatch(definition.file) === entry.idempotentFile
|
|
5980
|
-
)) {
|
|
5981
|
-
return false;
|
|
5982
|
-
}
|
|
5983
|
-
if (!entry.expectedBodyHash) {
|
|
5984
|
-
return true;
|
|
5985
|
-
}
|
|
5986
|
-
const definitions = [...finding.declarativeDefinitions, ...finding.idempotentDefinitions];
|
|
5987
|
-
if (definitions.length === 0) return false;
|
|
5988
|
-
return definitions.every((definition) => {
|
|
5989
|
-
const key = `${definition.layer}:${definition.file}:${definition.line}`;
|
|
5990
|
-
return bodyHashes.get(key) === entry.expectedBodyHash;
|
|
5991
|
-
});
|
|
5992
|
-
});
|
|
5993
|
-
}
|
|
5994
6041
|
function normalizeQualifiedObjectRef(schema, name, isFunctionCall) {
|
|
5995
6042
|
return `${schema}.${name}${isFunctionCall ? "()" : ""}`;
|
|
5996
6043
|
}
|
|
@@ -12069,14 +12116,10 @@ async function collectLocalPrecheckBundle(strict) {
|
|
|
12069
12116
|
guardrailAllowlist = loadSchemaGuardrailConfig(process.cwd()).allowedDuplicateFunctions;
|
|
12070
12117
|
} catch {
|
|
12071
12118
|
}
|
|
12072
|
-
const duplicateOwnershipBlockers =
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
bodyHashes: /* @__PURE__ */ new Map()
|
|
12077
|
-
// Hash check skipped; name + signature matching still works
|
|
12078
|
-
})
|
|
12079
|
-
).map((finding) => {
|
|
12119
|
+
const duplicateOwnershipBlockers = filterAllowlistedDuplicateFunctions({
|
|
12120
|
+
findings: duplicateOwnershipAnalysis.findings,
|
|
12121
|
+
allowlist: guardrailAllowlist
|
|
12122
|
+
}).map((finding) => {
|
|
12080
12123
|
const formatted = formatDuplicateFunctionOwnershipFinding(finding);
|
|
12081
12124
|
return [
|
|
12082
12125
|
formatted.summary,
|