@runa-ai/runa-cli 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/{build-HUDIP6KU.js → build-HQMSVN6N.js} +3 -3
  2. package/dist/{check-LOMVIRHX.js → check-PCSQPYDM.js} +2 -2
  3. package/dist/{chunk-QM53IQHM.js → chunk-2QX7T24B.js} +1 -1
  4. package/dist/{chunk-XDCHRVE3.js → chunk-4XHZQRRK.js} +2 -2
  5. package/dist/{chunk-7B5C6U2K.js → chunk-A6A7JIRD.js} +35 -2
  6. package/dist/{chunk-Z4Z5DNW4.js → chunk-B3POLMII.js} +12 -0
  7. package/dist/chunk-CKRLVEIO.js +119 -0
  8. package/dist/{chunk-HD74F6W2.js → chunk-FWMGC5FP.js} +1 -0
  9. package/dist/{chunk-H2AHNI75.js → chunk-LCK2LGVR.js} +1 -1
  10. package/dist/{chunk-FHG3ILE4.js → chunk-OBYZDT2E.js} +38 -8
  11. package/dist/{chunk-AFY3TX4I.js → chunk-PMXE5XOJ.js} +1 -1
  12. package/dist/{chunk-VM3IWOT5.js → chunk-QSEF4T3Y.js} +13 -5
  13. package/dist/{chunk-NPSRD26F.js → chunk-UHDAYPHH.js} +1 -1
  14. package/dist/{chunk-2APB25TT.js → chunk-VSH3IXDQ.js} +7 -3
  15. package/dist/{chunk-644FVGIQ.js → chunk-WPMR7RQ4.js} +9 -2
  16. package/dist/{chunk-EMB6IZFT.js → chunk-XVNDDHAF.js} +20 -1
  17. package/dist/{risk-detector-plpgsql-HWKS4OLR.js → chunk-Y5ANTCKE.js} +3 -412
  18. package/dist/{chunk-CCW3PLQY.js → chunk-Z7A4BEWF.js} +1 -1
  19. package/dist/{ci-XY6IKEDC.js → ci-Z4525QW6.js} +2150 -488
  20. package/dist/{cli-UZA4RBNQ.js → cli-Q2XIQDRS.js} +72 -54
  21. package/dist/commands/ci/commands/ci-prod-db-operations.d.ts +6 -4
  22. package/dist/commands/ci/commands/ci-prod-types.d.ts +3 -0
  23. package/dist/commands/ci/commands/ci-prod-workflow.d.ts +1 -1
  24. package/dist/commands/ci/commands/ci-resolvers.d.ts +1 -1
  25. package/dist/commands/ci/commands/ci-supabase-local.d.ts +4 -0
  26. package/dist/commands/ci/machine/actors/build/build-and-playwright.d.ts +1 -1
  27. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +11 -1
  28. package/dist/commands/ci/machine/actors/db/production-preview.d.ts +22 -4
  29. package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +8 -1
  30. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +1 -0
  31. package/dist/commands/ci/machine/actors/finalize/index.d.ts +0 -1
  32. package/dist/commands/ci/machine/actors/index.d.ts +1 -1
  33. package/dist/commands/ci/machine/actors/setup/local.d.ts +2 -0
  34. package/dist/commands/ci/machine/actors/setup/pr-common.d.ts +3 -0
  35. package/dist/commands/ci/machine/actors/setup/pr-local.d.ts +2 -0
  36. package/dist/commands/ci/machine/commands/machine-runner.d.ts +5 -1
  37. package/dist/commands/ci/machine/commands/step-telemetry.d.ts +16 -0
  38. package/dist/commands/ci/machine/contract.d.ts +40 -0
  39. package/dist/commands/ci/machine/formatters/github-comment-types.d.ts +7 -2
  40. package/dist/commands/ci/machine/formatters/github-comment.d.ts +2 -1
  41. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts +2 -1
  42. package/dist/commands/ci/machine/formatters/sections/index.d.ts +1 -1
  43. package/dist/commands/ci/machine/formatters/summary.d.ts +4 -4
  44. package/dist/commands/ci/machine/guards.d.ts +4 -0
  45. package/dist/commands/ci/machine/helpers.d.ts +25 -0
  46. package/dist/commands/ci/machine/machine-state-helpers.d.ts +1 -1
  47. package/dist/commands/ci/machine/machine.d.ts +15 -8
  48. package/dist/commands/ci/machine/types.d.ts +9 -0
  49. package/dist/commands/ci/utils/ci-diagnostics.d.ts +67 -0
  50. package/dist/commands/ci/utils/ci-summary.d.ts +118 -0
  51. package/dist/commands/ci/utils/db-url-utils.d.ts +4 -77
  52. package/dist/commands/ci/utils/github-api.d.ts +14 -0
  53. package/dist/commands/db/apply/contract.d.ts +73 -0
  54. package/dist/commands/db/apply/helpers/alter-statement-parsers.d.ts +95 -0
  55. package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts +0 -61
  56. package/dist/commands/db/apply/helpers/function-plan-false-positive-filter.d.ts +36 -0
  57. package/dist/commands/db/apply/helpers/hazard-handler.d.ts +4 -4
  58. package/dist/commands/db/apply/helpers/index.d.ts +14 -5
  59. package/dist/commands/db/apply/helpers/partition-acl-cleaner.d.ts +3 -1
  60. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +69 -6
  61. package/dist/commands/db/apply/helpers/plan-ast.d.ts +56 -0
  62. package/dist/commands/db/apply/helpers/plan-check-filter.d.ts +26 -0
  63. package/dist/commands/db/apply/helpers/plan-drop-protection.d.ts +43 -0
  64. package/dist/commands/db/apply/helpers/plan-ordering.d.ts +6 -0
  65. package/dist/commands/db/apply/helpers/plan-statement-parser.d.ts +39 -0
  66. package/dist/commands/db/apply/helpers/plan-validator.d.ts +8 -40
  67. package/dist/commands/db/apply/helpers/retry-logic.d.ts +1 -10
  68. package/dist/commands/db/apply/helpers/temp-db-bootstrap.d.ts +18 -0
  69. package/dist/commands/db/apply/helpers/temp-db-dsn.d.ts +14 -0
  70. package/dist/commands/db/apply/machine.d.ts +56 -32
  71. package/dist/commands/db/commands/db-apply-error.d.ts +5 -0
  72. package/dist/commands/db/commands/db-apply.d.ts +2 -0
  73. package/dist/commands/db/commands/db-sync/directory-placement-check.d.ts +4 -0
  74. package/dist/commands/db/commands/db-sync/error-classifier.d.ts +1 -1
  75. package/dist/commands/db/commands/db-sync/plan-boundary-reconciliation.d.ts +3 -0
  76. package/dist/commands/db/commands/db-sync/precheck-helpers.d.ts +18 -0
  77. package/dist/commands/db/commands/db-sync/production-precheck.d.ts +15 -0
  78. package/dist/commands/db/commands/db-sync/risk-scan-collectors.d.ts +11 -0
  79. package/dist/commands/db/commands/db-sync.d.ts +11 -5
  80. package/dist/commands/db/sync/contract.d.ts +80 -0
  81. package/dist/commands/db/sync/machine.d.ts +60 -1
  82. package/dist/commands/db/types.d.ts +5 -0
  83. package/dist/commands/db/utils/boundary-policy/rule-compiler.d.ts +2 -1
  84. package/dist/commands/db/utils/boundary-policy/types.d.ts +21 -0
  85. package/dist/commands/db/utils/boundary-policy-runtime.d.ts +12 -3
  86. package/dist/commands/db/utils/boundary-policy.d.ts +1 -1
  87. package/dist/commands/db/utils/db-target.d.ts +5 -3
  88. package/dist/commands/db/utils/declarative-dependency-collectors.d.ts +6 -0
  89. package/dist/commands/db/utils/declarative-dependency-contract.d.ts +78 -0
  90. package/dist/commands/db/utils/declarative-dependency-sql-utils.d.ts +49 -0
  91. package/dist/commands/db/utils/declarative-dependency-warning-governance.d.ts +24 -0
  92. package/dist/commands/db/utils/preflight-check.d.ts +1 -1
  93. package/dist/commands/db/utils/preflight-checks/declarative-dependency-checks.d.ts +4 -0
  94. package/dist/commands/db/utils/preflight-checks/idempotent-risk-checks.d.ts +4 -0
  95. package/dist/commands/db/utils/preflight-checks/schema-boundary-checks.d.ts +4 -0
  96. package/dist/commands/db/utils/preflight-checks/schema-risk-policy.d.ts +4 -0
  97. package/dist/commands/db/utils/preflight-checks/supabase-checks.d.ts +12 -0
  98. package/dist/commands/db/utils/psql.d.ts +23 -0
  99. package/dist/commands/db/utils/sql-table-extractor.d.ts +42 -1
  100. package/dist/commands/env/commands/setup/types.d.ts +1 -0
  101. package/dist/commands/env/constants/local-supabase.d.ts +4 -1
  102. package/dist/commands/observability.d.ts +72 -0
  103. package/dist/commands/observability.helpers.d.ts +25 -0
  104. package/dist/commands/template-check/contract.d.ts +3 -3
  105. package/dist/commands/template-check/machine.d.ts +1 -1
  106. package/dist/commands/workflow/commands/deploy-production.d.ts +0 -1
  107. package/dist/constants/versions.d.ts +1 -1
  108. package/dist/{db-Q3GF7JWP.js → db-BPQ2TEQM.js} +14361 -11076
  109. package/dist/{dev-5YXNPTCJ.js → dev-MLRKIP7F.js} +5 -5
  110. package/dist/{doctor-MZLOA53G.js → doctor-ROSWSMLH.js} +2 -2
  111. package/dist/{env-GMB3THRG.js → env-WNHJVLOT.js} +37 -20
  112. package/dist/{env-HMMRSYCI.js → env-XPPACZM4.js} +2 -2
  113. package/dist/{env-files-2UIUYLLR.js → env-files-HRNUGZ5O.js} +1 -1
  114. package/dist/{error-handler-HEXBRNVV.js → error-handler-YRQWRDEF.js} +17 -0
  115. package/dist/{hotfix-NDTPY2T4.js → hotfix-Z5EGVSMH.js} +4 -4
  116. package/dist/index.js +4 -4
  117. package/dist/{init-U4VCRHTD.js → init-S2ATHLJ6.js} +1 -1
  118. package/dist/{inject-test-attrs-P44BVTQS.js → inject-test-attrs-XN4I2AOR.js} +2 -2
  119. package/dist/internal/machines/index.d.ts +1 -1
  120. package/dist/internal/machines/snapshot-helpers.d.ts +6 -0
  121. package/dist/{manifest-TMFLESHW.js → manifest-EGCAZ4TK.js} +1 -1
  122. package/dist/observability-CJA5UFIC.js +721 -0
  123. package/dist/{risk-detector-4U6ZJ2G5.js → risk-detector-VO5HJR4R.js} +1 -1
  124. package/dist/{risk-detector-core-TK4OAI3N.js → risk-detector-core-7WZJZ5ZI.js} +61 -3
  125. package/dist/risk-detector-plpgsql-ULV7NLDB.js +638 -0
  126. package/dist/{template-check-FFJVDLBF.js → template-check-BDFMT6ZO.js} +1 -1
  127. package/dist/{upgrade-7TWORWBV.js → upgrade-BDUWBRT5.js} +1 -1
  128. package/dist/utils/db-url-utils.d.ts +81 -0
  129. package/dist/validators/risk-detector-plpgsql.d.ts +3 -1
  130. package/dist/{vuln-check-6CMNPSBR.js → vuln-check-66RXX3TO.js} +1 -1
  131. package/dist/{vuln-checker-EJJTNDNE.js → vuln-checker-FFOGOJPT.js} +1 -1
  132. package/dist/{watch-PNTKZYFB.js → watch-ITYW57SL.js} +1 -1
  133. package/dist/{workflow-H75N4BXX.js → workflow-UZIZ2JUS.js} +2 -3
  134. package/package.json +3 -3
  135. package/dist/chunk-AKZAN4BC.js +0 -90
  136. package/dist/commands/ci/machine/actors/finalize/summary.d.ts +0 -32
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ import { splitPlpgsqlStatementsWithOffsets, extractExecuteExpressions, skipWhitespace, parseTopLevelAssignment, extractStaticSqlFromExpression, mergeStringEnvValue, skipIdentifier } from './chunk-Y5ANTCKE.js';
4
+ import { stripSqlCommentsPreserveLines, buildLineStarts, lineNumberFromIndex, stripSqlStringsPreserveLines, detectRisksFromContent, stripSqlForPatternMatching } from './chunk-3FDQW524.js';
5
+ import { init_esm_shims } from './chunk-VRXHCR5K.js';
6
+
7
+ createRequire(import.meta.url);
8
+
9
+ // src/validators/risk-detector-plpgsql.ts
10
+ init_esm_shims();
11
+ var DO_STATEMENT_PATTERN = /\bDO\b/gi;
12
+ var CREATE_OR_ALTER_FUNCTION_PATTERN = /\b(?:CREATE|ALTER)\s+(?:OR\s+REPLACE\s+)?(?:FUNCTION|PROCEDURE)\b/gi;
13
+ var IDEMPOTENT_SQL_PATH_PATTERN = /(?:^|[\\/])supabase[\\/]schemas[\\/]idempotent[\\/]/i;
14
+ var DO_TABLE_REFERENCE_PATTERN = /\b(?:FROM|JOIN|INSERT\s+INTO|UPDATE(?!\s+USING\b)|DELETE\s+FROM|ALTER\s+TABLE|TRUNCATE(?:\s+TABLE)?)\s+(?:(?:"([^"]+)"|([A-Za-z_][A-Za-z0-9_]*))\.)?(?:"([^"]+)"|([A-Za-z_][A-Za-z0-9_]*))/gi;
15
+ var DO_TABLE_GUARD_PATTERNS = [
16
+ /\bpg_class\b/i,
17
+ /\binformation_schema\s*\.\s*tables\b/i,
18
+ /\bto_regclass\s*\(/i,
19
+ /\bpg_table_is_visible\s*\(/i
20
+ ];
21
+ var NON_APP_SCHEMAS = /* @__PURE__ */ new Set([
22
+ "pg_catalog",
23
+ "information_schema",
24
+ "pg_toast",
25
+ "pg_temp",
26
+ "pg_class",
27
+ "pg_namespace",
28
+ "pg_attribute",
29
+ "pg_proc",
30
+ "pg_roles",
31
+ "auth",
32
+ "storage",
33
+ "realtime",
34
+ "supabase_migrations",
35
+ "supabase_functions",
36
+ "extensions",
37
+ "graphql",
38
+ "graphql_public",
39
+ "net",
40
+ "pgsodium",
41
+ "pgsodium_masks",
42
+ "vault",
43
+ "pgbouncer",
44
+ "cron",
45
+ "pgtap",
46
+ "tests"
47
+ ]);
48
+ var NON_APP_RELATIONS = /* @__PURE__ */ new Set([
49
+ "schemata",
50
+ "tables",
51
+ "columns",
52
+ "views",
53
+ "routines",
54
+ "parameters",
55
+ "constraints",
56
+ "triggers"
57
+ ]);
58
+ var sqlParserUtilsPromise = null;
59
+ function getSqlParserUtils() {
60
+ sqlParserUtilsPromise ??= import('@runa-ai/runa/ast').then(async ({ loadSqlParserUtils }) => {
61
+ const loaded = await loadSqlParserUtils();
62
+ if (typeof loaded.parseSql === "function") {
63
+ return loaded;
64
+ }
65
+ throw new Error("SQL parser utilities are unavailable or missing parseSql()");
66
+ });
67
+ return sqlParserUtilsPromise;
68
+ }
69
+ function extractStringValue(node) {
70
+ if (!node) return void 0;
71
+ const strNode = node.String;
72
+ if (typeof strNode === "object" && strNode !== null) {
73
+ return strNode.sval;
74
+ }
75
+ return node.str;
76
+ }
77
+ function extractBodyFromDefElements(items, statementType) {
78
+ if (!items) return void 0;
79
+ for (const item of items) {
80
+ if (typeof item !== "object" || item === null) continue;
81
+ const defElem = item.DefElem;
82
+ if (!defElem || defElem.defname !== "as") continue;
83
+ const arg = defElem.arg;
84
+ if (!arg) continue;
85
+ if (statementType === "do") {
86
+ return extractStringValue(arg);
87
+ }
88
+ const list = arg.List;
89
+ const values = list?.items?.flatMap((entry) => {
90
+ if (typeof entry !== "object" || entry === null) return [];
91
+ const stringValue = extractStringValue(entry);
92
+ return stringValue ? [stringValue] : [];
93
+ });
94
+ if (values && values.length > 0) {
95
+ return values.join("\n");
96
+ }
97
+ }
98
+ return void 0;
99
+ }
100
+ function resolveStatementWindow(content, statementEntry, fallbackCursor) {
101
+ const start = typeof statementEntry.stmt_location === "number" && statementEntry.stmt_location >= 0 ? statementEntry.stmt_location : fallbackCursor;
102
+ const end = typeof statementEntry.stmt_len === "number" && statementEntry.stmt_len > 0 ? Math.min(content.length, start + statementEntry.stmt_len) : content.length;
103
+ return { start, end };
104
+ }
105
+ function locateBodyInStatement(statementText, body, statementType) {
106
+ const directIndex = statementText.indexOf(body);
107
+ if (directIndex !== -1) {
108
+ return {
109
+ bodyStart: directIndex,
110
+ bodyEnd: directIndex + body.length,
111
+ statementType
112
+ };
113
+ }
114
+ const fallbackRanges = statementType === "do" ? collectDoBodyRanges(statementText) : collectFunctionBodyRanges(statementText);
115
+ return fallbackRanges[0];
116
+ }
117
+ function detectAstStatementType(stmt) {
118
+ if (typeof stmt.DoStmt === "object" && stmt.DoStmt !== null) {
119
+ return "do";
120
+ }
121
+ if (typeof stmt.CreateFunctionStmt === "object" && stmt.CreateFunctionStmt !== null) {
122
+ return "function";
123
+ }
124
+ return null;
125
+ }
126
+ function extractAstStatementBody(stmt, statementType) {
127
+ if (statementType === "do") {
128
+ return extractBodyFromDefElements(
129
+ stmt.DoStmt.args,
130
+ statementType
131
+ );
132
+ }
133
+ return extractBodyFromDefElements(
134
+ stmt.CreateFunctionStmt.options,
135
+ statementType
136
+ );
137
+ }
138
+ function localizeBodyRange(content, statementEntry, fallbackCursor, body, statementType) {
139
+ const window = resolveStatementWindow(content, statementEntry, fallbackCursor);
140
+ const statementText = content.slice(window.start, window.end);
141
+ const localized = locateBodyInStatement(statementText, body, statementType);
142
+ return {
143
+ range: localized === void 0 ? void 0 : {
144
+ bodyStart: window.start + localized.bodyStart,
145
+ bodyEnd: window.start + localized.bodyEnd,
146
+ statementType
147
+ },
148
+ nextCursor: Math.max(window.end, fallbackCursor)
149
+ };
150
+ }
151
+ async function findFunctionAndDoBodiesWithAst(content) {
152
+ const parser = await getSqlParserUtils();
153
+ const parsed = await parser.parseSql(content);
154
+ if (!parsed || parsed.stmts.length === 0) return [];
155
+ const ranges = [];
156
+ let fallbackCursor = 0;
157
+ for (const statementEntry of parsed.stmts) {
158
+ const stmt = statementEntry.stmt;
159
+ const statementType = detectAstStatementType(stmt);
160
+ if (!statementType) {
161
+ const window = resolveStatementWindow(content, statementEntry, fallbackCursor);
162
+ fallbackCursor = Math.max(window.end, fallbackCursor);
163
+ continue;
164
+ }
165
+ const body = extractAstStatementBody(stmt, statementType);
166
+ const localized = body ? localizeBodyRange(content, statementEntry, fallbackCursor, body, statementType) : { range: void 0, nextCursor: fallbackCursor };
167
+ fallbackCursor = localized.nextCursor;
168
+ if (!body) continue;
169
+ if (localized.range) {
170
+ ranges.push(localized.range);
171
+ }
172
+ }
173
+ return ranges;
174
+ }
175
+ async function findFunctionAndDoBodies(content) {
176
+ try {
177
+ const astRanges = await findFunctionAndDoBodiesWithAst(content);
178
+ if (astRanges.length > 0) return astRanges;
179
+ } catch {
180
+ }
181
+ return [...collectDoBodyRanges(content), ...collectFunctionBodyRanges(content)];
182
+ }
183
+ function readDollarTag(content, index) {
184
+ const match = content.slice(index).match(/^\$([A-Za-z_][A-Za-z0-9_]*)?\$/);
185
+ return match ? match[0] : void 0;
186
+ }
187
+ function isWordChar(char) {
188
+ return /[A-Za-z0-9_]/.test(char);
189
+ }
190
+ function isKeywordAt(content, contentIndex, keyword) {
191
+ const endIndex = contentIndex + keyword.length;
192
+ if (endIndex > content.length) return false;
193
+ if (content.slice(contentIndex, endIndex).toUpperCase() !== keyword) return false;
194
+ const before = contentIndex > 0 ? content[contentIndex - 1] : " ";
195
+ const after = endIndex < content.length ? content[endIndex] : " ";
196
+ return !isWordChar(before) && !isWordChar(after);
197
+ }
198
+ function consumeSingleQuote(content, state) {
199
+ if (!state.inSingleQuote) return false;
200
+ if (content[state.cursor] === "'" && content[state.cursor + 1] === "'") {
201
+ state.cursor += 2;
202
+ return true;
203
+ }
204
+ if (content[state.cursor] === "'") {
205
+ state.inSingleQuote = false;
206
+ }
207
+ state.cursor += 1;
208
+ return true;
209
+ }
210
+ function consumeDoubleQuote(content, state) {
211
+ if (!state.inDoubleQuote) return false;
212
+ if (content[state.cursor] === '"' && content[state.cursor + 1] === '"') {
213
+ state.cursor += 2;
214
+ return true;
215
+ }
216
+ if (content[state.cursor] === '"') {
217
+ state.inDoubleQuote = false;
218
+ }
219
+ state.cursor += 1;
220
+ return true;
221
+ }
222
+ function consumeDollarQuote(content, state) {
223
+ if (state.inDollarQuote === void 0) return false;
224
+ const close = `$${state.inDollarQuote}$`;
225
+ if (content.startsWith(close, state.cursor)) {
226
+ state.inDollarQuote = void 0;
227
+ state.cursor += close.length;
228
+ return true;
229
+ }
230
+ state.cursor += 1;
231
+ return true;
232
+ }
233
+ function consumeQuotedSpan(content, state) {
234
+ if (consumeSingleQuote(content, state)) return true;
235
+ if (consumeDoubleQuote(content, state)) return true;
236
+ return consumeDollarQuote(content, state);
237
+ }
238
+ function tryStartSingleQuote(content, state) {
239
+ if (content[state.cursor] !== "'") return false;
240
+ state.inSingleQuote = true;
241
+ state.cursor += 1;
242
+ return true;
243
+ }
244
+ function tryStartDoubleQuote(content, state) {
245
+ if (content[state.cursor] !== '"') return false;
246
+ state.inDoubleQuote = true;
247
+ state.cursor += 1;
248
+ return true;
249
+ }
250
+ function tryStartDollarQuote(content, state) {
251
+ const dollarTag = readDollarTag(content, state.cursor);
252
+ if (dollarTag === void 0) return false;
253
+ state.inDollarQuote = dollarTag.slice(1, -1);
254
+ state.cursor += dollarTag.length;
255
+ return true;
256
+ }
257
+ function tryStartQuotedSpan(content, state) {
258
+ if (tryStartSingleQuote(content, state)) return true;
259
+ if (tryStartDoubleQuote(content, state)) return true;
260
+ return tryStartDollarQuote(content, state);
261
+ }
262
+ function findKeywordFromOffset(content, start, keyword) {
263
+ const state = {
264
+ cursor: start,
265
+ inSingleQuote: false,
266
+ inDoubleQuote: false,
267
+ inDollarQuote: void 0
268
+ };
269
+ while (state.cursor < content.length) {
270
+ if (consumeQuotedSpan(content, state)) continue;
271
+ if (tryStartQuotedSpan(content, state)) continue;
272
+ if (isKeywordAt(content, state.cursor, keyword)) {
273
+ return state.cursor;
274
+ }
275
+ state.cursor += 1;
276
+ }
277
+ return void 0;
278
+ }
279
+ var MAX_DOLLAR_QUOTE_DEPTH = 50;
280
+ function skipUntilDollarClose(content, state, openTag, depth) {
281
+ if (depth >= MAX_DOLLAR_QUOTE_DEPTH) return -1;
282
+ const nestedTag = readDollarTag(content, state.cursor);
283
+ if (nestedTag === void 0 || nestedTag === openTag) {
284
+ return state.cursor;
285
+ }
286
+ const nestedStart = state.cursor + nestedTag.length;
287
+ const nestedEnd = closeIndexForDollarTag(content, nestedStart, nestedTag, depth + 1);
288
+ if (nestedEnd === -1) return -1;
289
+ state.cursor = nestedEnd + nestedTag.length;
290
+ return state.cursor;
291
+ }
292
+ function closeIndexForDollarTag(content, bodyStart, openTag, depth = 0) {
293
+ if (depth >= MAX_DOLLAR_QUOTE_DEPTH) return -1;
294
+ const state = {
295
+ cursor: bodyStart,
296
+ inSingleQuote: false,
297
+ inDoubleQuote: false,
298
+ inDollarQuote: void 0
299
+ };
300
+ while (state.cursor < content.length) {
301
+ if (consumeQuotedSpan(content, state)) continue;
302
+ if (content.startsWith(openTag, state.cursor)) {
303
+ return state.cursor;
304
+ }
305
+ const nestedCursor = skipUntilDollarClose(content, state, openTag, depth);
306
+ if (nestedCursor !== state.cursor) {
307
+ continue;
308
+ }
309
+ if (tryStartSingleQuote(content, state)) continue;
310
+ if (tryStartDoubleQuote(content, state)) continue;
311
+ state.cursor += 1;
312
+ }
313
+ return -1;
314
+ }
315
+ function resolveBodyRange(content, openTagStart, statementType) {
316
+ const openTag = readDollarTag(content, openTagStart);
317
+ if (openTag === void 0) return void 0;
318
+ const bodyStart = openTagStart + openTag.length;
319
+ const bodyEnd = closeIndexForDollarTag(content, bodyStart, openTag);
320
+ if (bodyEnd === -1) return void 0;
321
+ return {
322
+ range: { bodyStart, bodyEnd, statementType },
323
+ nextScanIndex: bodyEnd + openTag.length
324
+ };
325
+ }
326
+ function skipDoLanguageClause(content, cursor) {
327
+ if (!isKeywordAt(content, cursor, "LANGUAGE")) {
328
+ return cursor;
329
+ }
330
+ const langNameStart = skipWhitespace(content, cursor + "LANGUAGE".length);
331
+ const afterLangName = skipIdentifier(content, langNameStart);
332
+ return skipWhitespace(content, afterLangName);
333
+ }
334
+ function collectDoBodyRanges(content) {
335
+ const ranges = [];
336
+ DO_STATEMENT_PATTERN.lastIndex = 0;
337
+ let doMatch = DO_STATEMENT_PATTERN.exec(content);
338
+ while (doMatch !== null) {
339
+ const doStart = doMatch.index;
340
+ if (findKeywordFromOffset(content, doStart, "DO") !== doStart) {
341
+ DO_STATEMENT_PATTERN.lastIndex = doStart + doMatch[0].length;
342
+ doMatch = DO_STATEMENT_PATTERN.exec(content);
343
+ continue;
344
+ }
345
+ let cursor = skipWhitespace(content, doStart + doMatch[0].length);
346
+ cursor = skipDoLanguageClause(content, cursor);
347
+ const resolved = resolveBodyRange(content, cursor, "do");
348
+ if (resolved !== void 0) {
349
+ ranges.push(resolved.range);
350
+ DO_STATEMENT_PATTERN.lastIndex = resolved.nextScanIndex;
351
+ }
352
+ doMatch = DO_STATEMENT_PATTERN.exec(content);
353
+ }
354
+ DO_STATEMENT_PATTERN.lastIndex = 0;
355
+ return ranges;
356
+ }
357
+ function collectFunctionBodyRanges(content) {
358
+ const ranges = [];
359
+ CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = 0;
360
+ let functionMatch = CREATE_OR_ALTER_FUNCTION_PATTERN.exec(content);
361
+ while (functionMatch !== null) {
362
+ const start = functionMatch.index;
363
+ const asIndex = findKeywordFromOffset(content, start + functionMatch[0].length, "AS");
364
+ if (asIndex === void 0) {
365
+ CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = start + functionMatch[0].length;
366
+ functionMatch = CREATE_OR_ALTER_FUNCTION_PATTERN.exec(content);
367
+ continue;
368
+ }
369
+ const bodyStartAt = skipWhitespace(content, asIndex + 2);
370
+ const resolved = resolveBodyRange(content, bodyStartAt, "function");
371
+ if (resolved !== void 0) {
372
+ ranges.push(resolved.range);
373
+ CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = resolved.nextScanIndex;
374
+ }
375
+ functionMatch = CREATE_OR_ALTER_FUNCTION_PATTERN.exec(content);
376
+ }
377
+ CREATE_OR_ALTER_FUNCTION_PATTERN.lastIndex = 0;
378
+ return ranges;
379
+ }
380
+ function enrichStaticExecuteRiskFromSql(sql, sourceLine) {
381
+ const staticSql = stripSqlForPatternMatching(sql);
382
+ if (!staticSql.trim()) return [];
383
+ const staticSqlLineStarts = buildLineStarts(sql);
384
+ return detectRisksFromContent(staticSql, sql, staticSqlLineStarts).map((risk) => ({
385
+ ...risk,
386
+ line: sourceLine,
387
+ evidence: {
388
+ source: "static execute body",
389
+ snippet: sql.trim().slice(0, 200),
390
+ detail: "Reconstructed PL/pgSQL EXECUTE expression"
391
+ }
392
+ }));
393
+ }
394
+ function buildDoBlockRisk(commentlessContent, range, declarationLine) {
395
+ const bodyPreviewStart = Math.max(0, range.bodyStart - 80);
396
+ const bodyPreviewEnd = range.bodyEnd + 80;
397
+ return {
398
+ level: "medium",
399
+ description: "DO block detected in SQL",
400
+ mitigation: "Prefer static declarative SQL or move bootstrap logic to idempotent script files with clear intent",
401
+ line: declarationLine,
402
+ reasonCode: "PLPGSQL_DO_BLOCK_DETECTED",
403
+ confidence: "medium",
404
+ evidence: {
405
+ source: "findFunctionAndDoBodies",
406
+ snippet: range.statementType === "do" ? commentlessContent.slice(bodyPreviewStart, bodyPreviewEnd) : void 0,
407
+ detail: `${range.statementType.toUpperCase()} block detected in schema SQL`
408
+ }
409
+ };
410
+ }
411
+ function maybeAddDoBlockRisk(risks, dedupe, commentlessContent, range, declarationLine) {
412
+ if (range.statementType !== "do") return;
413
+ const doKey = `do:${range.statementType}:${declarationLine}`;
414
+ if (dedupe.has(doKey)) return;
415
+ dedupe.add(doKey);
416
+ risks.push(buildDoBlockRisk(commentlessContent, range, declarationLine));
417
+ }
418
+ function isIdempotentSchemaFile(filePath) {
419
+ return filePath !== void 0 && IDEMPOTENT_SQL_PATH_PATTERN.test(filePath);
420
+ }
421
+ function doBodyContainsGuard(body) {
422
+ return DO_TABLE_GUARD_PATTERNS.some((pattern) => pattern.test(body));
423
+ }
424
+ function doBodyReferencesApplicationTables(body) {
425
+ const searchableBody = stripSqlStringsPreserveLines(stripSqlCommentsPreserveLines(body)).replace(
426
+ /^\s*(?:GRANT|REVOKE)\b[\s\S]*?;/gim,
427
+ ""
428
+ );
429
+ DO_TABLE_REFERENCE_PATTERN.lastIndex = 0;
430
+ let match = DO_TABLE_REFERENCE_PATTERN.exec(searchableBody);
431
+ while (match !== null) {
432
+ const schema = (match[1] ?? match[2] ?? "").toLowerCase();
433
+ const relation = (match[3] ?? match[4] ?? "").toLowerCase();
434
+ if (schema.length === 0) {
435
+ if (!relation.startsWith("pg_") && !NON_APP_SCHEMAS.has(relation) && !NON_APP_RELATIONS.has(relation)) {
436
+ return true;
437
+ }
438
+ match = DO_TABLE_REFERENCE_PATTERN.exec(searchableBody);
439
+ continue;
440
+ }
441
+ if (!NON_APP_SCHEMAS.has(schema)) {
442
+ return true;
443
+ }
444
+ match = DO_TABLE_REFERENCE_PATTERN.exec(searchableBody);
445
+ }
446
+ return false;
447
+ }
448
+ function maybeBuildIdempotentDoGuardRisk(body, declarationLine, filePath) {
449
+ if (!isIdempotentSchemaFile(filePath)) return void 0;
450
+ if (!doBodyReferencesApplicationTables(body)) return void 0;
451
+ if (doBodyContainsGuard(body)) return void 0;
452
+ return {
453
+ level: "medium",
454
+ description: "Idempotent DO block references application tables without an existence guard for 2-pass execution",
455
+ mitigation: "Add a table-existence guard using pg_class, information_schema.tables, or to_regclass() before referencing declarative tables inside the DO block.",
456
+ line: declarationLine,
457
+ reasonCode: "IDEMPOTENT_DO_TABLE_REFERENCE_WITHOUT_GUARD",
458
+ confidence: "medium",
459
+ evidence: {
460
+ source: "findFunctionAndDoBodies",
461
+ snippet: body.trim().slice(0, 200),
462
+ detail: "DO block references app tables but no recognizable table-existence guard was found"
463
+ }
464
+ };
465
+ }
466
+ function applyStatementAssignment(statement, stringEnv) {
467
+ const assignment = parseTopLevelAssignment(statement.statement);
468
+ if (!assignment) return;
469
+ const resolved = extractStaticSqlFromExpression(assignment.expression, stringEnv);
470
+ if (resolved.kind !== "static") {
471
+ stringEnv.delete(assignment.target);
472
+ return;
473
+ }
474
+ if (assignment.isConditionalContext) {
475
+ mergeStringEnvValue(stringEnv, assignment.target, resolved.sql);
476
+ return;
477
+ }
478
+ stringEnv.set(assignment.target, resolved.sql);
479
+ }
480
+ function buildExecuteLine(content, lineStarts, range, statement, execute) {
481
+ return lineNumberFromIndex(
482
+ content,
483
+ range.bodyStart + statement.startOffset + execute.statementStartOffset,
484
+ lineStarts
485
+ );
486
+ }
487
+ function buildExecuteKey(range, statement, execute, executeLine) {
488
+ return `execute:${range.statementType}:${statement.startOffset}:${execute.statementStartOffset}:${executeLine}`;
489
+ }
490
+ function buildStaticExecuteRisk(execute, executeLine) {
491
+ return {
492
+ level: "medium",
493
+ description: "Static EXECUTE SQL detected in PL/pgSQL body",
494
+ mitigation: "Prefer declarative/static SQL when possible; keep this change explicit in declarative schemas",
495
+ line: executeLine,
496
+ reasonCode: "PLPGSQL_EXECUTE_STATIC",
497
+ confidence: "low",
498
+ evidence: {
499
+ source: "extractExecuteExpressions",
500
+ snippet: execute.expression.slice(0, 200),
501
+ detail: "Static EXECUTE SQL detected from reconstructed expression"
502
+ }
503
+ };
504
+ }
505
+ function pushDerivedStaticRisks(risks, dedupe, staticSql, executeLine, hasUsingClause) {
506
+ const staticRisks = enrichStaticExecuteRiskFromSql(staticSql, executeLine);
507
+ for (const staticRisk of staticRisks) {
508
+ const key = `${staticRisk.level}:${staticRisk.description}:${executeLine}`;
509
+ const dedupeKey = `static:${key}`;
510
+ if (dedupe.has(dedupeKey)) continue;
511
+ dedupe.add(dedupeKey);
512
+ risks.push({
513
+ ...staticRisk,
514
+ reasonCode: staticRisk.reasonCode ?? "PLPGSQL_EXECUTE_STATIC",
515
+ confidence: staticRisk.confidence ?? "low",
516
+ evidence: {
517
+ ...staticRisk.evidence,
518
+ source: staticRisk.evidence?.source ?? "detectRisksFromContent",
519
+ detail: `Static EXECUTE SQL analyzed from reconstructed expression${hasUsingClause ? " (USING clause present)" : ""}`
520
+ }
521
+ });
522
+ }
523
+ }
524
+ function maybeBuildUsingClauseRisk(execute, executeLine) {
525
+ if (!execute.hasUsingClause) return void 0;
526
+ return {
527
+ level: "high",
528
+ description: "PL/pgSQL EXECUTE uses USING bindings; values are applied at runtime and must be auditable",
529
+ mitigation: "Avoid runtime-bound SQL mutation in migration paths, or keep this change in idempotent with explicit review",
530
+ line: executeLine,
531
+ reasonCode: "PLPGSQL_EXECUTE_DYNAMIC_USING",
532
+ confidence: "high",
533
+ evidence: {
534
+ source: "extractExecuteExpressions",
535
+ snippet: execute.expression.slice(0, 200),
536
+ detail: "EXECUTE contains USING clause"
537
+ }
538
+ };
539
+ }
540
+ function pushBodyStaticRisks(risks, dedupe, body, bodyStartLine) {
541
+ const searchableBody = stripSqlStringsPreserveLines(body);
542
+ if (!searchableBody.trim()) return;
543
+ const bodyLineStarts = buildLineStarts(body);
544
+ const bodyRisks = detectRisksFromContent(searchableBody, body, bodyLineStarts);
545
+ for (const risk of bodyRisks) {
546
+ const line = bodyStartLine + ((risk.line ?? 1) - 1);
547
+ const dedupeKey = `body:${risk.reasonCode ?? risk.description}:${line}`;
548
+ if (dedupe.has(dedupeKey)) continue;
549
+ dedupe.add(dedupeKey);
550
+ risks.push({
551
+ ...risk,
552
+ line,
553
+ evidence: {
554
+ source: "plpgsql body",
555
+ snippet: body.trim().slice(0, 200),
556
+ detail: `Matched ${risk.reasonCode ?? "pattern"} inside PL/pgSQL body`
557
+ }
558
+ });
559
+ }
560
+ }
561
+ function buildUnresolvedExecuteRisk(execute, executeLine, reason) {
562
+ const clauseSuffix = execute.hasUsingClause ? " includes USING clause" : execute.hasIntoClause ? " includes INTO clause" : "";
563
+ return {
564
+ level: "high",
565
+ description: `UNRESOLVED_DYNAMIC_SQL in PL/PGSQL EXECUTE: ${reason}${clauseSuffix}`,
566
+ mitigation: "Resolve SQL construction at migration time or add manual review before production apply preview",
567
+ line: executeLine,
568
+ reasonCode: execute.hasUsingClause ? "PLPGSQL_EXECUTE_DYNAMIC_USING" : execute.hasIntoClause ? "PLPGSQL_EXECUTE_DYNAMIC_INTO" : "PLPGSQL_EXECUTE_UNRESOLVED",
569
+ confidence: "high",
570
+ evidence: {
571
+ source: "extractStaticSqlFromExpression",
572
+ snippet: execute.expression.slice(0, 200),
573
+ detail: `Failed to reconstruct dynamic SQL (${reason})`
574
+ }
575
+ };
576
+ }
577
+ function analyzeExecuteStatement(risks, dedupe, content, lineStarts, range, statement, execute, stringEnv) {
578
+ const executeLine = buildExecuteLine(content, lineStarts, range, statement, execute);
579
+ const executeKey = buildExecuteKey(range, statement, execute, executeLine);
580
+ if (dedupe.has(executeKey)) return;
581
+ dedupe.add(executeKey);
582
+ const extracted = extractStaticSqlFromExpression(execute.expression, stringEnv);
583
+ if (extracted.kind !== "static") {
584
+ risks.push(buildUnresolvedExecuteRisk(execute, executeLine, extracted.reason));
585
+ return;
586
+ }
587
+ risks.push(buildStaticExecuteRisk(execute, executeLine));
588
+ pushDerivedStaticRisks(risks, dedupe, extracted.sql, executeLine, execute.hasUsingClause);
589
+ const usingRisk = maybeBuildUsingClauseRisk(execute, executeLine);
590
+ if (usingRisk !== void 0) {
591
+ risks.push(usingRisk);
592
+ }
593
+ }
594
+ function analyzeBodyRange(risks, dedupe, content, commentlessContent, lineStarts, range, options) {
595
+ const declarationLine = lineNumberFromIndex(commentlessContent, range.bodyStart, lineStarts);
596
+ maybeAddDoBlockRisk(risks, dedupe, commentlessContent, range, declarationLine);
597
+ const body = commentlessContent.slice(range.bodyStart, range.bodyEnd);
598
+ const doGuardRisk = maybeBuildIdempotentDoGuardRisk(body, declarationLine, options?.filePath);
599
+ if (doGuardRisk !== void 0) {
600
+ const key = `guard:${declarationLine}:${doGuardRisk.reasonCode}`;
601
+ if (!dedupe.has(key)) {
602
+ dedupe.add(key);
603
+ risks.push(doGuardRisk);
604
+ }
605
+ }
606
+ pushBodyStaticRisks(risks, dedupe, body, declarationLine);
607
+ const stringEnv = /* @__PURE__ */ new Map();
608
+ for (const statement of splitPlpgsqlStatementsWithOffsets(body)) {
609
+ applyStatementAssignment(statement, stringEnv);
610
+ const executeStatements = extractExecuteExpressions(statement.statement);
611
+ for (const execute of executeStatements) {
612
+ analyzeExecuteStatement(
613
+ risks,
614
+ dedupe,
615
+ content,
616
+ lineStarts,
617
+ range,
618
+ statement,
619
+ execute,
620
+ stringEnv
621
+ );
622
+ }
623
+ }
624
+ }
625
+ async function detectPlpgsqlDynamicExecutionRisks(content, options) {
626
+ const commentlessContent = stripSqlCommentsPreserveLines(content);
627
+ const lineStarts = buildLineStarts(commentlessContent);
628
+ const ranges = await findFunctionAndDoBodies(commentlessContent);
629
+ if (ranges.length === 0) return [];
630
+ const risks = [];
631
+ const dedupe = /* @__PURE__ */ new Set();
632
+ for (const range of ranges) {
633
+ analyzeBodyRange(risks, dedupe, content, commentlessContent, lineStarts, range, options);
634
+ }
635
+ return risks;
636
+ }
637
+
638
+ export { detectPlpgsqlDynamicExecutionRisks };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'module';
3
- import { getErrorMessage } from './chunk-EMB6IZFT.js';
3
+ import { getErrorMessage } from './chunk-XVNDDHAF.js';
4
4
  import { findRepoRoot } from './chunk-3WDV32GA.js';
5
5
  import { createMachineStateChangeLogger } from './chunk-5FT3F36G.js';
6
6
  import { getOutputFormatFromEnv } from './chunk-HKUWEGUX.js';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'module';
3
- import { fetchTemplates } from './chunk-CCW3PLQY.js';
3
+ import { fetchTemplates } from './chunk-Z7A4BEWF.js';
4
4
  import { updateRunaConfigSdkVersion } from './chunk-6AALH2ED.js';
5
5
  import './chunk-DRSUEMAK.js';
6
6
  import './chunk-RZLYEO4U.js';