@runa-ai/runa-cli 0.10.1 → 0.10.3

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.
Files changed (37) hide show
  1. package/dist/{chunk-Y5ANTCKE.js → chunk-EZ46JIEO.js} +5 -2
  2. package/dist/{chunk-XRLIZKB2.js → chunk-S7VGVFYF.js} +4934 -4289
  3. package/dist/{chunk-ZPE52NEK.js → chunk-SS7RIWW3.js} +1 -1
  4. package/dist/{chunk-PAWNJA3N.js → chunk-XFXGFUAM.js} +1 -1
  5. package/dist/{ci-3HZWUQFN.js → ci-6P7VK6WB.js} +3 -3
  6. package/dist/{cli-RES5QRC2.js → cli-Q665YRVT.js} +8 -8
  7. package/dist/commands/build/contract.d.ts +2 -2
  8. package/dist/commands/build/machine.d.ts +6 -6
  9. package/dist/commands/ci/commands/ci-prod-types.d.ts +1 -1
  10. package/dist/commands/ci/machine/contract.d.ts +10 -10
  11. package/dist/commands/ci/machine/machine.d.ts +3 -3
  12. package/dist/commands/ci/utils/ci-summary.d.ts +3 -3
  13. package/dist/commands/db/apply/contract.d.ts +1 -1
  14. package/dist/commands/db/apply/helpers/plan-check-filter.d.ts +1 -1
  15. package/dist/commands/db/apply/helpers/planner-artifact.d.ts +1 -1
  16. package/dist/commands/db/commands/db-preview-profile.d.ts +1 -1
  17. package/dist/commands/db/preflight/contract.d.ts +1 -1
  18. package/dist/commands/db/sync/contract.d.ts +5 -5
  19. package/dist/commands/db/sync/machine.d.ts +2 -2
  20. package/dist/commands/db/sync/schema-guardrail-graph-metadata.d.ts +1 -7
  21. package/dist/commands/db/sync/schema-guardrail-graph.d.ts +2 -0
  22. package/dist/commands/db/sync/schema-guardrail-rewrite.d.ts +8 -0
  23. package/dist/commands/db/sync/schema-guardrail-types.d.ts +2 -2
  24. package/dist/commands/db/utils/duplicate-function-ownership-allowlist.d.ts +13 -0
  25. package/dist/commands/db/utils/function-acl-manifest.d.ts +39 -0
  26. package/dist/commands/upgrade.d.ts +36 -0
  27. package/dist/{db-PRGL7PBX.js → db-BQOVOQXU.js} +816 -732
  28. package/dist/index.js +3 -3
  29. package/dist/{risk-detector-S7XQF4I2.js → risk-detector-GDDLISVE.js} +1 -1
  30. package/dist/{risk-detector-core-TGFKWHRS.js → risk-detector-core-YI3M6INI.js} +1 -1
  31. package/dist/{risk-detector-plpgsql-O32TUR34.js → risk-detector-plpgsql-4GWEQXUG.js} +1 -1
  32. package/dist/{template-check-VNNQQXCX.js → template-check-D35F2GDP.js} +4 -0
  33. package/dist/{upgrade-LBO3Z3J7.js → upgrade-X7P6WRD5.js} +189 -19
  34. package/dist/{vuln-check-5JJ2YAJW.js → vuln-check-WW43E7PS.js} +1 -1
  35. package/dist/{vuln-checker-JF5234BL.js → vuln-checker-BC3ZAXJ3.js} +1 -1
  36. package/dist/{watch-RFVCEQLH.js → watch-4RHXVCQ3.js} +1 -1
  37. package/package.json +1 -1
@@ -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-PAWNJA3N.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-XRLIZKB2.js';
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, FUNCTION_ACL_RECONCILIATION_RELATIVE_PATH, functionAclManifestHasEntries, validateFunctionAclMigration, isManagedFunctionAclFileContentStale, renderFunctionAclFile, assessPlanSize, formatPlanSizeSummary, buildFunctionAclManifestFromSqlFiles, buildFunctionAclIdempotentTouchMetadata, stableSorted, normalizePolicyCommand, normalizeFileList, extractFunctionOwnershipDefinition, SECURITY_DEFINER_RE, MANAGED_BOUNDARY_SCHEMAS, currentIsoTimestamp, makeGraphVersion, GENERATOR_VERSION, MAX_SCHEMA_GUIDANCE_TARGETS_PER_FILE, QUALIFIED_SQL_OBJECT_RE, SEARCH_PATH_LOCK_RE, TRIGGER_GUIDANCE_SUPPRESSED_FUNCTIONS, SQL_EXTENSION_IDENTIFIER_PATTERN, SQL_IDENTIFIER_PATTERN } from './chunk-S7VGVFYF.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-Y5ANTCKE.js';
11
+ import './chunk-EZ46JIEO.js';
12
12
  import { loadEnvFiles } from './chunk-IWVXI5O4.js';
13
- import './chunk-ZPE52NEK.js';
13
+ import './chunk-SS7RIWW3.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';
@@ -1145,7 +1145,8 @@ z.object({
1145
1145
  });
1146
1146
  var SchemaManagedBlockKindSchema = z.enum([
1147
1147
  "file-header",
1148
- "table-header"
1148
+ "table-header",
1149
+ "generated-file"
1149
1150
  ]);
1150
1151
  var SchemaGuardrailPhaseIdSchema = z.enum([
1151
1152
  "load_sources",
@@ -1166,6 +1167,7 @@ var SchemaGuardrailFailureCodeSchema = z.enum([
1166
1167
  "dynamic_sql_blocked",
1167
1168
  "extension_placement_blocked",
1168
1169
  "stale_generated_header",
1170
+ "function_acl_migration_required",
1169
1171
  "generated_header_validation_failed",
1170
1172
  "generated_header_rewrite_failed",
1171
1173
  "static_graph_build_failed",
@@ -1509,39 +1511,46 @@ function buildGuardrailConflictEntries(report, entries) {
1509
1511
  pushSemanticEvidenceEntries(entries, report);
1510
1512
  pushBoundaryGuidanceEntries(entries, report);
1511
1513
  }
1512
- function buildGuardrailHeaderEntries(report, entries) {
1513
- if (report.staleBlocks.length > 0) {
1514
- const byFile = /* @__PURE__ */ new Map();
1515
- for (const block of report.staleBlocks) {
1516
- const kinds = byFile.get(block.file) ?? [];
1517
- kinds.push(block.kind === "file-header" ? "file metadata" : `table: ${block.target}`);
1518
- byFile.set(block.file, kinds);
1519
- }
1514
+ function describeStaleBlock(block) {
1515
+ return block.kind === "file-header" ? "file metadata" : block.kind === "generated-file" ? "managed file body" : `table: ${block.target}`;
1516
+ }
1517
+ function pushStaleArtifactEntries(report, entries) {
1518
+ if (report.staleBlocks.length === 0) {
1519
+ return;
1520
+ }
1521
+ const byFile = /* @__PURE__ */ new Map();
1522
+ for (const block of report.staleBlocks) {
1523
+ const kinds = byFile.get(block.file) ?? [];
1524
+ kinds.push(describeStaleBlock(block));
1525
+ byFile.set(block.file, kinds);
1526
+ }
1527
+ entries.push({
1528
+ level: "warn",
1529
+ message: `Generated SQL artifacts are stale (${report.staleBlocks.length} block(s) in ${byFile.size} file(s)):`
1530
+ });
1531
+ for (const [file, kinds] of byFile) {
1520
1532
  entries.push({
1521
- level: "warn",
1522
- message: `Generated headers are stale (${report.staleBlocks.length} block(s) in ${byFile.size} file(s)):`
1533
+ level: "info",
1534
+ message: ` ${file}: ${kinds.join(", ")}`
1535
+ });
1536
+ }
1537
+ if (!report.failure) {
1538
+ entries.push({
1539
+ level: "info",
1540
+ message: "Auto-fix: Run `runa db sync` to regenerate managed SQL artifacts automatically."
1541
+ });
1542
+ entries.push({
1543
+ level: "info",
1544
+ message: "Managed SQL artifacts track: FK references, RLS policies, triggers, function ownership, schema dependencies, and function ACL reconciliation."
1523
1545
  });
1524
- for (const [file, kinds] of byFile) {
1525
- entries.push({
1526
- level: "info",
1527
- message: ` ${file}: ${kinds.join(", ")}`
1528
- });
1529
- }
1530
- if (!report.failure) {
1531
- entries.push({
1532
- level: "info",
1533
- message: "Auto-fix: Run `runa db sync` to regenerate all headers automatically."
1534
- });
1535
- entries.push({
1536
- level: "info",
1537
- message: "Headers track: FK references, RLS policies, triggers, function ownership, and schema dependencies."
1538
- });
1539
- }
1540
1546
  }
1547
+ }
1548
+ function buildGuardrailHeaderEntries(report, entries) {
1549
+ pushStaleArtifactEntries(report, entries);
1541
1550
  if (report.headersRewritten.length > 0) {
1542
1551
  entries.push({
1543
1552
  level: "info",
1544
- message: `Generated headers refreshed in ${report.headersRewritten.length} file(s).`
1553
+ message: `Generated SQL artifacts refreshed in ${report.headersRewritten.length} file(s).`
1545
1554
  });
1546
1555
  }
1547
1556
  }
@@ -1848,6 +1857,10 @@ async function collectSchemaRisks(sqlDir, sqlFiles) {
1848
1857
  }
1849
1858
  return allRisks;
1850
1859
  }
1860
+ function filterAllowlistedSchemaRisks(risks) {
1861
+ const policy = getBoundaryPolicy();
1862
+ return risks.filter((risk) => !findDeclarativeRiskAllowlistMatch(risk, policy));
1863
+ }
1851
1864
  function summarizeRisks(risks) {
1852
1865
  const summaries = /* @__PURE__ */ new Map();
1853
1866
  for (const risk of risks) {
@@ -1966,7 +1979,7 @@ async function runSqlSchemaRiskCheck(result, logger4, step) {
1966
1979
  const sqlFiles = getDeclarativeSqlFiles(sqlDir, logger4);
1967
1980
  if (!sqlFiles) return;
1968
1981
  try {
1969
- const allRisks = await collectSchemaRisks(sqlDir, sqlFiles);
1982
+ const allRisks = filterAllowlistedSchemaRisks(await collectSchemaRisks(sqlDir, sqlFiles));
1970
1983
  if (allRisks.length === 0) {
1971
1984
  logger4.success(`Scanned ${sqlFiles.length} SQL file(s) - no violations`);
1972
1985
  return;
@@ -2653,193 +2666,501 @@ async function runDeclarativeDependencyCheck(result, logger4, step, strictOption
2653
2666
 
2654
2667
  // src/commands/db/utils/preflight-checks/duplicate-function-ownership-checks.ts
2655
2668
  init_esm_shims();
2656
- async function runDuplicateFunctionOwnershipCheck(result, logger4, step) {
2657
- logger4.step("Checking duplicate function ownership", step.next());
2658
- const analysis = analyzeDuplicateFunctionOwnership(process.cwd());
2659
- if (analysis.findings.length === 0) {
2660
- logger4.success("No duplicate declarative/idempotent function ownership detected");
2661
- return;
2662
- }
2663
- result.passed = false;
2664
- result.errors.push(`Found ${analysis.findings.length} duplicate function ownership finding(s)`);
2665
- logger4.error(`Found ${analysis.findings.length} duplicate function ownership finding(s):`);
2666
- logger4.info(` ${analysis.contractNote}`);
2667
- for (const finding of analysis.findings) {
2668
- const formatted = formatDuplicateFunctionOwnershipFinding(finding);
2669
- logger4.info(` [ERROR] ${formatted.summary}`);
2670
- for (const location of formatted.declarativeLocations) {
2671
- logger4.info(` declarative: ${location}`);
2672
- }
2673
- for (const location of formatted.idempotentLocations) {
2674
- logger4.info(` idempotent: ${location}`);
2675
- }
2676
- logger4.info(` ${formatted.suggestion}`);
2677
- }
2678
- }
2679
-
2680
- // src/commands/db/utils/preflight-checks/schema-boundary-checks.ts
2681
- init_esm_shims();
2682
2669
 
2683
- // src/commands/db/commands/db-sync/precheck-helpers.ts
2670
+ // src/commands/db/utils/duplicate-function-ownership-allowlist.ts
2684
2671
  init_esm_shims();
2685
- var SHOW_ALLOWLIST_REPORT = process.env.RUNA_DB_PRECHECK_ALLOWLIST_REPORT === "1";
2686
- var DIRECTORY_PLACEMENT_WARNING_PREFIX = " [misplacement] ";
2687
- function applyStrictModeToReport(report, strict) {
2688
- if (!strict) return report;
2689
- return {
2690
- blockers: [...report.blockers, ...report.warnings],
2691
- warnings: []
2692
- };
2693
- }
2694
- function formatAllowlistReason({
2695
- label,
2696
- ruleId,
2697
- reason,
2698
- rule
2699
- }) {
2700
- const meta = rule ? formatAllowlistMetadata(rule) : "";
2701
- return meta ? `[allowlist:${label}] ${ruleId}: ${reason} (${meta})` : `[allowlist:${label}] ${ruleId}: ${reason}`;
2702
- }
2703
2672
 
2704
- // src/commands/db/commands/db-sync/directory-placement-check.ts
2673
+ // src/commands/db/sync/schema-guardrail-config.ts
2705
2674
  init_esm_shims();
2706
2675
 
2707
- // src/commands/db/utils/schema-precheck-budget.ts
2676
+ // src/commands/db/sync/schema-guardrail-config-test-support.ts
2708
2677
  init_esm_shims();
2709
- var ONE_MB = 1024 * 1024;
2710
- var ONE_GB = 1024 * ONE_MB;
2711
- function parseIntEnv(name, fallback, min, max) {
2712
- const raw = process.env[name];
2713
- if (!raw) return fallback;
2714
- const value = Number.parseInt(raw, 10);
2715
- if (!Number.isFinite(value) || Number.isNaN(value)) return fallback;
2716
- const normalized = Math.trunc(value);
2717
- if (normalized < min) return min;
2718
- if (normalized > max) return max;
2719
- return normalized;
2720
- }
2721
- var RUNA_SCHEMA_PRECHECK_MAX_FILES = parseIntEnv(
2722
- "RUNA_SCHEMA_PRECHECK_MAX_FILES",
2723
- 3e3,
2724
- 100,
2725
- 2e4
2726
- );
2727
- var RUNA_SCHEMA_PRECHECK_MAX_TOTAL_BYTES = parseIntEnv(
2728
- "RUNA_SCHEMA_PRECHECK_MAX_TOTAL_BYTES",
2729
- 512 * ONE_MB,
2730
- 32 * ONE_MB,
2731
- 20 * ONE_GB
2732
- );
2733
- var RUNA_SCHEMA_PRECHECK_MAX_FILE_BYTES = parseIntEnv(
2734
- "RUNA_SCHEMA_PRECHECK_MAX_FILE_BYTES",
2735
- 64 * ONE_MB,
2736
- 1 * ONE_MB,
2737
- 512 * ONE_MB
2738
- );
2739
- var RUNA_PLAN_PRECHECK_MAX_BYTES = parseIntEnv(
2740
- "RUNA_PLAN_PRECHECK_MAX_BYTES",
2741
- 128 * ONE_MB,
2742
- 8 * ONE_MB,
2743
- 4 * ONE_GB
2744
- );
2745
- var RUNA_PLAN_PRECHECK_MAX_STATEMENTS = parseIntEnv(
2746
- "RUNA_PLAN_PRECHECK_MAX_STATEMENTS",
2747
- 4e3,
2748
- 100,
2749
- 5e4
2750
- );
2751
- function formatBytes2(value) {
2752
- if (value >= ONE_MB * 1024) return `${(value / (ONE_MB * 1024)).toFixed(1)} GB`;
2753
- if (value >= ONE_MB) return `${(value / ONE_MB).toFixed(1)} MB`;
2754
- return `${(value / 1024).toFixed(1)} KB`;
2755
- }
2756
- function buildBudgetExceededReason(context, files, bytes, maxFiles, maxBytes) {
2757
- return `${context}: budget exceeded (files=${files}/${maxFiles}, bytes=${formatBytes2(bytes)}/${formatBytes2(maxBytes)}). Stop and review with stricter scoping or increase RUNA_SCHEMA_PRECHECK_MAX_* settings only after approval.`;
2758
- }
2759
- function createSchemaPrecheckBudgetState() {
2760
- return {
2761
- scannedFiles: 0,
2762
- scannedBytes: 0,
2763
- maxFiles: RUNA_SCHEMA_PRECHECK_MAX_FILES,
2764
- maxBytes: RUNA_SCHEMA_PRECHECK_MAX_TOTAL_BYTES,
2765
- maxFileBytes: RUNA_SCHEMA_PRECHECK_MAX_FILE_BYTES
2766
- };
2678
+ function extractFirstStringMatch(content, fieldName) {
2679
+ const match = content.match(new RegExp(`${fieldName}\\s*:\\s*['"]([^'"]+)['"]`));
2680
+ return match?.[1];
2767
2681
  }
2768
- function shouldAbortSchemaPrecheckForBudget(state, filePath) {
2769
- const size = statSync(filePath).size;
2770
- if (size > state.maxFileBytes) {
2771
- return `Unscanned schema file ${filePath}: ${formatBytes2(size)} exceeds per-file limit ${formatBytes2(
2772
- state.maxFileBytes
2773
- )}`;
2774
- }
2775
- if (state.scannedFiles + 1 > state.maxFiles) {
2776
- return `Schema file scan budget exceeds ${state.maxFiles} files at ${path12.basename(filePath)}.`;
2777
- }
2778
- const projectedBytes = state.scannedBytes + size;
2779
- if (projectedBytes > state.maxBytes) {
2780
- return `Schema scan budget exceeds total size limit ${formatBytes2(state.maxBytes)}.`;
2682
+ function extractFirstNumberMatch(content, fieldName) {
2683
+ const match = content.match(new RegExp(`${fieldName}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`));
2684
+ if (!match?.[1]) {
2685
+ return void 0;
2781
2686
  }
2782
- state.scannedBytes = projectedBytes;
2783
- state.scannedFiles += 1;
2784
- return null;
2687
+ const parsed = Number(match[1]);
2688
+ return Number.isFinite(parsed) ? parsed : void 0;
2785
2689
  }
2786
-
2787
- // src/commands/db/utils/sql-file-collector.ts
2788
- init_esm_shims();
2789
- var IGNORED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([
2790
- "compat",
2791
- "archive",
2792
- "legacy",
2793
- "deprecated",
2794
- "backup",
2795
- "node_modules",
2796
- ".git",
2797
- "dist",
2798
- "build"
2799
- ]);
2800
- function shouldSkipDirectory(name) {
2801
- return IGNORED_DIRECTORY_NAMES.has(name.toLowerCase());
2690
+ function extractStringArrayMatch(content, fieldName) {
2691
+ const match = content.match(new RegExp(`${fieldName}\\s*:\\s*\\[([\\s\\S]*?)\\]`));
2692
+ if (!match?.[1]) {
2693
+ return [];
2694
+ }
2695
+ return Array.from(match[1].matchAll(/['"]([^'"]+)['"]/g), (entry) => entry[1] ?? "").filter(
2696
+ (value) => value.length > 0
2697
+ );
2802
2698
  }
2803
- function scanDirectory(dir, queue, sqlFiles) {
2804
- try {
2805
- const entries = readdirSync(dir, { withFileTypes: true });
2806
- for (const entry of entries) {
2807
- const fullPath = path12.join(dir, entry.name);
2808
- if (entry.isDirectory()) {
2809
- if (!shouldSkipDirectory(entry.name)) queue.push(fullPath);
2810
- } else if (entry.isFile() && entry.name.endsWith(".sql")) {
2811
- sqlFiles.push(fullPath);
2699
+ function extractNamedObjectBlock(content, fieldName) {
2700
+ const nameMatch = new RegExp(`${fieldName}\\s*:\\s*\\{`).exec(content);
2701
+ if (!nameMatch) {
2702
+ return null;
2703
+ }
2704
+ const startIndex = nameMatch.index + nameMatch[0].length - 1;
2705
+ let depth = 0;
2706
+ for (let index = startIndex; index < content.length; index += 1) {
2707
+ const current = content[index];
2708
+ if (current === "{") {
2709
+ depth += 1;
2710
+ } else if (current === "}") {
2711
+ depth -= 1;
2712
+ if (depth === 0) {
2713
+ return content.slice(startIndex + 1, index);
2812
2714
  }
2813
2715
  }
2814
- } catch {
2815
2716
  }
2717
+ return null;
2816
2718
  }
2817
- function* collectSqlFilesRecursively(baseDir) {
2818
- const queue = [baseDir];
2819
- const sqlFiles = [];
2820
- let index = 0;
2821
- while (index < queue.length) {
2822
- const currentDir = queue[index++];
2823
- if (!currentDir) continue;
2824
- scanDirectory(currentDir, queue, sqlFiles);
2719
+ function extractAllowedDuplicateFunctions(content, normalizers) {
2720
+ const match = content.match(/allowedDuplicateFunctions\s*:\s*\[([\s\S]*?)\]/);
2721
+ if (!match?.[1]) {
2722
+ return [];
2825
2723
  }
2826
- for (const file of sqlFiles) {
2827
- yield file;
2724
+ const entries = [];
2725
+ for (const objectMatch of match[1].matchAll(/\{([\s\S]*?)\}/g)) {
2726
+ const objectBody = objectMatch[1] ?? "";
2727
+ const qualifiedName = extractFirstStringMatch(objectBody, "qualifiedName");
2728
+ const signature = extractFirstStringMatch(objectBody, "signature");
2729
+ const reason = extractFirstStringMatch(objectBody, "reason");
2730
+ if (!qualifiedName || !signature || !reason) {
2731
+ continue;
2732
+ }
2733
+ entries.push({
2734
+ qualifiedName: normalizers.normalizeFunctionQualifiedName(qualifiedName),
2735
+ signature: normalizers.normalizeAllowlistSignature(signature),
2736
+ reason,
2737
+ declarativeFile: extractFirstStringMatch(objectBody, "declarativeFile"),
2738
+ idempotentFile: extractFirstStringMatch(objectBody, "idempotentFile"),
2739
+ expectedBodyHash: extractFirstStringMatch(objectBody, "expectedBodyHash")
2740
+ });
2828
2741
  }
2742
+ return entries;
2743
+ }
2744
+ function tryLoadSchemaGuardrailConfigFromText(params) {
2745
+ const configPath = findRunaConfig(params.targetDir);
2746
+ if (!configPath || !existsSync(configPath)) {
2747
+ return null;
2748
+ }
2749
+ try {
2750
+ const content = readFileSync(configPath, "utf-8");
2751
+ const schemaGuardrailsBlock = extractNamedObjectBlock(content, "schemaGuardrails") ?? "";
2752
+ const pgSchemaDiffBlock = extractNamedObjectBlock(content, "pgSchemaDiff") ?? "";
2753
+ return {
2754
+ ...params.defaults,
2755
+ declarativeSqlDir: extractFirstStringMatch(schemaGuardrailsBlock, "declarativeSqlDir") ?? params.defaults.declarativeSqlDir,
2756
+ allowedDuplicateFunctions: extractAllowedDuplicateFunctions(
2757
+ schemaGuardrailsBlock,
2758
+ params.normalizers
2759
+ ),
2760
+ generatedHeaderRewriteTargets: params.normalizers.normalizeFileList(
2761
+ extractStringArrayMatch(schemaGuardrailsBlock, "generatedHeaderRewriteTargets").map(
2762
+ (value) => params.normalizers.normalizePathForMatch(value)
2763
+ )
2764
+ ),
2765
+ semanticWarnings: {
2766
+ threshold: extractFirstNumberMatch(schemaGuardrailsBlock, "threshold") ?? params.defaults.semanticWarnings.threshold,
2767
+ maxCandidates: extractFirstNumberMatch(schemaGuardrailsBlock, "maxCandidates") ?? params.defaults.semanticWarnings.maxCandidates,
2768
+ ignorePairs: new Set(
2769
+ extractStringArrayMatch(schemaGuardrailsBlock, "ignorePairs").map(
2770
+ (value) => params.normalizers.normalizeSuppressionPair(value)
2771
+ )
2772
+ )
2773
+ },
2774
+ tableHeaderMaxWidth: extractFirstNumberMatch(schemaGuardrailsBlock, "tableHeaderMaxWidth") ?? params.defaults.tableHeaderMaxWidth,
2775
+ excludeFromOrphanDetection: extractStringArrayMatch(
2776
+ pgSchemaDiffBlock,
2777
+ "excludeFromOrphanDetection"
2778
+ ),
2779
+ idempotentSqlDir: extractFirstStringMatch(pgSchemaDiffBlock, "idempotentSqlDir") ?? params.defaults.idempotentSqlDir
2780
+ };
2781
+ } catch {
2782
+ return null;
2783
+ }
2784
+ }
2785
+
2786
+ // src/commands/db/sync/schema-guardrail-config.ts
2787
+ var DEFAULT_TABLE_HEADER_MAX_WIDTH = 160;
2788
+ var DEFAULT_SEMANTIC_WARNING_THRESHOLD = 0.55;
2789
+ var DEFAULT_SEMANTIC_WARNING_MAX_CANDIDATES = 3;
2790
+ var GENERIC_SIMILARITY_COLUMNS = /* @__PURE__ */ new Set([
2791
+ "id",
2792
+ "created_at",
2793
+ "updated_at",
2794
+ "deleted_at",
2795
+ "scope_id"
2796
+ ]);
2797
+ function normalizePathForMatch(filePath) {
2798
+ return filePath.replaceAll("\\", "/");
2799
+ }
2800
+ function normalizeFunctionQualifiedName(value) {
2801
+ return value.trim().toLowerCase();
2802
+ }
2803
+ function normalizeAllowlistSignature(value) {
2804
+ return value.replace(/\s+/g, " ").trim();
2805
+ }
2806
+ function normalizeSuppressionPair(value) {
2807
+ return value.split("::").map((part) => part.trim().toLowerCase()).filter((part) => part.length > 0).sort((left, right) => left.localeCompare(right)).join("::");
2808
+ }
2809
+ function normalizeFileList2(files) {
2810
+ return [...new Set(files)].sort((a, b) => a.localeCompare(b));
2811
+ }
2812
+ function isSchemaGuardrailTextFallbackAllowed() {
2813
+ return Boolean(process.env.VITEST);
2814
+ }
2815
+ function createDefaultSchemaGuardrailConfig() {
2816
+ return {
2817
+ declarativeSqlDir: "supabase/schemas/declarative",
2818
+ allowedDuplicateFunctions: [],
2819
+ generatedHeaderRewriteTargets: [],
2820
+ semanticWarnings: {
2821
+ threshold: DEFAULT_SEMANTIC_WARNING_THRESHOLD,
2822
+ maxCandidates: DEFAULT_SEMANTIC_WARNING_MAX_CANDIDATES,
2823
+ ignorePairs: /* @__PURE__ */ new Set()
2824
+ },
2825
+ tableHeaderMaxWidth: DEFAULT_TABLE_HEADER_MAX_WIDTH,
2826
+ excludeFromOrphanDetection: [],
2827
+ idempotentSqlDir: "supabase/schemas/idempotent"
2828
+ };
2829
+ }
2830
+ function loadAllowedDuplicatesFromSchemaOwnership(targetDir) {
2831
+ const candidates = [
2832
+ join(targetDir, "supabase", "schemas", "schema-ownership.json"),
2833
+ join(targetDir, "schema-ownership.json")
2834
+ ];
2835
+ for (const filePath of candidates) {
2836
+ if (!existsSync(filePath)) continue;
2837
+ try {
2838
+ const raw = JSON.parse(readFileSync(filePath, "utf-8"));
2839
+ const allowedDuplicates = raw?.rules?.allowed_duplicates;
2840
+ if (!Array.isArray(allowedDuplicates)) continue;
2841
+ return allowedDuplicates.filter(
2842
+ (entry) => typeof entry === "object" && entry !== null && "qualifiedName" in entry && typeof entry.qualifiedName === "string"
2843
+ ).map((entry) => ({
2844
+ qualifiedName: normalizeFunctionQualifiedName(entry.qualifiedName),
2845
+ signature: normalizeAllowlistSignature(entry.signature ?? ""),
2846
+ reason: entry.reason ?? "Loaded from schema-ownership.json"
2847
+ }));
2848
+ } catch {
2849
+ }
2850
+ }
2851
+ return [];
2852
+ }
2853
+ function loadSchemaGuardrailConfig(targetDir) {
2854
+ const defaults = createDefaultSchemaGuardrailConfig();
2855
+ try {
2856
+ const config = loadRunaConfig(targetDir);
2857
+ const databaseConfig = config.database ?? {};
2858
+ return {
2859
+ declarativeSqlDir: databaseConfig.schemaGuardrails?.declarativeSqlDir ?? defaults.declarativeSqlDir,
2860
+ allowedDuplicateFunctions: [
2861
+ ...databaseConfig.schemaGuardrails?.allowedDuplicateFunctions?.map((entry) => ({
2862
+ ...entry,
2863
+ qualifiedName: normalizeFunctionQualifiedName(entry.qualifiedName),
2864
+ signature: normalizeAllowlistSignature(entry.signature),
2865
+ declarativeFile: entry.declarativeFile ? normalizePathForMatch(entry.declarativeFile) : void 0,
2866
+ idempotentFile: entry.idempotentFile ? normalizePathForMatch(entry.idempotentFile) : void 0
2867
+ })) ?? [],
2868
+ // Fallback: merge schema-ownership.json allowed_duplicates if present
2869
+ ...loadAllowedDuplicatesFromSchemaOwnership(targetDir)
2870
+ ],
2871
+ generatedHeaderRewriteTargets: normalizeFileList2(
2872
+ (databaseConfig.schemaGuardrails?.generatedHeaderRewriteTargets ?? []).map(
2873
+ (value) => normalizePathForMatch(value)
2874
+ )
2875
+ ),
2876
+ semanticWarnings: {
2877
+ threshold: databaseConfig.schemaGuardrails?.semanticWarnings?.threshold ?? defaults.semanticWarnings.threshold,
2878
+ maxCandidates: databaseConfig.schemaGuardrails?.semanticWarnings?.maxCandidates ?? defaults.semanticWarnings.maxCandidates,
2879
+ ignorePairs: new Set(
2880
+ (databaseConfig.schemaGuardrails?.semanticWarnings?.ignorePairs ?? []).map(
2881
+ (value) => normalizeSuppressionPair(value)
2882
+ )
2883
+ )
2884
+ },
2885
+ tableHeaderMaxWidth: databaseConfig.schemaGuardrails?.tableHeaderMaxWidth ?? defaults.tableHeaderMaxWidth,
2886
+ excludeFromOrphanDetection: databaseConfig.pgSchemaDiff?.excludeFromOrphanDetection ?? [],
2887
+ idempotentSqlDir: databaseConfig.pgSchemaDiff?.idempotentSqlDir ?? defaults.idempotentSqlDir
2888
+ };
2889
+ } catch (error) {
2890
+ if (isSchemaGuardrailTextFallbackAllowed()) {
2891
+ return tryLoadSchemaGuardrailConfigFromText({
2892
+ targetDir,
2893
+ defaults,
2894
+ normalizers: {
2895
+ normalizeAllowlistSignature,
2896
+ normalizeFileList: normalizeFileList2,
2897
+ normalizeFunctionQualifiedName,
2898
+ normalizePathForMatch,
2899
+ normalizeSuppressionPair
2900
+ }
2901
+ }) ?? defaults;
2902
+ }
2903
+ throw error;
2904
+ }
2905
+ }
2906
+
2907
+ // src/commands/db/utils/duplicate-function-ownership-allowlist.ts
2908
+ function matchesDuplicateFunctionSignature(params) {
2909
+ return normalizeFunctionQualifiedName(params.entry.qualifiedName) === normalizeFunctionQualifiedName(params.finding.qualifiedName) && normalizeAllowlistSignature(params.entry.signature) === normalizeAllowlistSignature(params.finding.signature ?? "");
2910
+ }
2911
+ function matchesOptionalFilePath(params) {
2912
+ if (!params.configuredPath) {
2913
+ return true;
2914
+ }
2915
+ return params.definitionFiles.some(
2916
+ (filePath) => normalizePathForMatch(filePath) === params.configuredPath
2917
+ );
2918
+ }
2919
+ function matchesExpectedBodyHash(params) {
2920
+ if (!params.entry.expectedBodyHash || !params.bodyHashes) {
2921
+ return true;
2922
+ }
2923
+ const definitions = [
2924
+ ...params.finding.declarativeDefinitions,
2925
+ ...params.finding.idempotentDefinitions
2926
+ ];
2927
+ if (definitions.length === 0) {
2928
+ return false;
2929
+ }
2930
+ return definitions.every((definition) => {
2931
+ const key = `${definition.layer}:${definition.file}:${definition.line}`;
2932
+ return params.bodyHashes?.get(key) === params.entry.expectedBodyHash;
2933
+ });
2934
+ }
2935
+ function isAllowlistedDuplicateFunction(params) {
2936
+ const { finding, allowlist, bodyHashes } = params;
2937
+ if (!finding.signature) return false;
2938
+ return allowlist.some((entry) => {
2939
+ if (!matchesDuplicateFunctionSignature({ entry, finding })) {
2940
+ return false;
2941
+ }
2942
+ if (!matchesOptionalFilePath({
2943
+ configuredPath: entry.declarativeFile,
2944
+ definitionFiles: finding.declarativeDefinitions.map((definition) => definition.file)
2945
+ })) {
2946
+ return false;
2947
+ }
2948
+ if (!matchesOptionalFilePath({
2949
+ configuredPath: entry.idempotentFile,
2950
+ definitionFiles: finding.idempotentDefinitions.map((definition) => definition.file)
2951
+ })) {
2952
+ return false;
2953
+ }
2954
+ return matchesExpectedBodyHash({ entry, finding, bodyHashes });
2955
+ });
2956
+ }
2957
+ function filterAllowlistedDuplicateFunctions(params) {
2958
+ return params.findings.filter(
2959
+ (finding) => !isAllowlistedDuplicateFunction({
2960
+ finding,
2961
+ allowlist: params.allowlist,
2962
+ bodyHashes: params.bodyHashes
2963
+ })
2964
+ );
2965
+ }
2966
+
2967
+ // src/commands/db/utils/preflight-checks/duplicate-function-ownership-checks.ts
2968
+ async function runDuplicateFunctionOwnershipCheck(result, logger4, step) {
2969
+ logger4.step("Checking duplicate function ownership", step.next());
2970
+ const analysis = analyzeDuplicateFunctionOwnership(process.cwd());
2971
+ let findings = analysis.findings;
2972
+ try {
2973
+ const allowlist = loadSchemaGuardrailConfig(process.cwd()).allowedDuplicateFunctions;
2974
+ findings = filterAllowlistedDuplicateFunctions({
2975
+ findings,
2976
+ allowlist
2977
+ });
2978
+ } catch {
2979
+ }
2980
+ if (findings.length === 0) {
2981
+ logger4.success("No duplicate declarative/idempotent function ownership detected");
2982
+ return;
2983
+ }
2984
+ result.passed = false;
2985
+ result.errors.push(`Found ${findings.length} duplicate function ownership finding(s)`);
2986
+ logger4.error(`Found ${findings.length} duplicate function ownership finding(s):`);
2987
+ logger4.info(` ${analysis.contractNote}`);
2988
+ for (const finding of findings) {
2989
+ const formatted = formatDuplicateFunctionOwnershipFinding(finding);
2990
+ logger4.info(` [ERROR] ${formatted.summary}`);
2991
+ for (const location of formatted.declarativeLocations) {
2992
+ logger4.info(` declarative: ${location}`);
2993
+ }
2994
+ for (const location of formatted.idempotentLocations) {
2995
+ logger4.info(` idempotent: ${location}`);
2996
+ }
2997
+ logger4.info(` ${formatted.suggestion}`);
2998
+ }
2999
+ }
3000
+
3001
+ // src/commands/db/utils/preflight-checks/schema-boundary-checks.ts
3002
+ init_esm_shims();
3003
+
3004
+ // src/commands/db/commands/db-sync/precheck-helpers.ts
3005
+ init_esm_shims();
3006
+ var SHOW_ALLOWLIST_REPORT = process.env.RUNA_DB_PRECHECK_ALLOWLIST_REPORT === "1";
3007
+ var DIRECTORY_PLACEMENT_WARNING_PREFIX = " [misplacement] ";
3008
+ function applyStrictModeToReport(report, strict) {
3009
+ if (!strict) return report;
3010
+ return {
3011
+ blockers: [...report.blockers, ...report.warnings],
3012
+ warnings: []
3013
+ };
3014
+ }
3015
+ function formatAllowlistReason({
3016
+ label,
3017
+ ruleId,
3018
+ reason,
3019
+ rule
3020
+ }) {
3021
+ const meta = rule ? formatAllowlistMetadata(rule) : "";
3022
+ return meta ? `[allowlist:${label}] ${ruleId}: ${reason} (${meta})` : `[allowlist:${label}] ${ruleId}: ${reason}`;
3023
+ }
3024
+
3025
+ // src/commands/db/commands/db-sync/directory-placement-check.ts
3026
+ init_esm_shims();
3027
+
3028
+ // src/commands/db/utils/schema-precheck-budget.ts
3029
+ init_esm_shims();
3030
+ var ONE_MB = 1024 * 1024;
3031
+ var ONE_GB = 1024 * ONE_MB;
3032
+ function parseIntEnv(name, fallback, min, max) {
3033
+ const raw = process.env[name];
3034
+ if (!raw) return fallback;
3035
+ const value = Number.parseInt(raw, 10);
3036
+ if (!Number.isFinite(value) || Number.isNaN(value)) return fallback;
3037
+ const normalized = Math.trunc(value);
3038
+ if (normalized < min) return min;
3039
+ if (normalized > max) return max;
3040
+ return normalized;
3041
+ }
3042
+ var RUNA_SCHEMA_PRECHECK_MAX_FILES = parseIntEnv(
3043
+ "RUNA_SCHEMA_PRECHECK_MAX_FILES",
3044
+ 3e3,
3045
+ 100,
3046
+ 2e4
3047
+ );
3048
+ var RUNA_SCHEMA_PRECHECK_MAX_TOTAL_BYTES = parseIntEnv(
3049
+ "RUNA_SCHEMA_PRECHECK_MAX_TOTAL_BYTES",
3050
+ 512 * ONE_MB,
3051
+ 32 * ONE_MB,
3052
+ 20 * ONE_GB
3053
+ );
3054
+ var RUNA_SCHEMA_PRECHECK_MAX_FILE_BYTES = parseIntEnv(
3055
+ "RUNA_SCHEMA_PRECHECK_MAX_FILE_BYTES",
3056
+ 64 * ONE_MB,
3057
+ 1 * ONE_MB,
3058
+ 512 * ONE_MB
3059
+ );
3060
+ var RUNA_PLAN_PRECHECK_MAX_BYTES = parseIntEnv(
3061
+ "RUNA_PLAN_PRECHECK_MAX_BYTES",
3062
+ 128 * ONE_MB,
3063
+ 8 * ONE_MB,
3064
+ 4 * ONE_GB
3065
+ );
3066
+ var RUNA_PLAN_PRECHECK_MAX_STATEMENTS = parseIntEnv(
3067
+ "RUNA_PLAN_PRECHECK_MAX_STATEMENTS",
3068
+ 4e3,
3069
+ 100,
3070
+ 5e4
3071
+ );
3072
+ function formatBytes2(value) {
3073
+ if (value >= ONE_MB * 1024) return `${(value / (ONE_MB * 1024)).toFixed(1)} GB`;
3074
+ if (value >= ONE_MB) return `${(value / ONE_MB).toFixed(1)} MB`;
3075
+ return `${(value / 1024).toFixed(1)} KB`;
3076
+ }
3077
+ function buildBudgetExceededReason(context, files, bytes, maxFiles, maxBytes) {
3078
+ return `${context}: budget exceeded (files=${files}/${maxFiles}, bytes=${formatBytes2(bytes)}/${formatBytes2(maxBytes)}). Stop and review with stricter scoping or increase RUNA_SCHEMA_PRECHECK_MAX_* settings only after approval.`;
3079
+ }
3080
+ function createSchemaPrecheckBudgetState() {
3081
+ return {
3082
+ scannedFiles: 0,
3083
+ scannedBytes: 0,
3084
+ maxFiles: RUNA_SCHEMA_PRECHECK_MAX_FILES,
3085
+ maxBytes: RUNA_SCHEMA_PRECHECK_MAX_TOTAL_BYTES,
3086
+ maxFileBytes: RUNA_SCHEMA_PRECHECK_MAX_FILE_BYTES
3087
+ };
3088
+ }
3089
+ function shouldAbortSchemaPrecheckForBudget(state, filePath) {
3090
+ const size = statSync(filePath).size;
3091
+ if (size > state.maxFileBytes) {
3092
+ return `Unscanned schema file ${filePath}: ${formatBytes2(size)} exceeds per-file limit ${formatBytes2(
3093
+ state.maxFileBytes
3094
+ )}`;
3095
+ }
3096
+ if (state.scannedFiles + 1 > state.maxFiles) {
3097
+ return `Schema file scan budget exceeds ${state.maxFiles} files at ${path12.basename(filePath)}.`;
3098
+ }
3099
+ const projectedBytes = state.scannedBytes + size;
3100
+ if (projectedBytes > state.maxBytes) {
3101
+ return `Schema scan budget exceeds total size limit ${formatBytes2(state.maxBytes)}.`;
3102
+ }
3103
+ state.scannedBytes = projectedBytes;
3104
+ state.scannedFiles += 1;
3105
+ return null;
3106
+ }
3107
+
3108
+ // src/commands/db/utils/sql-file-collector.ts
3109
+ init_esm_shims();
3110
+ var IGNORED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([
3111
+ "compat",
3112
+ "archive",
3113
+ "legacy",
3114
+ "deprecated",
3115
+ "backup",
3116
+ "node_modules",
3117
+ ".git",
3118
+ "dist",
3119
+ "build"
3120
+ ]);
3121
+ function shouldSkipDirectory(name) {
3122
+ return IGNORED_DIRECTORY_NAMES.has(name.toLowerCase());
3123
+ }
3124
+ function scanDirectory(dir, queue, sqlFiles) {
3125
+ try {
3126
+ const entries = readdirSync(dir, { withFileTypes: true });
3127
+ for (const entry of entries) {
3128
+ const fullPath = path12.join(dir, entry.name);
3129
+ if (entry.isDirectory()) {
3130
+ if (!shouldSkipDirectory(entry.name)) queue.push(fullPath);
3131
+ } else if (entry.isFile() && entry.name.endsWith(".sql")) {
3132
+ sqlFiles.push(fullPath);
3133
+ }
3134
+ }
3135
+ } catch {
3136
+ }
3137
+ }
3138
+ function* collectSqlFilesRecursively(baseDir) {
3139
+ const queue = [baseDir];
3140
+ const sqlFiles = [];
3141
+ let index = 0;
3142
+ while (index < queue.length) {
3143
+ const currentDir = queue[index++];
3144
+ if (!currentDir) continue;
3145
+ scanDirectory(currentDir, queue, sqlFiles);
3146
+ }
3147
+ for (const file of sqlFiles) {
3148
+ yield file;
3149
+ }
3150
+ }
3151
+
3152
+ // src/commands/db/commands/db-sync/boundary-classifier.ts
3153
+ init_esm_shims();
3154
+
3155
+ // src/commands/db/commands/db-sync/sql-parser.ts
3156
+ init_esm_shims();
3157
+ function isWordChar(char) {
3158
+ const code = char.charCodeAt(0);
3159
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "_";
3160
+ }
3161
+ function isWhitespaceChar(char) {
3162
+ return char === " " || char === " " || char === "\n" || char === "\r" || char === "\f";
2829
3163
  }
2830
-
2831
- // src/commands/db/commands/db-sync/boundary-classifier.ts
2832
- init_esm_shims();
2833
-
2834
- // src/commands/db/commands/db-sync/sql-parser.ts
2835
- init_esm_shims();
2836
- function isWordChar(char) {
2837
- const code = char.charCodeAt(0);
2838
- return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || char === "_";
2839
- }
2840
- function isWhitespaceChar(char) {
2841
- return char === " " || char === " " || char === "\n" || char === "\r" || char === "\f";
2842
- }
2843
3164
  function parseWordAt(statement, start) {
2844
3165
  if (start < 0 || start >= statement.length) return null;
2845
3166
  const first = statement[start];
@@ -3884,7 +4205,7 @@ init_esm_shims();
3884
4205
  var riskDetectorLoader = null;
3885
4206
  function loadRiskDetectorModule() {
3886
4207
  if (!riskDetectorLoader) {
3887
- riskDetectorLoader = import('./risk-detector-S7XQF4I2.js').then((module) => ({
4208
+ riskDetectorLoader = import('./risk-detector-GDDLISVE.js').then((module) => ({
3888
4209
  detectSchemaRisks: module.detectSchemaRisks
3889
4210
  })).catch((error) => {
3890
4211
  riskDetectorLoader = null;
@@ -4933,437 +5254,203 @@ var dbSyncMachine = setup({
4933
5254
  }
4934
5255
  ],
4935
5256
  onError: {
4936
- target: "failed",
4937
- actions: assign({
4938
- error: ({ event }) => event.error instanceof Error ? event.error.message : "Setup failed"
4939
- })
4940
- }
4941
- }
4942
- },
4943
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4944
- // Step 2: Reconcile (optional)
4945
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4946
- reconcile: {
4947
- meta: { e2e: e2eMeta.reconcile },
4948
- invoke: {
4949
- src: "reconcile",
4950
- input: ({ context }) => ({
4951
- ctx: assertStepCtx(context),
4952
- autoApprove: context.input.autoApprove ?? false,
4953
- force: context.input.force ?? false
4954
- }),
4955
- onDone: [
4956
- {
4957
- guard: ({ event }) => event.output.passed === false,
4958
- target: "failed",
4959
- actions: assign({
4960
- error: ({ event }) => event.output.error ?? "Reconcile failed"
4961
- })
4962
- },
4963
- {
4964
- target: "preflight",
4965
- actions: assign({ reconciled: true })
4966
- }
4967
- ],
4968
- onError: {
4969
- // Non-critical, continue to preflight
4970
- target: "preflight",
4971
- actions: assign({ reconciled: false })
4972
- }
4973
- }
4974
- },
4975
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4976
- // Step 3: Preflight Checks
4977
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4978
- preflight: {
4979
- meta: { e2e: e2eMeta.preflight },
4980
- invoke: {
4981
- src: "preflight",
4982
- input: ({ context }) => ({ ctx: assertStepCtx(context) }),
4983
- onDone: [
4984
- {
4985
- guard: ({ event }) => event.output.passed === false,
4986
- target: "failed",
4987
- actions: assign({
4988
- error: ({ event }) => event.output.error ?? "Preflight failed"
4989
- })
4990
- },
4991
- {
4992
- target: "snapshot",
4993
- actions: assign({ preflightPassed: true })
4994
- }
4995
- ],
4996
- onError: {
4997
- target: "failed",
4998
- actions: assign({
4999
- error: ({ event }) => event.error instanceof Error ? event.error.message : "Preflight failed"
5000
- })
5001
- }
5002
- }
5003
- },
5004
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5005
- // Step 4: Snapshot (optional)
5006
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5007
- snapshot: {
5008
- meta: { e2e: e2eMeta.snapshot },
5009
- invoke: {
5010
- src: "snapshot",
5011
- input: ({ context }) => ({
5012
- ctx: assertStepCtx(context),
5013
- autoSnapshot: context.stepCtx?.autoSnapshot ?? false
5014
- }),
5015
- onDone: {
5016
- target: "sync",
5017
- actions: assign({
5018
- snapshotCreated: ({ event }) => event.output.created
5019
- })
5020
- },
5021
- onError: {
5022
- // Non-critical, continue to sync
5023
- target: "sync",
5024
- actions: assign({ snapshotCreated: false })
5257
+ target: "failed",
5258
+ actions: assign({
5259
+ error: ({ event }) => event.error instanceof Error ? event.error.message : "Setup failed"
5260
+ })
5025
5261
  }
5026
5262
  }
5027
5263
  },
5028
5264
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5029
- // Step 5: Sync Schema
5265
+ // Step 2: Reconcile (optional)
5030
5266
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5031
- sync: {
5032
- meta: { e2e: e2eMeta.sync },
5267
+ reconcile: {
5268
+ meta: { e2e: e2eMeta.reconcile },
5033
5269
  invoke: {
5034
- src: "syncSchema",
5035
- input: ({ context }) => ({ ctx: assertStepCtx(context) }),
5270
+ src: "reconcile",
5271
+ input: ({ context }) => ({
5272
+ ctx: assertStepCtx(context),
5273
+ autoApprove: context.input.autoApprove ?? false,
5274
+ force: context.input.force ?? false
5275
+ }),
5036
5276
  onDone: [
5037
5277
  {
5038
- guard: ({ event }) => event.output.applied === false && event.output.error != null,
5039
- target: "report",
5278
+ guard: ({ event }) => event.output.passed === false,
5279
+ target: "failed",
5040
5280
  actions: assign({
5041
- applied: false,
5042
- applyCommitted: ({ event }) => event.output.applyCommitted ?? false,
5043
- stepsCompleted: ({ event }) => event.output.stepsCompleted,
5044
- error: ({ event }) => event.output.error ?? "Sync failed"
5281
+ error: ({ event }) => event.output.error ?? "Reconcile failed"
5045
5282
  })
5046
5283
  },
5047
5284
  {
5048
- target: "report",
5049
- actions: assign({
5050
- applied: ({ event }) => event.output.applied,
5051
- applyCommitted: ({ event }) => event.output.applyCommitted ?? false,
5052
- stepsCompleted: ({ event }) => event.output.stepsCompleted
5053
- })
5285
+ target: "preflight",
5286
+ actions: assign({ reconciled: true })
5054
5287
  }
5055
5288
  ],
5056
5289
  onError: {
5057
- target: "report",
5058
- actions: assign({
5059
- applied: false,
5060
- applyCommitted: false,
5061
- error: ({ event }) => event.error instanceof Error ? event.error.message : "Sync failed"
5062
- })
5290
+ // Non-critical, continue to preflight
5291
+ target: "preflight",
5292
+ actions: assign({ reconciled: false })
5063
5293
  }
5064
5294
  }
5065
5295
  },
5066
5296
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5067
- // Step 6: Write Report
5297
+ // Step 3: Preflight Checks
5068
5298
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5069
- report: {
5070
- meta: { e2e: e2eMeta.report },
5299
+ preflight: {
5300
+ meta: { e2e: e2eMeta.preflight },
5071
5301
  invoke: {
5072
- src: "writeReport",
5073
- input: ({ context }) => ({
5074
- ctx: assertStepCtx(context),
5075
- startTime: context.startTime,
5076
- stepsCompleted: context.stepsCompleted,
5077
- success: context.error == null,
5078
- error: context.error ?? void 0
5079
- }),
5302
+ src: "preflight",
5303
+ input: ({ context }) => ({ ctx: assertStepCtx(context) }),
5080
5304
  onDone: [
5081
5305
  {
5082
- guard: ({ context }) => context.error != null,
5306
+ guard: ({ event }) => event.output.passed === false,
5083
5307
  target: "failed",
5084
- actions: assign({ reportWritten: true })
5308
+ actions: assign({
5309
+ error: ({ event }) => event.output.error ?? "Preflight failed"
5310
+ })
5085
5311
  },
5086
5312
  {
5087
- target: "done",
5088
- actions: assign({ reportWritten: true })
5313
+ target: "snapshot",
5314
+ actions: assign({ preflightPassed: true })
5089
5315
  }
5090
5316
  ],
5091
5317
  onError: {
5092
5318
  target: "failed",
5093
- actions: assign({ reportWritten: false })
5319
+ actions: assign({
5320
+ error: ({ event }) => event.error instanceof Error ? event.error.message : "Preflight failed"
5321
+ })
5094
5322
  }
5095
5323
  }
5096
5324
  },
5097
5325
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5098
- // Final States
5326
+ // Step 4: Snapshot (optional)
5099
5327
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5100
- done: {
5101
- meta: { e2e: e2eMeta.done },
5102
- type: "final"
5103
- },
5104
- failed: {
5105
- meta: { e2e: e2eMeta.failed },
5106
- type: "final"
5107
- }
5108
- },
5109
- output: ({ context }) => buildMachineOutput(context)
5110
- });
5111
- function getDbSyncStateName(snapshot2) {
5112
- return snapshot2.value;
5113
- }
5114
- function isDbSyncComplete(snapshot2) {
5115
- return snapshot2.status === "done";
5116
- }
5117
- function getDbSyncError(snapshot2) {
5118
- return snapshot2.context.error;
5119
- }
5120
-
5121
- // src/commands/db/sync/guardrail-orchestrator.ts
5122
- init_esm_shims();
5123
-
5124
- // src/commands/db/sync/schema-guardrail.ts
5125
- init_esm_shims();
5126
-
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()
5328
+ snapshot: {
5329
+ meta: { e2e: e2eMeta.snapshot },
5330
+ invoke: {
5331
+ src: "snapshot",
5332
+ input: ({ context }) => ({
5333
+ ctx: assertStepCtx(context),
5334
+ autoSnapshot: context.stepCtx?.autoSnapshot ?? false
5335
+ }),
5336
+ onDone: {
5337
+ target: "sync",
5338
+ actions: assign({
5339
+ snapshotCreated: ({ event }) => event.output.created
5340
+ })
5341
+ },
5342
+ onError: {
5343
+ // Non-critical, continue to sync
5344
+ target: "sync",
5345
+ actions: assign({ snapshotCreated: false })
5346
+ }
5347
+ }
5278
5348
  },
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
5349
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5350
+ // Step 5: Sync Schema
5351
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5352
+ sync: {
5353
+ meta: { e2e: e2eMeta.sync },
5354
+ invoke: {
5355
+ src: "syncSchema",
5356
+ input: ({ context }) => ({ ctx: assertStepCtx(context) }),
5357
+ onDone: [
5358
+ {
5359
+ guard: ({ event }) => event.output.applied === false && event.output.error != null,
5360
+ target: "report",
5361
+ actions: assign({
5362
+ applied: false,
5363
+ applyCommitted: ({ event }) => event.output.applyCommitted ?? false,
5364
+ stepsCompleted: ({ event }) => event.output.stepsCompleted,
5365
+ error: ({ event }) => event.output.error ?? "Sync failed"
5366
+ })
5367
+ },
5368
+ {
5369
+ target: "report",
5370
+ actions: assign({
5371
+ applied: ({ event }) => event.output.applied,
5372
+ applyCommitted: ({ event }) => event.output.applyCommitted ?? false,
5373
+ stepsCompleted: ({ event }) => event.output.stepsCompleted
5374
+ })
5375
+ }
5376
+ ],
5377
+ onError: {
5378
+ target: "report",
5379
+ actions: assign({
5380
+ applied: false,
5381
+ applyCommitted: false,
5382
+ error: ({ event }) => event.error instanceof Error ? event.error.message : "Sync failed"
5383
+ })
5354
5384
  }
5355
- }) ?? defaults;
5385
+ }
5386
+ },
5387
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5388
+ // Step 6: Write Report
5389
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5390
+ report: {
5391
+ meta: { e2e: e2eMeta.report },
5392
+ invoke: {
5393
+ src: "writeReport",
5394
+ input: ({ context }) => ({
5395
+ ctx: assertStepCtx(context),
5396
+ startTime: context.startTime,
5397
+ stepsCompleted: context.stepsCompleted,
5398
+ success: context.error == null,
5399
+ error: context.error ?? void 0
5400
+ }),
5401
+ onDone: [
5402
+ {
5403
+ guard: ({ context }) => context.error != null,
5404
+ target: "failed",
5405
+ actions: assign({ reportWritten: true })
5406
+ },
5407
+ {
5408
+ target: "done",
5409
+ actions: assign({ reportWritten: true })
5410
+ }
5411
+ ],
5412
+ onError: {
5413
+ target: "failed",
5414
+ actions: assign({ reportWritten: false })
5415
+ }
5416
+ }
5417
+ },
5418
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5419
+ // Final States
5420
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5421
+ done: {
5422
+ meta: { e2e: e2eMeta.done },
5423
+ type: "final"
5424
+ },
5425
+ failed: {
5426
+ meta: { e2e: e2eMeta.failed },
5427
+ type: "final"
5356
5428
  }
5357
- throw error;
5358
- }
5429
+ },
5430
+ output: ({ context }) => buildMachineOutput(context)
5431
+ });
5432
+ function getDbSyncStateName(snapshot2) {
5433
+ return snapshot2.value;
5359
5434
  }
5435
+ function isDbSyncComplete(snapshot2) {
5436
+ return snapshot2.status === "done";
5437
+ }
5438
+ function getDbSyncError(snapshot2) {
5439
+ return snapshot2.context.error;
5440
+ }
5441
+
5442
+ // src/commands/db/sync/guardrail-orchestrator.ts
5443
+ init_esm_shims();
5444
+
5445
+ // src/commands/db/sync/schema-guardrail.ts
5446
+ init_esm_shims();
5360
5447
 
5361
5448
  // src/commands/db/sync/schema-guardrail-graph.ts
5362
5449
  init_esm_shims();
5363
5450
 
5364
5451
  // src/commands/db/sync/schema-guardrail-runtime.ts
5365
5452
  init_esm_shims();
5366
- function stableSorted(values, map) {
5453
+ function stableSorted2(values, map) {
5367
5454
  return [...values].sort((a, b) => map(a).localeCompare(map(b)));
5368
5455
  }
5369
5456
  function normalizeFkSet(foreignKeys) {
@@ -5485,8 +5572,8 @@ function reconcileRuntimeGraph(params) {
5485
5572
  });
5486
5573
  }
5487
5574
  return {
5488
- warnings: stableSorted(warnings, (value) => value.target),
5489
- contradictions: stableSorted(contradictions, (value) => `${value.code}.${value.target}`)
5575
+ warnings: stableSorted2(warnings, (value) => value.target),
5576
+ contradictions: stableSorted2(contradictions, (value) => `${value.code}.${value.target}`)
5490
5577
  };
5491
5578
  }
5492
5579
  function runSchemaGuardrailRuntime(input) {
@@ -5516,7 +5603,7 @@ function runSchemaGuardrailRuntime(input) {
5516
5603
 
5517
5604
  // src/commands/db/sync/schema-guardrail-local-blockers.ts
5518
5605
  init_esm_shims();
5519
- function stableSorted2(values, keyOf) {
5606
+ function stableSorted3(values, keyOf) {
5520
5607
  return [...values].sort((left, right) => keyOf(left).localeCompare(keyOf(right)));
5521
5608
  }
5522
5609
  function createCrossSchemaHelperSuggestion(_filePath) {
@@ -5545,7 +5632,7 @@ function buildRawCrossSchemaRlsBlockers(params) {
5545
5632
  });
5546
5633
  }
5547
5634
  }
5548
- return stableSorted2(
5635
+ return stableSorted3(
5549
5636
  blockers,
5550
5637
  (value) => `${value.sourceFile}:${value.line ?? 0}:${value.target}`
5551
5638
  );
@@ -5572,7 +5659,7 @@ function buildExtensionPlacementBlockers(params) {
5572
5659
  });
5573
5660
  }
5574
5661
  }
5575
- return stableSorted2(
5662
+ return stableSorted3(
5576
5663
  blockers,
5577
5664
  (value) => `${value.sourceFile}:${value.line ?? 0}:${value.target}`
5578
5665
  );
@@ -5663,7 +5750,7 @@ function buildDynamicSqlBlockers(params) {
5663
5750
  blockers.push(...stmtBlockers);
5664
5751
  }
5665
5752
  }
5666
- return stableSorted2(blockers, (value) => `${value.sourceFile}:${value.line ?? 0}:${value.kind}`);
5753
+ return stableSorted3(blockers, (value) => `${value.sourceFile}:${value.line ?? 0}:${value.kind}`);
5667
5754
  }
5668
5755
  function buildLocalBlindSpotBlockers(params) {
5669
5756
  const blockers = [
@@ -5680,7 +5767,7 @@ function buildLocalBlindSpotBlockers(params) {
5680
5767
  requiredFile: detectExtensionFilePath()
5681
5768
  })
5682
5769
  ];
5683
- return stableSorted2(
5770
+ return stableSorted3(
5684
5771
  blockers,
5685
5772
  (value) => `${value.kind}:${value.sourceFile}:${value.line ?? 0}:${value.target}`
5686
5773
  );
@@ -5688,7 +5775,7 @@ function buildLocalBlindSpotBlockers(params) {
5688
5775
 
5689
5776
  // src/commands/db/sync/schema-guardrail-semantic-warnings.ts
5690
5777
  init_esm_shims();
5691
- function stableSorted3(values, map) {
5778
+ function stableSorted4(values, map) {
5692
5779
  return [...values].sort((a, b) => map(a).localeCompare(map(b)));
5693
5780
  }
5694
5781
  function normalizeFkSet2(foreignKeys) {
@@ -5893,50 +5980,7 @@ function buildSemanticDuplicateWarnings(params) {
5893
5980
  suppressionKey: createSuppressionPair(proposed.qualifiedName, primary.qualifiedName)
5894
5981
  });
5895
5982
  }
5896
- return stableSorted3(warnings, (value) => `${value.proposedTable}.${value.suppressionKey}`);
5897
- }
5898
-
5899
- // src/commands/db/sync/schema-guardrail-graph-types.ts
5900
- init_esm_shims();
5901
- var GENERATOR_VERSION = "1.0.0";
5902
- var SQL_IDENTIFIER_PATTERN = /"(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_$]*/g;
5903
- var SQL_EXTENSION_IDENTIFIER_PATTERN = /"(?:[^"]|"")+"|[A-Za-z0-9_-]+/g;
5904
- var QUALIFIED_SQL_OBJECT_RE = /\b([A-Za-z_][A-Za-z0-9_$]*)\s*\.\s*([A-Za-z_][A-Za-z0-9_$]*)\b/g;
5905
- var SECURITY_DEFINER_RE = /\bSECURITY\s+DEFINER\b/i;
5906
- var SEARCH_PATH_LOCK_RE = /\bSET\s+search_path\s*(?:=|TO)\s*''/i;
5907
- var MANAGED_BOUNDARY_SCHEMAS = [
5908
- "auth",
5909
- "storage",
5910
- "extensions",
5911
- "net",
5912
- "supabase_functions"
5913
- ];
5914
- var TRIGGER_GUIDANCE_SUPPRESSED_FUNCTIONS = /* @__PURE__ */ new Set(["public.handle_updated_at()"]);
5915
- var MAX_SCHEMA_GUIDANCE_TARGETS_PER_FILE = 3;
5916
- function makeGraphVersion(graph) {
5917
- const canonical = JSON.stringify(graph);
5918
- const hash = createHash("sha256").update(canonical).digest("hex");
5919
- return `sha256:${hash}`;
5920
- }
5921
- function currentIsoTimestamp() {
5922
- return (/* @__PURE__ */ new Date()).toISOString();
5923
- }
5924
- function stableSorted4(values, map) {
5925
- return [...values].sort((a, b) => map(a).localeCompare(map(b)));
5926
- }
5927
- function normalizeFileList2(files) {
5928
- return [...new Set(files)].sort((a, b) => a.localeCompare(b));
5929
- }
5930
- function normalizePolicyCommand(input) {
5931
- switch (input.toLowerCase()) {
5932
- case "select":
5933
- case "insert":
5934
- case "update":
5935
- case "delete":
5936
- return input.toLowerCase();
5937
- default:
5938
- return "all";
5939
- }
5983
+ return stableSorted4(warnings, (value) => `${value.proposedTable}.${value.suppressionKey}`);
5940
5984
  }
5941
5985
 
5942
5986
  // src/commands/db/sync/schema-guardrail-graph-nodes.ts
@@ -5960,37 +6004,6 @@ function buildFunctionBodyHashMap(files, layer) {
5960
6004
  }
5961
6005
  return hashes;
5962
6006
  }
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
6007
  function normalizeQualifiedObjectRef(schema, name, isFunctionCall) {
5995
6008
  return `${schema}.${name}${isFunctionCall ? "()" : ""}`;
5996
6009
  }
@@ -6013,8 +6026,8 @@ function collectQualifiedObjectRefs(params) {
6013
6026
  schemas.add(schema);
6014
6027
  }
6015
6028
  return {
6016
- refs: stableSorted4(refs, (value) => value),
6017
- schemas: stableSorted4(schemas, (value) => value)
6029
+ refs: stableSorted(refs, (value) => value),
6030
+ schemas: stableSorted(schemas, (value) => value)
6018
6031
  };
6019
6032
  }
6020
6033
  function isSearchPathLocked(statement) {
@@ -6052,7 +6065,7 @@ function buildDefinedFunctionMetadataByFile(params) {
6052
6065
  }
6053
6066
  metadataByFile.set(
6054
6067
  file.relativePath,
6055
- stableSorted4(fileMetadata, (value) => value.qualifiedSignature)
6068
+ stableSorted(fileMetadata, (value) => value.qualifiedSignature)
6056
6069
  );
6057
6070
  }
6058
6071
  return metadataByFile;
@@ -6146,13 +6159,13 @@ async function buildDeclarativeFileRecord(file) {
6146
6159
  file,
6147
6160
  declaredSchemas: [...declaredSchemas].sort((a, b) => a.localeCompare(b)),
6148
6161
  createSchemaClaims: [...createSchemaClaims].sort((a, b) => a.localeCompare(b)),
6149
- tables: stableSorted4(
6162
+ tables: stableSorted(
6150
6163
  document.tables.map((table) => ({
6151
6164
  schema: table.schema,
6152
6165
  name: table.name,
6153
6166
  qualifiedName: table.qualifiedName,
6154
6167
  lineNumber: table.lineNumber,
6155
- columns: stableSorted4(table.columns, (value) => value.name).map((value) => value.name),
6168
+ columns: stableSorted(table.columns, (value) => value.name).map((value) => value.name),
6156
6169
  outboundForeignKeys: table.foreignKeys.map((fk) => ({
6157
6170
  column: fk.column,
6158
6171
  referencesTable: fk.referencesTable,
@@ -6163,7 +6176,7 @@ async function buildDeclarativeFileRecord(file) {
6163
6176
  })),
6164
6177
  (value) => value.qualifiedName
6165
6178
  ),
6166
- policies: stableSorted4(
6179
+ policies: stableSorted(
6167
6180
  document.policies.map((policy) => ({
6168
6181
  name: policy.name,
6169
6182
  targetTable: `${policy.schema}.${policy.table}`,
@@ -6217,10 +6230,10 @@ function collectDeclarativeClaims(records) {
6217
6230
  };
6218
6231
  }
6219
6232
  function createDuplicateTableOwners(tableOwnerClaims) {
6220
- return stableSorted4(
6233
+ return stableSorted(
6221
6234
  [...tableOwnerClaims.entries()].filter(([, files]) => files.size > 1).map(([qualifiedName, files]) => ({
6222
6235
  qualifiedName,
6223
- files: normalizeFileList2(files)
6236
+ files: normalizeFileList(files)
6224
6237
  })),
6225
6238
  (value) => value.qualifiedName
6226
6239
  );
@@ -6234,7 +6247,7 @@ function buildFunctionValidationArtifacts(params) {
6234
6247
  ...buildFunctionBodyHashMap(params.declarativeFiles, "declarative"),
6235
6248
  ...buildFunctionBodyHashMap(params.idempotentFiles, "idempotent")
6236
6249
  ]);
6237
- const duplicateFunctionOwners = stableSorted4(
6250
+ const duplicateFunctionOwners = stableSorted(
6238
6251
  functionAnalysis.findings.filter(
6239
6252
  (finding) => !isAllowlistedDuplicateFunction({
6240
6253
  finding,
@@ -6244,16 +6257,16 @@ function buildFunctionValidationArtifacts(params) {
6244
6257
  ).map((finding) => ({
6245
6258
  qualifiedName: finding.qualifiedName,
6246
6259
  signature: finding.signature,
6247
- declarativeFiles: normalizeFileList2(
6260
+ declarativeFiles: normalizeFileList(
6248
6261
  finding.declarativeDefinitions.map((value) => value.file)
6249
6262
  ),
6250
- idempotentFiles: normalizeFileList2(
6263
+ idempotentFiles: normalizeFileList(
6251
6264
  finding.idempotentDefinitions.map((value) => value.file)
6252
6265
  )
6253
6266
  })),
6254
6267
  (value) => `${value.qualifiedName}.${value.signature ?? ""}`
6255
6268
  );
6256
- const functionClaims = stableSorted4(
6269
+ const functionClaims = stableSorted(
6257
6270
  [
6258
6271
  ...functionAnalysis.definitions.declarative.map((definition) => ({
6259
6272
  qualifiedName: definition.qualifiedName,
@@ -6282,7 +6295,7 @@ function buildFunctionValidationArtifacts(params) {
6282
6295
  function buildOwnerFileByTable(tableOwnerClaims) {
6283
6296
  const ownerFileByTable = /* @__PURE__ */ new Map();
6284
6297
  for (const [qualifiedName, files] of tableOwnerClaims.entries()) {
6285
- const normalizedFiles = normalizeFileList2(files);
6298
+ const normalizedFiles = normalizeFileList(files);
6286
6299
  if (normalizedFiles[0]) {
6287
6300
  ownerFileByTable.set(qualifiedName, normalizedFiles[0]);
6288
6301
  }
@@ -6302,7 +6315,7 @@ function buildPolicyOwnershipConflicts(params) {
6302
6315
  conflicts.push({
6303
6316
  policyName: policyClaim.name,
6304
6317
  targetTable: policyClaim.targetTable,
6305
- files: normalizeFileList2([policyClaim.sourceFile]),
6318
+ files: normalizeFileList([policyClaim.sourceFile]),
6306
6319
  tableOwnerFile: ownerFile
6307
6320
  });
6308
6321
  }
@@ -6313,11 +6326,11 @@ function buildPolicyOwnershipConflicts(params) {
6313
6326
  conflicts.push({
6314
6327
  policyName,
6315
6328
  targetTable,
6316
- files: normalizeFileList2(files),
6329
+ files: normalizeFileList(files),
6317
6330
  tableOwnerFile: params.ownerFileByTable.get(targetTable) ?? ""
6318
6331
  });
6319
6332
  }
6320
- return stableSorted4(
6333
+ return stableSorted(
6321
6334
  conflicts,
6322
6335
  (value) => `${value.targetTable}.${value.policyName}.${value.files.join(",")}`
6323
6336
  );
@@ -6333,16 +6346,16 @@ function buildTableNodesByName(params) {
6333
6346
  ownerFile: record.file.relativePath,
6334
6347
  lineNumber: table.lineNumber,
6335
6348
  columns: table.columns,
6336
- outboundForeignKeys: stableSorted4(
6349
+ outboundForeignKeys: stableSorted(
6337
6350
  table.outboundForeignKeys,
6338
6351
  (value) => `${value.referencesTable}.${value.column}`
6339
6352
  ),
6340
6353
  inboundForeignKeys: [],
6341
- policies: stableSorted4(
6354
+ policies: stableSorted(
6342
6355
  params.tablePolicies.get(table.qualifiedName) ?? [],
6343
6356
  (value) => value.name
6344
6357
  ),
6345
- triggers: stableSorted4(
6358
+ triggers: stableSorted(
6346
6359
  record.triggersByTable.get(table.qualifiedName) ?? [],
6347
6360
  (value) => value.name
6348
6361
  )
@@ -6366,7 +6379,7 @@ function attachInboundForeignKeys(tableNodesByName) {
6366
6379
  }
6367
6380
  }
6368
6381
  for (const tableNode of tableNodesByName.values()) {
6369
- tableNode.inboundForeignKeys = stableSorted4(
6382
+ tableNode.inboundForeignKeys = stableSorted(
6370
6383
  tableNode.inboundForeignKeys,
6371
6384
  (value) => `${value.fromTable}.${value.fromColumn}`
6372
6385
  );
@@ -6385,12 +6398,12 @@ function buildFileDependencies(params) {
6385
6398
  fileDependencyMap.set(tableNode.ownerFile, edges);
6386
6399
  }
6387
6400
  }
6388
- return stableSorted4(
6401
+ return stableSorted(
6389
6402
  [...fileDependencyMap.entries()].flatMap(
6390
6403
  ([fromFile, toMap]) => [...toMap.entries()].map(([toFile, viaTables]) => ({
6391
6404
  fromFile,
6392
6405
  toFile,
6393
- viaTables: normalizeFileList2(viaTables)
6406
+ viaTables: normalizeFileList(viaTables)
6394
6407
  }))
6395
6408
  ),
6396
6409
  (value) => `${value.fromFile}->${value.toFile}`
@@ -6412,7 +6425,7 @@ function buildFileNodes(params) {
6412
6425
  ["22_observability_cron.sql", "cron-registration"],
6413
6426
  ["25_storage_seed_bucket.sql", "storage-bootstrap"]
6414
6427
  ]);
6415
- return stableSorted4(
6428
+ return stableSorted(
6416
6429
  [
6417
6430
  ...params.records.map((record) => {
6418
6431
  const definedFunctions = params.definedFunctionMetadataByFile.get(record.file.relativePath) ?? [];
@@ -6422,7 +6435,7 @@ function buildFileNodes(params) {
6422
6435
  schemas: [],
6423
6436
  refs: []
6424
6437
  };
6425
- const triggerFunctions = stableSorted4(
6438
+ const triggerFunctions = stableSorted(
6426
6439
  new Set(
6427
6440
  [...record.triggersByTable.values()].flat().map((trigger) => normalizeTriggerFunctionToken(trigger.functionName)).filter((value) => value !== null)
6428
6441
  ),
@@ -6435,7 +6448,7 @@ function buildFileNodes(params) {
6435
6448
  domainName: path12.basename(record.file.relativePath, ".sql"),
6436
6449
  declaredSchemas: record.declaredSchemas,
6437
6450
  ownedTables: record.tables.map((table) => table.qualifiedName),
6438
- forwardDependsOnFiles: stableSorted4(
6451
+ forwardDependsOnFiles: stableSorted(
6439
6452
  params.fileDependencies.filter((edge) => edge.fromFile === record.file.relativePath).map((edge) => edge.toFile),
6440
6453
  (value) => value
6441
6454
  ),
@@ -6443,7 +6456,7 @@ function buildFileNodes(params) {
6443
6456
  securityDefinerFunctions: definedFunctions.filter((value) => value.securityDefiner).map((value) => value.qualifiedSignature),
6444
6457
  securityDefinerContracts: definedFunctions.filter((value) => value.securityDefiner && value.searchPathLocked).map((value) => value.qualifiedSignature),
6445
6458
  triggerFunctions,
6446
- functionCrossSchemaRefs: stableSorted4(
6459
+ functionCrossSchemaRefs: stableSorted(
6447
6460
  new Set(definedFunctions.flatMap((value) => value.crossSchemaRefs)),
6448
6461
  (value) => value
6449
6462
  ),
@@ -6453,7 +6466,7 @@ function buildFileNodes(params) {
6453
6466
  touchedExtensions: [],
6454
6467
  touchedFunctions: [],
6455
6468
  touchedPolicies: [],
6456
- policyCrossSchemaRefs: stableSorted4(
6469
+ policyCrossSchemaRefs: stableSorted(
6457
6470
  new Set(
6458
6471
  collectPolicyCrossSchemaReferences({
6459
6472
  content: record.file.content,
@@ -6490,7 +6503,7 @@ function buildFileNodes(params) {
6490
6503
  securityDefinerFunctions: definedFunctions.filter((value) => value.securityDefiner).map((value) => value.qualifiedSignature),
6491
6504
  securityDefinerContracts: definedFunctions.filter((value) => value.securityDefiner && value.searchPathLocked).map((value) => value.qualifiedSignature),
6492
6505
  triggerFunctions: [],
6493
- functionCrossSchemaRefs: stableSorted4(
6506
+ functionCrossSchemaRefs: stableSorted(
6494
6507
  new Set(definedFunctions.flatMap((value) => value.crossSchemaRefs)),
6495
6508
  (value) => value
6496
6509
  ),
@@ -6653,7 +6666,7 @@ function extractTouchedSchemas(content) {
6653
6666
  }
6654
6667
  }
6655
6668
  }
6656
- return stableSorted4(touched, (value) => value);
6669
+ return stableSorted(touched, (value) => value);
6657
6670
  }
6658
6671
  function extractTouchedFunctions(content) {
6659
6672
  const touched = /* @__PURE__ */ new Set();
@@ -6670,7 +6683,7 @@ function extractTouchedFunctions(content) {
6670
6683
  }
6671
6684
  }
6672
6685
  }
6673
- return stableSorted4(touched, (value) => value);
6686
+ return stableSorted(touched, (value) => value);
6674
6687
  }
6675
6688
  function normalizeExtensionIdentifier(identifier) {
6676
6689
  const trimmed = identifier.trim();
@@ -6693,7 +6706,7 @@ function extractTouchedExtensions(content) {
6693
6706
  }
6694
6707
  touched.add(normalizeExtensionIdentifier(identifier));
6695
6708
  }
6696
- return stableSorted4(touched, (value) => value);
6709
+ return stableSorted(touched, (value) => value);
6697
6710
  }
6698
6711
  function extractTouchedPolicies(content) {
6699
6712
  const touched = /* @__PURE__ */ new Set();
@@ -6707,7 +6720,7 @@ function extractTouchedPolicies(content) {
6707
6720
  }
6708
6721
  touched.add(`${schemaName}.${tableName}.${policyName}`);
6709
6722
  }
6710
- return stableSorted4(touched, (value) => value);
6723
+ return stableSorted(touched, (value) => value);
6711
6724
  }
6712
6725
  function extractIdempotentTouchMetadata(file) {
6713
6726
  const sanitizedContent = sanitizeIdempotentTouchScanContent(file.content);
@@ -6725,7 +6738,7 @@ function buildIdempotentTouchMetadata(files) {
6725
6738
  // src/commands/db/sync/schema-guardrail-graph-guidance.ts
6726
6739
  init_esm_shims();
6727
6740
  function summarizeBoundaryTargets(targets) {
6728
- const sortedTargets = stableSorted4(new Set(targets), (value) => value);
6741
+ const sortedTargets = stableSorted(new Set(targets), (value) => value);
6729
6742
  if (sortedTargets.length <= 2) {
6730
6743
  return sortedTargets.join(", ");
6731
6744
  }
@@ -6878,7 +6891,7 @@ function buildUnlockedSecurityDefinerGuidanceWarnings(params) {
6878
6891
  return [];
6879
6892
  }
6880
6893
  const lockedContracts = new Set(params.fileNode.securityDefinerContracts);
6881
- const unlockedFunctions = stableSorted4(
6894
+ const unlockedFunctions = stableSorted(
6882
6895
  params.fileNode.securityDefinerFunctions.filter(
6883
6896
  (qualifiedName) => !lockedContracts.has(qualifiedName)
6884
6897
  ),
@@ -6942,7 +6955,7 @@ function buildSecurityDefinerGuidanceWarnings(params) {
6942
6955
  if (!suggestedIdempotentFile || !targets) {
6943
6956
  return [];
6944
6957
  }
6945
- const touchedSecurityDefiners = stableSorted4(
6958
+ const touchedSecurityDefiners = stableSorted(
6946
6959
  new Set(targets.filter((target) => securityDefinerSet.has(target))),
6947
6960
  (value) => value
6948
6961
  );
@@ -7030,7 +7043,7 @@ function buildBoundaryGuidanceWarnings(params) {
7030
7043
  );
7031
7044
  continue;
7032
7045
  }
7033
- const touchedSchemas = stableSorted4(new Set(fileNode.touchedSchemas), (value) => value);
7046
+ const touchedSchemas = stableSorted(new Set(fileNode.touchedSchemas), (value) => value);
7034
7047
  if (touchedSchemas.length <= MAX_SCHEMA_GUIDANCE_TARGETS_PER_FILE) {
7035
7048
  for (const schema of touchedSchemas) {
7036
7049
  const ownerFile = schemaOwnerByName.get(schema);
@@ -7067,7 +7080,7 @@ function buildBoundaryGuidanceWarnings(params) {
7067
7080
  reason: "This idempotent file applies policy DDL against a table that is structurally owned in declarative SQL."
7068
7081
  });
7069
7082
  }
7070
- return stableSorted4(
7083
+ return stableSorted(
7071
7084
  warnings,
7072
7085
  (value) => `${value.sourceFile}.${value.kind}.${value.suggestedDeclarativeFile ?? ""}.${value.suggestedIdempotentFile ?? ""}.${value.target}`
7073
7086
  );
@@ -7143,7 +7156,7 @@ function validateDispatchCoverage(argsByFunction, sources) {
7143
7156
  }
7144
7157
  }
7145
7158
  }
7146
- return stableSorted4(warnings, (w) => `${w.sourceFile}:${w.target}`);
7159
+ return stableSorted(warnings, (w) => `${w.sourceFile}:${w.target}`);
7147
7160
  }
7148
7161
  var CREATE_INDEX_PATTERN = /^\s*CREATE\s+(?:UNIQUE\s+)?(?:INDEX\s+)?(?:CONCURRENTLY\s+)?(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|([A-Za-z_]\w*))\s+ON\b/gim;
7149
7162
  function extractIndexNames(content) {
@@ -7178,7 +7191,7 @@ function buildCrossLayerDuplicateIndexWarnings(sources) {
7178
7191
  }
7179
7192
  }
7180
7193
  }
7181
- return stableSorted4(warnings, (w) => `${w.sourceFile}:${w.target}`);
7194
+ return stableSorted(warnings, (w) => `${w.sourceFile}:${w.target}`);
7182
7195
  }
7183
7196
 
7184
7197
  // src/commands/db/sync/schema-guardrail-graph.ts
@@ -7189,11 +7202,11 @@ function loadSqlSources(targetDir, config) {
7189
7202
  };
7190
7203
  }
7191
7204
  function buildSchemaNodes(params) {
7192
- return stableSorted4(
7205
+ return stableSorted(
7193
7206
  [...params.schemaClaims.entries()].map(([name, files]) => ({
7194
7207
  name,
7195
- claimFiles: normalizeFileList2(files),
7196
- createSchemaFiles: normalizeFileList2(params.createSchemaClaims.get(name) ?? [])
7208
+ claimFiles: normalizeFileList(files),
7209
+ createSchemaFiles: normalizeFileList(params.createSchemaClaims.get(name) ?? [])
7197
7210
  })),
7198
7211
  (value) => value.name
7199
7212
  );
@@ -7206,9 +7219,9 @@ function createSchemaGraphManifest(params) {
7206
7219
  generatorVersion: GENERATOR_VERSION,
7207
7220
  files: params.fileNodes,
7208
7221
  schemas: params.schemaNodes,
7209
- tables: stableSorted4(params.tableNodesByName.values(), (value) => value.qualifiedName),
7222
+ tables: stableSorted(params.tableNodesByName.values(), (value) => value.qualifiedName),
7210
7223
  functionClaims: params.functionClaims,
7211
- policyClaims: stableSorted4(
7224
+ policyClaims: stableSorted(
7212
7225
  params.policyClaims,
7213
7226
  (value) => `${value.targetTable}.${value.name}.${value.sourceFile}`
7214
7227
  ),
@@ -7235,7 +7248,12 @@ async function buildStaticGraph(targetDir, config, sources) {
7235
7248
  const records = await Promise.all(
7236
7249
  declarativeFiles.map((file) => buildDeclarativeFileRecord(file))
7237
7250
  );
7251
+ const functionAclManifest = buildFunctionAclManifestFromSqlFiles(declarativeFiles);
7238
7252
  const idempotentTouchMetadata = buildIdempotentTouchMetadata(idempotentFiles);
7253
+ idempotentTouchMetadata.set(
7254
+ FUNCTION_ACL_RECONCILIATION_RELATIVE_PATH,
7255
+ buildFunctionAclIdempotentTouchMetadata(functionAclManifest)
7256
+ );
7239
7257
  const { tableOwnerClaims, schemaClaims, createSchemaClaims, tablePolicies, policyClaims } = collectDeclarativeClaims(records);
7240
7258
  const duplicateTableOwners = createDuplicateTableOwners(tableOwnerClaims);
7241
7259
  const { duplicateFunctionOwners, functionClaims } = buildFunctionValidationArtifacts({
@@ -7297,7 +7315,7 @@ async function buildStaticGraph(targetDir, config, sources) {
7297
7315
  policyClaims,
7298
7316
  fileDependencies
7299
7317
  });
7300
- const multiFileSchemas = stableSorted4(
7318
+ const multiFileSchemas = stableSorted(
7301
7319
  graph.schemas.filter((schemaNode) => schemaNode.claimFiles.length > 1).map((schemaNode) => ({
7302
7320
  schema: schemaNode.name,
7303
7321
  files: schemaNode.claimFiles
@@ -7329,6 +7347,7 @@ async function buildStaticGraph(targetDir, config, sources) {
7329
7347
  });
7330
7348
  return {
7331
7349
  graph,
7350
+ functionAclManifest,
7332
7351
  duplicateTableOwners,
7333
7352
  duplicateFunctionOwners,
7334
7353
  policyOwnershipConflicts,
@@ -7345,8 +7364,8 @@ var GUARDRAIL_PHASE_LABELS = {
7345
7364
  load_sources: "Load declarative SQL sources",
7346
7365
  build_static_graph: "Build static SQL graph",
7347
7366
  validate_ownership: "Validate ownership",
7348
- compare_generated_headers: "Compare generated headers",
7349
- refresh_generated_headers: "Refresh generated headers",
7367
+ compare_generated_headers: "Compare generated SQL artifacts",
7368
+ refresh_generated_headers: "Refresh generated SQL artifacts",
7350
7369
  handoff_db_sync: "Hand off to db sync",
7351
7370
  runtime_reconcile: "Runtime reconcile",
7352
7371
  publish_report: "Publish report"
@@ -7435,7 +7454,7 @@ function createCheckModePhases(report) {
7435
7454
  details: {
7436
7455
  file: block.file,
7437
7456
  target: block.target,
7438
- repair: "Run `runa db sync` to auto-regenerate headers"
7457
+ repair: "Run `runa db sync` to auto-regenerate managed SQL artifacts"
7439
7458
  }
7440
7459
  }))
7441
7460
  })
@@ -7937,12 +7956,35 @@ function buildHeaderRewritePlans(params) {
7937
7956
  }
7938
7957
  function loadHeaderRewritePlans(params) {
7939
7958
  try {
7940
- return buildHeaderRewritePlans({
7959
+ const headerPlans = buildHeaderRewritePlans({
7941
7960
  targetDir: params.targetDir,
7942
7961
  graph: params.graph,
7943
7962
  tableHeaderMaxWidth: params.config.tableHeaderMaxWidth,
7944
7963
  generatedHeaderRewriteTargets: params.config.generatedHeaderRewriteTargets
7945
7964
  });
7965
+ const generatedFileResult = buildGeneratedFileRewritePlans({
7966
+ targetDir: params.targetDir,
7967
+ manifest: params.functionAclManifest
7968
+ });
7969
+ if ("failure" in generatedFileResult) {
7970
+ const failureMessage = generatedFileResult.failure ?? "Unknown function ACL migration failure";
7971
+ return {
7972
+ failure: {
7973
+ graph: params.graph,
7974
+ report: setFailure2(
7975
+ params.report,
7976
+ "compare_generated_headers",
7977
+ "function_acl_migration_required",
7978
+ failureMessage
7979
+ )
7980
+ }
7981
+ };
7982
+ }
7983
+ return {
7984
+ staleBlocks: [...headerPlans.staleBlocks, ...generatedFileResult.staleBlocks],
7985
+ rewritePlans: headerPlans.rewritePlans,
7986
+ generatedFileRewritePlans: generatedFileResult.rewritePlans
7987
+ };
7946
7988
  } catch (error) {
7947
7989
  const message = error instanceof Error ? error.message : String(error);
7948
7990
  return {
@@ -7971,7 +8013,7 @@ function finalizeCheckModeReport(params) {
7971
8013
  params.report,
7972
8014
  "compare_generated_headers",
7973
8015
  "stale_generated_header",
7974
- `Generated headers are stale in ${params.report.staleBlocks.map((value) => `${value.file}:${value.kind}`).join(", ")}`
8016
+ `Generated SQL artifacts are stale in ${params.report.staleBlocks.map((value) => `${value.file}:${value.kind}`).join(", ")}`
7975
8017
  )
7976
8018
  };
7977
8019
  }
@@ -7990,6 +8032,15 @@ function rewriteManagedHeaders(params) {
7990
8032
  writeFileSync(path12.join(params.targetDir, plan.filePath), rewrittenSql, "utf-8");
7991
8033
  params.report.headersRewritten.push(plan.filePath);
7992
8034
  }
8035
+ for (const plan of params.generatedFileRewritePlans) {
8036
+ const absolutePath = path12.join(params.targetDir, plan.filePath);
8037
+ const currentSql = existsSync(absolutePath) ? readFileSync(absolutePath, "utf-8") : "";
8038
+ if (normalizeMultilineText(currentSql) === normalizeMultilineText(plan.expectedSql)) {
8039
+ continue;
8040
+ }
8041
+ writeFileSync(absolutePath, plan.expectedSql, "utf-8");
8042
+ params.report.headersRewritten.push(plan.filePath);
8043
+ }
7993
8044
  } catch (error) {
7994
8045
  const message = error instanceof Error ? error.message : String(error);
7995
8046
  return {
@@ -8007,6 +8058,41 @@ function rewriteManagedHeaders(params) {
8007
8058
  params.report.staleBlocks = [];
8008
8059
  return null;
8009
8060
  }
8061
+ function buildGeneratedFileRewritePlans(params) {
8062
+ const absolutePath = path12.join(params.targetDir, FUNCTION_ACL_RECONCILIATION_RELATIVE_PATH);
8063
+ const fileExists = existsSync(absolutePath);
8064
+ const existingSql = fileExists ? readFileSync(absolutePath, "utf-8") : "";
8065
+ if (!fileExists && !functionAclManifestHasEntries(params.manifest)) {
8066
+ return {
8067
+ staleBlocks: [],
8068
+ rewritePlans: []
8069
+ };
8070
+ }
8071
+ if (fileExists) {
8072
+ const migrationGaps = validateFunctionAclMigration(params.manifest, existingSql);
8073
+ if (migrationGaps.length > 0) {
8074
+ return {
8075
+ failure: `Function ACL migration is required before auto-generation can proceed. Add declarative annotations for: ${migrationGaps.join(", ")}`
8076
+ };
8077
+ }
8078
+ }
8079
+ const staleBlocks = !fileExists || isManagedFunctionAclFileContentStale(params.manifest, existingSql) ? [
8080
+ {
8081
+ file: FUNCTION_ACL_RECONCILIATION_RELATIVE_PATH,
8082
+ kind: "generated-file",
8083
+ target: `file:${FUNCTION_ACL_RECONCILIATION_RELATIVE_PATH}`
8084
+ }
8085
+ ] : [];
8086
+ return {
8087
+ staleBlocks,
8088
+ rewritePlans: [
8089
+ {
8090
+ filePath: FUNCTION_ACL_RECONCILIATION_RELATIVE_PATH,
8091
+ expectedSql: renderFunctionAclFile(params.manifest)
8092
+ }
8093
+ ]
8094
+ };
8095
+ }
8010
8096
 
8011
8097
  // src/commands/db/sync/schema-guardrail.ts
8012
8098
  function createEmptyReport(mode) {
@@ -8078,7 +8164,7 @@ function validateLocalBlindSpotBlockers(report, graph) {
8078
8164
  if (!primary) {
8079
8165
  return null;
8080
8166
  }
8081
- const failureCode = primary.kind === "cross-schema-rls" ? "raw_cross_schema_rls_blocked" : primary.kind === "dynamic-sql" ? "dynamic_sql_blocked" : "extension_placement_blocked";
8167
+ const failureCode = primary.kind === "cross-schema-rls" ? "raw_cross_schema_rls_blocked" : primary.kind === "dynamic-sql" || primary.kind === "dynamic-sql-infra" ? "dynamic_sql_blocked" : "extension_placement_blocked";
8082
8168
  return {
8083
8169
  graph,
8084
8170
  report: setFailure3(report, "validate_ownership", failureCode, primary.details)
@@ -8169,6 +8255,7 @@ async function runSchemaGuardrailStatic(input) {
8169
8255
  const headerPlanResult = loadHeaderRewritePlans({
8170
8256
  targetDir: input.targetDir,
8171
8257
  graph: staticGraphResult.graph,
8258
+ functionAclManifest: staticGraphResult.functionAclManifest,
8172
8259
  config,
8173
8260
  report
8174
8261
  });
@@ -8190,6 +8277,7 @@ async function runSchemaGuardrailStatic(input) {
8190
8277
  const rewriteFailure = rewriteManagedHeaders({
8191
8278
  targetDir: input.targetDir,
8192
8279
  rewritePlans: headerPlanResult.rewritePlans,
8280
+ generatedFileRewritePlans: headerPlanResult.generatedFileRewritePlans,
8193
8281
  report
8194
8282
  });
8195
8283
  if (rewriteFailure) {
@@ -12069,14 +12157,10 @@ async function collectLocalPrecheckBundle(strict) {
12069
12157
  guardrailAllowlist = loadSchemaGuardrailConfig(process.cwd()).allowedDuplicateFunctions;
12070
12158
  } catch {
12071
12159
  }
12072
- const duplicateOwnershipBlockers = duplicateOwnershipAnalysis.findings.filter(
12073
- (finding) => !isAllowlistedDuplicateFunction({
12074
- finding,
12075
- allowlist: guardrailAllowlist,
12076
- bodyHashes: /* @__PURE__ */ new Map()
12077
- // Hash check skipped; name + signature matching still works
12078
- })
12079
- ).map((finding) => {
12160
+ const duplicateOwnershipBlockers = filterAllowlistedDuplicateFunctions({
12161
+ findings: duplicateOwnershipAnalysis.findings,
12162
+ allowlist: guardrailAllowlist
12163
+ }).map((finding) => {
12080
12164
  const formatted = formatDuplicateFunctionOwnershipFinding(finding);
12081
12165
  return [
12082
12166
  formatted.summary,