@scopebond/hook 0.8.1 → 0.10.0

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 (110) hide show
  1. package/README.md +239 -0
  2. package/dist/budget-load.d.ts +68 -0
  3. package/dist/budget-load.d.ts.map +1 -0
  4. package/dist/budget-load.js +164 -0
  5. package/dist/budget-load.js.map +1 -0
  6. package/dist/capabilities.d.ts +103 -0
  7. package/dist/capabilities.d.ts.map +1 -0
  8. package/dist/capabilities.js +212 -0
  9. package/dist/capabilities.js.map +1 -0
  10. package/dist/classify.d.ts +17 -0
  11. package/dist/classify.d.ts.map +1 -0
  12. package/dist/classify.js +78 -0
  13. package/dist/classify.js.map +1 -0
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +585 -13
  16. package/dist/cli.js.map +1 -1
  17. package/dist/cloud.d.ts +8 -0
  18. package/dist/cloud.d.ts.map +1 -1
  19. package/dist/cloud.js.map +1 -1
  20. package/dist/dispatch-cli.d.ts +2 -0
  21. package/dist/dispatch-cli.d.ts.map +1 -0
  22. package/dist/dispatch-cli.js +128 -0
  23. package/dist/dispatch-cli.js.map +1 -0
  24. package/dist/explain.d.ts.map +1 -1
  25. package/dist/explain.js +2 -1
  26. package/dist/explain.js.map +1 -1
  27. package/dist/group.d.ts +12 -0
  28. package/dist/group.d.ts.map +1 -0
  29. package/dist/group.js +55 -0
  30. package/dist/group.js.map +1 -0
  31. package/dist/index.d.ts +29 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +19 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/install.d.ts +10 -0
  36. package/dist/install.d.ts.map +1 -1
  37. package/dist/install.js +56 -0
  38. package/dist/install.js.map +1 -1
  39. package/dist/managed.d.ts +69 -0
  40. package/dist/managed.d.ts.map +1 -0
  41. package/dist/managed.js +184 -0
  42. package/dist/managed.js.map +1 -0
  43. package/dist/map.d.ts +4 -0
  44. package/dist/map.d.ts.map +1 -1
  45. package/dist/map.js +29 -1
  46. package/dist/map.js.map +1 -1
  47. package/dist/obs-emitter.d.ts +132 -0
  48. package/dist/obs-emitter.d.ts.map +1 -0
  49. package/dist/obs-emitter.js +415 -0
  50. package/dist/obs-emitter.js.map +1 -0
  51. package/dist/obs-store.d.ts +160 -0
  52. package/dist/obs-store.d.ts.map +1 -0
  53. package/dist/obs-store.js +367 -0
  54. package/dist/obs-store.js.map +1 -0
  55. package/dist/obs-upload.d.ts +24 -0
  56. package/dist/obs-upload.d.ts.map +1 -0
  57. package/dist/obs-upload.js +231 -0
  58. package/dist/obs-upload.js.map +1 -0
  59. package/dist/observation.d.ts +232 -0
  60. package/dist/observation.d.ts.map +1 -0
  61. package/dist/observation.js +308 -0
  62. package/dist/observation.js.map +1 -0
  63. package/dist/paths.d.ts +15 -0
  64. package/dist/paths.d.ts.map +1 -0
  65. package/dist/paths.js +79 -0
  66. package/dist/paths.js.map +1 -0
  67. package/dist/policy-load.d.ts +60 -0
  68. package/dist/policy-load.d.ts.map +1 -0
  69. package/dist/policy-load.js +151 -0
  70. package/dist/policy-load.js.map +1 -0
  71. package/dist/policy-sync.d.ts +45 -0
  72. package/dist/policy-sync.d.ts.map +1 -0
  73. package/dist/policy-sync.js +171 -0
  74. package/dist/policy-sync.js.map +1 -0
  75. package/dist/proof.d.ts +30 -0
  76. package/dist/proof.d.ts.map +1 -0
  77. package/dist/proof.js +186 -0
  78. package/dist/proof.js.map +1 -0
  79. package/dist/rules.d.ts +20 -0
  80. package/dist/rules.d.ts.map +1 -1
  81. package/dist/rules.js +50 -0
  82. package/dist/rules.js.map +1 -1
  83. package/dist/runtime.d.ts +21 -1
  84. package/dist/runtime.d.ts.map +1 -1
  85. package/dist/runtime.js +77 -10
  86. package/dist/runtime.js.map +1 -1
  87. package/dist/scan.d.ts +7 -0
  88. package/dist/scan.d.ts.map +1 -0
  89. package/dist/scan.js +38 -0
  90. package/dist/scan.js.map +1 -0
  91. package/dist/shell.d.ts.map +1 -1
  92. package/dist/shell.js +12 -3
  93. package/dist/shell.js.map +1 -1
  94. package/dist/sql-classify.d.ts +11 -0
  95. package/dist/sql-classify.d.ts.map +1 -0
  96. package/dist/sql-classify.js +251 -0
  97. package/dist/sql-classify.js.map +1 -0
  98. package/dist/typed-infra.d.ts +73 -0
  99. package/dist/typed-infra.d.ts.map +1 -0
  100. package/dist/typed-infra.js +758 -0
  101. package/dist/typed-infra.js.map +1 -0
  102. package/dist/typed-ops.d.ts +140 -0
  103. package/dist/typed-ops.d.ts.map +1 -0
  104. package/dist/typed-ops.js +549 -0
  105. package/dist/typed-ops.js.map +1 -0
  106. package/dist/vectors.d.ts +40 -0
  107. package/dist/vectors.d.ts.map +1 -0
  108. package/dist/vectors.js +219 -0
  109. package/dist/vectors.js.map +1 -0
  110. package/package.json +5 -5
@@ -0,0 +1,251 @@
1
+ // A conservative SQL classifier for the typed `database` operation.
2
+ //
3
+ // It reads a statement only to decide its verb and whether an UPDATE or DELETE names a
4
+ // selective predicate. Its result is two closed values; the text, the table names, the
5
+ // column names, the literals and the rows never leave this function. It is a classifier for
6
+ // a coding agent's own command line, not a parser: a statement it cannot place with
7
+ // confidence makes the whole input `null` (the caller then records no database operation and
8
+ // the command stays a plain shell action), and an UPDATE or DELETE whose predicate it cannot
9
+ // show to be selective is reported as covering `all` rows. Unknown is never reported clean.
10
+ //
11
+ // Known limits, on purpose: a function call inside a SELECT can have effects (`SELECT
12
+ // pg_terminate_backend(...)`), dynamic SQL and procedural blocks are not read, and a predicate
13
+ // that is selective in form but matches every row at run time (`WHERE id > -1`) is bounded.
14
+ const MAX_SQL = 1_000_000;
15
+ const SEVERITY = { read: 0, insert: 1, create: 2, update: 3, delete: 4, alter: 5, delete_all: 6, drop: 7 };
16
+ /** Comments removed, string literals reduced to `'s'` (or kept as `'%'` when only wildcards),
17
+ * quoted identifiers unquoted. Null when a quote or comment is left open or a dollar-quoted body is present. */
18
+ function clean(text) {
19
+ let out = "";
20
+ for (let i = 0; i < text.length;) {
21
+ const c = text[i];
22
+ const next = text[i + 1];
23
+ if (c === "-" && next === "-") {
24
+ const e = text.indexOf("\n", i);
25
+ i = e < 0 ? text.length : e;
26
+ out += " ";
27
+ continue;
28
+ }
29
+ if (c === "/" && next === "*") {
30
+ const e = text.indexOf("*/", i + 2);
31
+ if (e < 0)
32
+ return null;
33
+ i = e + 2;
34
+ out += " ";
35
+ continue;
36
+ }
37
+ if (c === "'") {
38
+ let j = i + 1;
39
+ let body = "";
40
+ for (;;) {
41
+ if (j >= text.length)
42
+ return null;
43
+ if (text[j] === "'" && text[j + 1] === "'") {
44
+ body += "'";
45
+ j += 2;
46
+ continue;
47
+ }
48
+ if (text[j] === "'")
49
+ break;
50
+ body += text[j++];
51
+ }
52
+ out += /^[%_]+$/.test(body) ? `'${body}'` : "'s'";
53
+ i = j + 1;
54
+ continue;
55
+ }
56
+ if (c === '"' || c === "`") {
57
+ const e = text.indexOf(c, i + 1);
58
+ if (e < 0)
59
+ return null;
60
+ out += ` ${text.slice(i + 1, e).replace(/[^A-Za-z0-9_]/g, "_")} `;
61
+ i = e + 1;
62
+ continue;
63
+ }
64
+ if (c === "[" && /^\[[A-Za-z0-9_ ]+\]/.test(text.slice(i))) {
65
+ const e = text.indexOf("]", i);
66
+ out += ` ${text.slice(i + 1, e).replace(/ /g, "_")} `;
67
+ i = e + 1;
68
+ continue;
69
+ }
70
+ if (c === "$" && (next === "$" || /[A-Za-z_]/.test(next ?? "")))
71
+ return null; // dollar-quoted body
72
+ out += c;
73
+ i++;
74
+ }
75
+ return out;
76
+ }
77
+ /** Split on `;` outside parentheses. */
78
+ function statements(text) {
79
+ const out = [];
80
+ let depth = 0;
81
+ let cur = "";
82
+ for (const c of text) {
83
+ if (c === "(")
84
+ depth++;
85
+ else if (c === ")")
86
+ depth = Math.max(0, depth - 1);
87
+ if (c === ";" && depth === 0) {
88
+ out.push(cur);
89
+ cur = "";
90
+ continue;
91
+ }
92
+ cur += c;
93
+ }
94
+ out.push(cur);
95
+ return out.map((s) => s.trim()).filter((s) => s !== "");
96
+ }
97
+ /** Split `text` at a top-level (depth 0) whole-word keyword; returns the parts. */
98
+ function splitTop(text, word) {
99
+ const parts = [];
100
+ const re = new RegExp(`^${word}(?![A-Za-z0-9_])`, "i");
101
+ let depth = 0;
102
+ let last = 0;
103
+ for (let i = 0; i < text.length; i++) {
104
+ const c = text[i];
105
+ if (c === "(")
106
+ depth++;
107
+ else if (c === ")")
108
+ depth = Math.max(0, depth - 1);
109
+ else if (depth === 0 && (i === 0 || /[^A-Za-z0-9_]/.test(text[i - 1])) && re.test(text.slice(i))) {
110
+ parts.push(text.slice(last, i));
111
+ last = i + word.length;
112
+ i = last - 1;
113
+ }
114
+ }
115
+ parts.push(text.slice(last));
116
+ return parts;
117
+ }
118
+ const LITERAL = /^(?:'[^']*'|-?\d+(?:\.\d+)?|true|false|null)$/i;
119
+ function stripParens(s) {
120
+ let t = s.trim();
121
+ while (t.startsWith("(") && t.endsWith(")")) {
122
+ let depth = 0;
123
+ let closesAtEnd = true;
124
+ for (let i = 0; i < t.length; i++) {
125
+ if (t[i] === "(")
126
+ depth++;
127
+ else if (t[i] === ")") {
128
+ depth--;
129
+ if (depth === 0 && i < t.length - 1) {
130
+ closesAtEnd = false;
131
+ break;
132
+ }
133
+ }
134
+ }
135
+ if (!closesAtEnd)
136
+ break;
137
+ t = t.slice(1, -1).trim();
138
+ }
139
+ return t;
140
+ }
141
+ /** Whether one AND-ed condition narrows the rows: a comparison, IN, EXISTS or IS NULL that is
142
+ * neither a tautology nor a bare `IS NOT NULL`. */
143
+ function selective(raw) {
144
+ const c = stripParens(raw).replace(/\s+/g, " ");
145
+ if (c === "")
146
+ return false;
147
+ if (splitTop(c, "or").length > 1)
148
+ return false;
149
+ if (splitTop(c, "and").length > 1)
150
+ return splitTop(c, "and").some((p) => selective(p));
151
+ if (/^(?:true|1|not false|not 0)$/i.test(c))
152
+ return false;
153
+ if (/\bis not null$/i.test(c))
154
+ return false;
155
+ if (/between_r$/i.test(c))
156
+ return true;
157
+ if (/^(?:not )?exists\b/i.test(c) || /\bis null$/i.test(c))
158
+ return true;
159
+ const m = /^(.+?) ?(==|=|<>|!=|<=|>=|<|>|\bnot like\b|\bnot ilike\b|\blike\b|\bilike\b|\bnot in\b|\bin\b|\bbetween_r\b|\bis not\b|\bis\b) ?(.+)$/i.exec(c);
160
+ if (!m)
161
+ return false;
162
+ const left = m[1].trim();
163
+ const right = m[3].trim();
164
+ if (LITERAL.test(left) && LITERAL.test(right))
165
+ return false;
166
+ if (left.toLowerCase() === right.toLowerCase())
167
+ return false;
168
+ if (/like$/i.test(m[2]) && /^'[%_]+'$/.test(right))
169
+ return false;
170
+ return true;
171
+ }
172
+ /** The predicate of an UPDATE or DELETE, `bounded` only when a top-level AND-ed condition is selective. */
173
+ function predicateOf(stmt) {
174
+ const parts = splitTop(stmt, "where");
175
+ if (parts.length < 2)
176
+ return "all";
177
+ let where = parts.slice(1).join(" ");
178
+ where = where.replace(/\b(?:returning|order by|limit)\b[\s\S]*$/i, "");
179
+ where = where.replace(/\bbetween\s+\S+\s+and\s+\S+/gi, "between_r");
180
+ if (splitTop(where, "or").length > 1)
181
+ return "all";
182
+ return splitTop(where, "and").some((p) => selective(p)) ? "bounded" : "all";
183
+ }
184
+ const NEUTRAL = new Set(["BEGIN", "COMMIT", "ROLLBACK", "END", "SAVEPOINT", "RELEASE", "START"]);
185
+ const DOT_READ = new Set([".schema", ".tables", ".dump", ".indexes", ".databases", ".headers", ".mode", ".timer", ".help", ".quit", ".exit", ".show", ".width", ".nullvalue", ".separator"]);
186
+ function one(stmt) {
187
+ const s = stmt.trim();
188
+ if (s.startsWith(".")) {
189
+ const word = s.split(/\s+/)[0].toLowerCase();
190
+ return DOT_READ.has(word) ? { verb: "read", predicate: "not_applicable" } : null;
191
+ }
192
+ const first = /^[A-Za-z]+/.exec(s)?.[0].toUpperCase() ?? "";
193
+ if (NEUTRAL.has(first))
194
+ return "neutral";
195
+ switch (first) {
196
+ case "SELECT": return /\binto\b/i.test(s) ? { verb: "create", predicate: "not_applicable" } : { verb: "read", predicate: "not_applicable" };
197
+ case "SHOW":
198
+ case "DESCRIBE":
199
+ case "DESC": return { verb: "read", predicate: "not_applicable" };
200
+ case "PRAGMA": return s.includes("=") ? null : { verb: "read", predicate: "not_applicable" };
201
+ case "EXPLAIN": {
202
+ const rest = s.replace(/^explain\s+(?:analyze\s+|verbose\s+|query\s+plan\s+)*(?:\([^)]*\)\s*)?/i, "");
203
+ return rest === s || rest === "" ? null : one(rest);
204
+ }
205
+ case "WITH": return /\b(?:insert|update|delete|merge)\b/i.test(s) ? null : { verb: "read", predicate: "not_applicable" };
206
+ case "INSERT":
207
+ case "REPLACE": return { verb: "insert", predicate: "not_applicable" };
208
+ case "UPDATE": return { verb: "update", predicate: predicateOf(s) };
209
+ case "DELETE": {
210
+ const p = predicateOf(s);
211
+ return { verb: p === "all" ? "delete_all" : "delete", predicate: p };
212
+ }
213
+ case "TRUNCATE": return { verb: "delete_all", predicate: "all" };
214
+ case "CREATE": return { verb: "create", predicate: "not_applicable" };
215
+ case "ALTER": return { verb: "alter", predicate: "not_applicable", destructive: /\b(?:drop|rename)\b/i.test(s) };
216
+ case "DROP": return { verb: "drop", predicate: "not_applicable" };
217
+ default: return null;
218
+ }
219
+ }
220
+ /** Classify SQL text, or null when any statement cannot be placed. */
221
+ export function classifySql(text) {
222
+ if (text.length > MAX_SQL)
223
+ return null;
224
+ const cleaned = clean(text);
225
+ if (cleaned === null)
226
+ return null;
227
+ let verb = "read";
228
+ let sawStatement = false;
229
+ let broadest = "not_applicable";
230
+ let destructive = false;
231
+ for (const stmt of statements(cleaned)) {
232
+ const r = one(stmt);
233
+ if (r === null)
234
+ return null;
235
+ if (r === "neutral")
236
+ continue;
237
+ sawStatement = true;
238
+ if (SEVERITY[r.verb] > SEVERITY[verb])
239
+ verb = r.verb;
240
+ if (r.predicate === "all")
241
+ broadest = "all";
242
+ else if (r.predicate === "bounded" && broadest !== "all")
243
+ broadest = "bounded";
244
+ if (r.verb === "drop" || r.verb === "delete_all" || r.destructive === true)
245
+ destructive = true;
246
+ }
247
+ if (!sawStatement)
248
+ return null;
249
+ return { verb, predicate_class: verb === "delete_all" ? "all" : broadest, destructive };
250
+ }
251
+ //# sourceMappingURL=sql-classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-classify.js","sourceRoot":"","sources":["../src/sql-classify.ts"],"names":[],"mappings":"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","sourcesContent":["// A conservative SQL classifier for the typed `database` operation.\n//\n// It reads a statement only to decide its verb and whether an UPDATE or DELETE names a\n// selective predicate. Its result is two closed values; the text, the table names, the\n// column names, the literals and the rows never leave this function. It is a classifier for\n// a coding agent's own command line, not a parser: a statement it cannot place with\n// confidence makes the whole input `null` (the caller then records no database operation and\n// the command stays a plain shell action), and an UPDATE or DELETE whose predicate it cannot\n// show to be selective is reported as covering `all` rows. Unknown is never reported clean.\n//\n// Known limits, on purpose: a function call inside a SELECT can have effects (`SELECT\n// pg_terminate_backend(...)`), dynamic SQL and procedural blocks are not read, and a predicate\n// that is selective in form but matches every row at run time (`WHERE id > -1`) is bounded.\n\nexport type SqlVerb = \"read\" | \"insert\" | \"update\" | \"delete\" | \"delete_all\" | \"create\" | \"alter\" | \"drop\";\nexport interface SqlClass {\n verb: SqlVerb;\n /** The broadest predicate among the UPDATE and DELETE statements, `not_applicable` when there are none. */\n predicate_class: \"bounded\" | \"all\" | \"not_applicable\";\n /** Any statement that removes data or structure: drop, delete_all (a truncate too), or an ALTER that drops or renames. */\n destructive: boolean;\n}\n\nconst MAX_SQL = 1_000_000;\nconst SEVERITY: Record<SqlVerb, number> = { read: 0, insert: 1, create: 2, update: 3, delete: 4, alter: 5, delete_all: 6, drop: 7 };\n\n/** Comments removed, string literals reduced to `'s'` (or kept as `'%'` when only wildcards),\n * quoted identifiers unquoted. Null when a quote or comment is left open or a dollar-quoted body is present. */\nfunction clean(text: string): string | null {\n let out = \"\";\n for (let i = 0; i < text.length;) {\n const c = text[i];\n const next = text[i + 1];\n if (c === \"-\" && next === \"-\") { const e = text.indexOf(\"\\n\", i); i = e < 0 ? text.length : e; out += \" \"; continue; }\n if (c === \"/\" && next === \"*\") { const e = text.indexOf(\"*/\", i + 2); if (e < 0) return null; i = e + 2; out += \" \"; continue; }\n if (c === \"'\") {\n let j = i + 1;\n let body = \"\";\n for (;;) {\n if (j >= text.length) return null;\n if (text[j] === \"'\" && text[j + 1] === \"'\") { body += \"'\"; j += 2; continue; }\n if (text[j] === \"'\") break;\n body += text[j++];\n }\n out += /^[%_]+$/.test(body) ? `'${body}'` : \"'s'\";\n i = j + 1;\n continue;\n }\n if (c === '\"' || c === \"`\") {\n const e = text.indexOf(c, i + 1);\n if (e < 0) return null;\n out += ` ${text.slice(i + 1, e).replace(/[^A-Za-z0-9_]/g, \"_\")} `;\n i = e + 1;\n continue;\n }\n if (c === \"[\" && /^\\[[A-Za-z0-9_ ]+\\]/.test(text.slice(i))) { const e = text.indexOf(\"]\", i); out += ` ${text.slice(i + 1, e).replace(/ /g, \"_\")} `; i = e + 1; continue; }\n if (c === \"$\" && (next === \"$\" || /[A-Za-z_]/.test(next ?? \"\"))) return null; // dollar-quoted body\n out += c;\n i++;\n }\n return out;\n}\n\n/** Split on `;` outside parentheses. */\nfunction statements(text: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let cur = \"\";\n for (const c of text) {\n if (c === \"(\") depth++;\n else if (c === \")\") depth = Math.max(0, depth - 1);\n if (c === \";\" && depth === 0) { out.push(cur); cur = \"\"; continue; }\n cur += c;\n }\n out.push(cur);\n return out.map((s) => s.trim()).filter((s) => s !== \"\");\n}\n\n/** Split `text` at a top-level (depth 0) whole-word keyword; returns the parts. */\nfunction splitTop(text: string, word: string): string[] {\n const parts: string[] = [];\n const re = new RegExp(`^${word}(?![A-Za-z0-9_])`, \"i\");\n let depth = 0;\n let last = 0;\n for (let i = 0; i < text.length; i++) {\n const c = text[i];\n if (c === \"(\") depth++;\n else if (c === \")\") depth = Math.max(0, depth - 1);\n else if (depth === 0 && (i === 0 || /[^A-Za-z0-9_]/.test(text[i - 1])) && re.test(text.slice(i))) {\n parts.push(text.slice(last, i));\n last = i + word.length;\n i = last - 1;\n }\n }\n parts.push(text.slice(last));\n return parts;\n}\n\nconst LITERAL = /^(?:'[^']*'|-?\\d+(?:\\.\\d+)?|true|false|null)$/i;\n\nfunction stripParens(s: string): string {\n let t = s.trim();\n while (t.startsWith(\"(\") && t.endsWith(\")\")) {\n let depth = 0;\n let closesAtEnd = true;\n for (let i = 0; i < t.length; i++) {\n if (t[i] === \"(\") depth++;\n else if (t[i] === \")\") { depth--; if (depth === 0 && i < t.length - 1) { closesAtEnd = false; break; } }\n }\n if (!closesAtEnd) break;\n t = t.slice(1, -1).trim();\n }\n return t;\n}\n\n/** Whether one AND-ed condition narrows the rows: a comparison, IN, EXISTS or IS NULL that is\n * neither a tautology nor a bare `IS NOT NULL`. */\nfunction selective(raw: string): boolean {\n const c = stripParens(raw).replace(/\\s+/g, \" \");\n if (c === \"\") return false;\n if (splitTop(c, \"or\").length > 1) return false;\n if (splitTop(c, \"and\").length > 1) return splitTop(c, \"and\").some((p) => selective(p));\n if (/^(?:true|1|not false|not 0)$/i.test(c)) return false;\n if (/\\bis not null$/i.test(c)) return false;\n if (/between_r$/i.test(c)) return true;\n if (/^(?:not )?exists\\b/i.test(c) || /\\bis null$/i.test(c)) return true;\n const m = /^(.+?) ?(==|=|<>|!=|<=|>=|<|>|\\bnot like\\b|\\bnot ilike\\b|\\blike\\b|\\bilike\\b|\\bnot in\\b|\\bin\\b|\\bbetween_r\\b|\\bis not\\b|\\bis\\b) ?(.+)$/i.exec(c);\n if (!m) return false;\n const left = m[1].trim();\n const right = m[3].trim();\n if (LITERAL.test(left) && LITERAL.test(right)) return false;\n if (left.toLowerCase() === right.toLowerCase()) return false;\n if (/like$/i.test(m[2]) && /^'[%_]+'$/.test(right)) return false;\n return true;\n}\n\n/** The predicate of an UPDATE or DELETE, `bounded` only when a top-level AND-ed condition is selective. */\nfunction predicateOf(stmt: string): \"bounded\" | \"all\" {\n const parts = splitTop(stmt, \"where\");\n if (parts.length < 2) return \"all\";\n let where = parts.slice(1).join(\" \");\n where = where.replace(/\\b(?:returning|order by|limit)\\b[\\s\\S]*$/i, \"\");\n where = where.replace(/\\bbetween\\s+\\S+\\s+and\\s+\\S+/gi, \"between_r\");\n if (splitTop(where, \"or\").length > 1) return \"all\";\n return splitTop(where, \"and\").some((p) => selective(p)) ? \"bounded\" : \"all\";\n}\n\nconst NEUTRAL = new Set([\"BEGIN\", \"COMMIT\", \"ROLLBACK\", \"END\", \"SAVEPOINT\", \"RELEASE\", \"START\"]);\nconst DOT_READ = new Set([\".schema\", \".tables\", \".dump\", \".indexes\", \".databases\", \".headers\", \".mode\", \".timer\", \".help\", \".quit\", \".exit\", \".show\", \".width\", \".nullvalue\", \".separator\"]);\n\ninterface One { verb: SqlVerb; predicate: \"bounded\" | \"all\" | \"not_applicable\"; destructive?: boolean }\n\nfunction one(stmt: string): One | \"neutral\" | null {\n const s = stmt.trim();\n if (s.startsWith(\".\")) {\n const word = s.split(/\\s+/)[0].toLowerCase();\n return DOT_READ.has(word) ? { verb: \"read\", predicate: \"not_applicable\" } : null;\n }\n const first = /^[A-Za-z]+/.exec(s)?.[0].toUpperCase() ?? \"\";\n if (NEUTRAL.has(first)) return \"neutral\";\n switch (first) {\n case \"SELECT\": return /\\binto\\b/i.test(s) ? { verb: \"create\", predicate: \"not_applicable\" } : { verb: \"read\", predicate: \"not_applicable\" };\n case \"SHOW\": case \"DESCRIBE\": case \"DESC\": return { verb: \"read\", predicate: \"not_applicable\" };\n case \"PRAGMA\": return s.includes(\"=\") ? null : { verb: \"read\", predicate: \"not_applicable\" };\n case \"EXPLAIN\": {\n const rest = s.replace(/^explain\\s+(?:analyze\\s+|verbose\\s+|query\\s+plan\\s+)*(?:\\([^)]*\\)\\s*)?/i, \"\");\n return rest === s || rest === \"\" ? null : one(rest);\n }\n case \"WITH\": return /\\b(?:insert|update|delete|merge)\\b/i.test(s) ? null : { verb: \"read\", predicate: \"not_applicable\" };\n case \"INSERT\": case \"REPLACE\": return { verb: \"insert\", predicate: \"not_applicable\" };\n case \"UPDATE\": return { verb: \"update\", predicate: predicateOf(s) };\n case \"DELETE\": { const p = predicateOf(s); return { verb: p === \"all\" ? \"delete_all\" : \"delete\", predicate: p }; }\n case \"TRUNCATE\": return { verb: \"delete_all\", predicate: \"all\" };\n case \"CREATE\": return { verb: \"create\", predicate: \"not_applicable\" };\n case \"ALTER\": return { verb: \"alter\", predicate: \"not_applicable\", destructive: /\\b(?:drop|rename)\\b/i.test(s) };\n case \"DROP\": return { verb: \"drop\", predicate: \"not_applicable\" };\n default: return null;\n }\n}\n\n/** Classify SQL text, or null when any statement cannot be placed. */\nexport function classifySql(text: string): SqlClass | null {\n if (text.length > MAX_SQL) return null;\n const cleaned = clean(text);\n if (cleaned === null) return null;\n let verb: SqlVerb = \"read\";\n let sawStatement = false;\n let broadest: \"bounded\" | \"all\" | \"not_applicable\" = \"not_applicable\";\n let destructive = false;\n for (const stmt of statements(cleaned)) {\n const r = one(stmt);\n if (r === null) return null;\n if (r === \"neutral\") continue;\n sawStatement = true;\n if (SEVERITY[r.verb] > SEVERITY[verb]) verb = r.verb;\n if (r.predicate === \"all\") broadest = \"all\";\n else if (r.predicate === \"bounded\" && broadest !== \"all\") broadest = \"bounded\";\n if (r.verb === \"drop\" || r.verb === \"delete_all\" || r.destructive === true) destructive = true;\n }\n if (!sawStatement) return null;\n return { verb, predicate_class: verb === \"delete_all\" ? \"all\" : broadest, destructive };\n}\n"]}
@@ -0,0 +1,73 @@
1
+ import { type SimpleCommand } from "./shell.js";
2
+ import type { BindingKey } from "./observation.js";
3
+ type Operation = Record<string, unknown>;
4
+ /** Everything the derivations read from disk or the environment. Injected in tests and by the proof runner. */
5
+ export interface FileProbe {
6
+ /** UTF-8 text of a file, or null when missing, unreadable or larger than `max` bytes. */
7
+ readText(path: string, max: number): string | null;
8
+ /** Lowercase SHA-256 of a file's bytes, or null. */
9
+ sha256File(path: string): string | null;
10
+ /** Lowercase SHA-256 over the relative paths and bytes of every file under a directory, or null. */
11
+ sha256Dir(path: string): string | null;
12
+ /** The `.sql` files of a directory in name order, or null when it cannot be read or holds none. */
13
+ listSql(path: string): Array<{
14
+ name: string;
15
+ text: string;
16
+ }> | null;
17
+ }
18
+ export declare const systemFiles: FileProbe;
19
+ /** No files, deterministic: what the proof fixtures read, so a proof never depends on the machine it runs on. */
20
+ export declare const fixtureFiles: FileProbe;
21
+ /** What the derivations need beyond the binding key. */
22
+ export interface InfraContext {
23
+ key: BindingKey;
24
+ cwd: string;
25
+ referenceSetVersion: string;
26
+ files?: FileProbe;
27
+ /** Environment variable lookup for account and PostgreSQL defaults; `process.env` when absent. */
28
+ env?: (name: string) => string | undefined;
29
+ }
30
+ /** What the database and Wrangler readers need: no key, because they only read facts. */
31
+ export type ReadContext = Pick<InfraContext, "cwd" | "files" | "env">;
32
+ /** A URL reduced to scheme, IDNA-lowercase host and effective port. Null for anything else
33
+ * (an unexpanded variable, a glob, an IPv6 literal, a non-HTTP scheme, a host that is not a
34
+ * plain hostname). A scheme-less operand reads as `http`, as curl does. */
35
+ export declare function parseDestination(raw: string, assumeHttp: boolean): {
36
+ scheme: "http" | "https";
37
+ host: string;
38
+ port: number;
39
+ } | null;
40
+ /** A WebFetch tool call: a GET of the URL in its input. */
41
+ export declare function fetchOperation(url: string, ctx: InfraContext, request: unknown): Operation | null;
42
+ /** What a database command is, before it is wrapped in an operation. */
43
+ export interface DatabaseFacts {
44
+ provider: "cloudflare_d1" | "postgres" | "sqlite";
45
+ verb: string;
46
+ predicate_class: "bounded" | "all" | "not_applicable";
47
+ /** A drop, a delete of every row, an ALTER that drops, or an unbounded UPDATE; `unknown` when the SQL could not be read. */
48
+ risk: "ordinary" | "destructive" | "unknown";
49
+ scope: "remote" | "local" | "unknown";
50
+ /** Environment class the command itself shows. */
51
+ environment: string;
52
+ /** Provider-scoped name the id is keyed from. */
53
+ databaseKey: string;
54
+ databaseKind: string;
55
+ /** Keyed digest input: the statement text or the migration set. Never exported. */
56
+ digestInput?: unknown;
57
+ migrate?: boolean;
58
+ }
59
+ /** The database facts of a simple command, or null when it is not a readable database command. */
60
+ export declare function databaseFacts(sc: SimpleCommand, ctx: ReadContext): DatabaseFacts | null;
61
+ /** The typed operation for one simple command: a Wrangler or database CLI call, or a shell fetcher. */
62
+ export declare function deriveInfraOperation(sc: SimpleCommand, ctx: InfraContext, request: unknown, dialect?: "posix" | "powershell", networkOnly?: boolean): Operation | null;
63
+ /** One remote-database action per command of a shell call that touches a remote database, for
64
+ * the opt-in enforcement rule. `risk` is the same classification the typed operation uses. */
65
+ export interface DatabaseGuardAction {
66
+ provider: string;
67
+ verb: string;
68
+ scope: string;
69
+ risk: string;
70
+ }
71
+ export declare function databaseGuardActions(command: string, dialect: "posix" | "powershell", ctx: ReadContext): DatabaseGuardAction[];
72
+ export {};
73
+ //# sourceMappingURL=typed-infra.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-infra.d.ts","sourceRoot":"","sources":["../src/typed-infra.ts"],"names":[],"mappings":"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"}