@askdb/core 1.0.0-beta.41 → 1.0.0-beta.43

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 (105) hide show
  1. package/README.md +44 -4
  2. package/dist/ask.d.ts +129 -2
  3. package/dist/ask.d.ts.map +1 -1
  4. package/dist/ask.js +391 -15
  5. package/dist/ask.js.map +1 -1
  6. package/dist/errors.d.ts +87 -2
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +29 -0
  9. package/dist/errors.js.map +1 -1
  10. package/dist/index.d.ts +8 -5
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +5 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/logging/log-events.d.ts +13 -0
  15. package/dist/logging/log-events.d.ts.map +1 -1
  16. package/dist/logging/log-events.js +13 -0
  17. package/dist/logging/log-events.js.map +1 -1
  18. package/dist/schema/v2/index.d.ts +1 -1
  19. package/dist/schema/v2/index.d.ts.map +1 -1
  20. package/dist/schema/v2/loader.d.ts +9 -1
  21. package/dist/schema/v2/loader.d.ts.map +1 -1
  22. package/dist/schema/v2/loader.js +171 -49
  23. package/dist/schema/v2/loader.js.map +1 -1
  24. package/dist/schema/v2/normalized.d.ts +33 -0
  25. package/dist/schema/v2/normalized.d.ts.map +1 -1
  26. package/dist/schema/v2/parser.d.ts +9 -0
  27. package/dist/schema/v2/parser.d.ts.map +1 -1
  28. package/dist/schema/v2/parser.js +18 -2
  29. package/dist/schema/v2/parser.js.map +1 -1
  30. package/dist/schema/v2/tenant-policy-loader.d.ts.map +1 -1
  31. package/dist/schema/v2/tenant-policy-loader.js +7 -3
  32. package/dist/schema/v2/tenant-policy-loader.js.map +1 -1
  33. package/dist/schema/v2/tenant-policy.d.ts +28 -21
  34. package/dist/schema/v2/tenant-policy.d.ts.map +1 -1
  35. package/dist/schema/v2/tenant-policy.js +67 -11
  36. package/dist/schema/v2/tenant-policy.js.map +1 -1
  37. package/dist/sql/bind.d.ts +102 -0
  38. package/dist/sql/bind.d.ts.map +1 -0
  39. package/dist/sql/bind.js +422 -0
  40. package/dist/sql/bind.js.map +1 -0
  41. package/dist/sql/dialect-spec.d.ts +24 -1
  42. package/dist/sql/dialect-spec.d.ts.map +1 -1
  43. package/dist/sql/dialect-spec.js +82 -3
  44. package/dist/sql/dialect-spec.js.map +1 -1
  45. package/dist/sql/extract-sql.d.ts +8 -1
  46. package/dist/sql/extract-sql.d.ts.map +1 -1
  47. package/dist/sql/extract-sql.js +27 -4
  48. package/dist/sql/extract-sql.js.map +1 -1
  49. package/dist/sql/generate.d.ts +22 -0
  50. package/dist/sql/generate.d.ts.map +1 -1
  51. package/dist/sql/generate.js +92 -16
  52. package/dist/sql/generate.js.map +1 -1
  53. package/dist/sql/index.d.ts +4 -2
  54. package/dist/sql/index.d.ts.map +1 -1
  55. package/dist/sql/index.js +4 -2
  56. package/dist/sql/index.js.map +1 -1
  57. package/dist/sql/lexer.d.ts +114 -0
  58. package/dist/sql/lexer.d.ts.map +1 -0
  59. package/dist/sql/lexer.js +397 -0
  60. package/dist/sql/lexer.js.map +1 -0
  61. package/dist/sql/parameter-manifest.d.ts +34 -0
  62. package/dist/sql/parameter-manifest.d.ts.map +1 -0
  63. package/dist/sql/parameter-manifest.js +150 -0
  64. package/dist/sql/parameter-manifest.js.map +1 -0
  65. package/dist/sql/prompt.d.ts +7 -1
  66. package/dist/sql/prompt.d.ts.map +1 -1
  67. package/dist/sql/prompt.js +24 -1
  68. package/dist/sql/prompt.js.map +1 -1
  69. package/dist/sql/sensitive-guardrail.d.ts +62 -0
  70. package/dist/sql/sensitive-guardrail.d.ts.map +1 -0
  71. package/dist/sql/sensitive-guardrail.js +876 -0
  72. package/dist/sql/sensitive-guardrail.js.map +1 -0
  73. package/dist/sql/tenant-guardrail.d.ts +70 -8
  74. package/dist/sql/tenant-guardrail.d.ts.map +1 -1
  75. package/dist/sql/tenant-guardrail.js +799 -52
  76. package/dist/sql/tenant-guardrail.js.map +1 -1
  77. package/dist/sql/tenant-hierarchy.d.ts +19 -0
  78. package/dist/sql/tenant-hierarchy.d.ts.map +1 -0
  79. package/dist/sql/tenant-hierarchy.js +53 -0
  80. package/dist/sql/tenant-hierarchy.js.map +1 -0
  81. package/dist/sql/tenant-placeholders.d.ts +60 -9
  82. package/dist/sql/tenant-placeholders.d.ts.map +1 -1
  83. package/dist/sql/tenant-placeholders.js +237 -63
  84. package/dist/sql/tenant-placeholders.js.map +1 -1
  85. package/dist/sql/tenant-prompt.d.ts +4 -0
  86. package/dist/sql/tenant-prompt.d.ts.map +1 -1
  87. package/dist/sql/tenant-prompt.js +79 -11
  88. package/dist/sql/tenant-prompt.js.map +1 -1
  89. package/dist/sql/tenant-scope-validate.d.ts +3 -1
  90. package/dist/sql/tenant-scope-validate.d.ts.map +1 -1
  91. package/dist/sql/tenant-scope-validate.js +5 -2
  92. package/dist/sql/tenant-scope-validate.js.map +1 -1
  93. package/dist/sql/validate.d.ts +24 -4
  94. package/dist/sql/validate.d.ts.map +1 -1
  95. package/dist/sql/validate.js +123 -83
  96. package/dist/sql/validate.js.map +1 -1
  97. package/package.json +9 -7
  98. package/dist/exec/postgres.d.ts +0 -6
  99. package/dist/exec/postgres.d.ts.map +0 -1
  100. package/dist/exec/postgres.js +0 -30
  101. package/dist/exec/postgres.js.map +0 -1
  102. package/dist/modes/post-execute-log.d.ts +0 -9
  103. package/dist/modes/post-execute-log.d.ts.map +0 -1
  104. package/dist/modes/post-execute-log.js +0 -26
  105. package/dist/modes/post-execute-log.js.map +0 -1
@@ -1,27 +1,38 @@
1
+ import { TenantScopeError } from "../errors.js";
2
+ import { assertDistinctRootPlaceholders, placeholderForRoot, placeholderForTenantRoot, } from "../schema/v2/tenant-policy.js";
3
+ import { getDialectSpec, isBuiltInDialectId } from "./dialect-spec.js";
4
+ import { escapeSqlLiteral, escapeSqlLiteralLegacy, formatMarker, markerStyleForDialect, scanTenantPlaceholders, tokenizeSqlSpans, } from "./bind.js";
5
+ /** The lexer input for a {@link TenantSqlDialect}: only meaningful when an `id` is known. */
6
+ function lexerDialect(dialect) {
7
+ return dialect?.id === undefined
8
+ ? undefined
9
+ : { id: dialect.id, backslashEscapes: dialect.backslashEscapes };
10
+ }
1
11
  // ---------------------------------------------------------------------------
2
- // Placeholder naming convention (matches tenant-prompt.ts)
12
+ // Placeholder naming convention (owned by the policy; re-exported for callers here)
3
13
  // ---------------------------------------------------------------------------
4
- const PLACEHOLDER_RE = /:tenant_([a-z0-9_]+)_ids/g;
5
- export function placeholderForRoot(label) {
6
- return `:tenant_${label.toLowerCase().replace(/[^a-z0-9]+/g, "_")}_ids`;
7
- }
14
+ export { placeholderForRoot, placeholderForTenantRoot };
8
15
  // ---------------------------------------------------------------------------
9
- // Extract placeholders found in SQL
16
+ // Extract placeholders found in SQL (quote-aware via shared scanner)
10
17
  // ---------------------------------------------------------------------------
11
- export function extractTenantPlaceholders(sql) {
18
+ export function extractTenantPlaceholders(sql, dialect) {
12
19
  const matches = new Set();
13
- for (const m of sql.matchAll(PLACEHOLDER_RE)) {
14
- matches.add(m[0]);
20
+ for (const p of scanTenantPlaceholders(sql, lexerDialect(dialect))) {
21
+ matches.add(p.placeholder);
15
22
  }
16
23
  return [...matches];
17
24
  }
18
- export function resolvePlaceholders(sql, policy, scope) {
19
- const placeholders = extractTenantPlaceholders(sql);
25
+ export function resolvePlaceholders(sql, policy, scope, dialect) {
26
+ const placeholders = extractTenantPlaceholders(sql, dialect);
20
27
  if (placeholders.length === 0)
21
28
  return [];
29
+ // The loader and validateTenantScope() reject a shared placeholder too, but this is
30
+ // where the IDs bind: a policy built in code, or changed after validation, must not
31
+ // make the map below keep one root and bind its IDs where the other's are compared.
32
+ assertDistinctRootPlaceholders(policy.roots);
22
33
  const rootsByPlaceholder = new Map();
23
34
  for (const root of policy.roots) {
24
- rootsByPlaceholder.set(placeholderForRoot(root.label), {
35
+ rootsByPlaceholder.set(placeholderForTenantRoot(root), {
25
36
  rootId: root.id,
26
37
  label: root.label,
27
38
  });
@@ -49,8 +60,8 @@ function buildIdsByRoot(access) {
49
60
  m.set(access.tenantRoot, access.ids);
50
61
  break;
51
62
  case "subtree":
52
- m.set(access.tenantRoot, access.rootIds);
53
- break;
63
+ // resolveTenantSql() rejects an unexpanded subtree before reaching here.
64
+ throw unexpandedSubtreeError(access.tenantRoot, "resolveTenantSql()");
54
65
  case "multi_root":
55
66
  for (const s of access.scopes) {
56
67
  const existing = m.get(s.tenantRoot) ?? [];
@@ -62,75 +73,238 @@ function buildIdsByRoot(access) {
62
73
  }
63
74
  return m;
64
75
  }
76
+ /**
77
+ * The error for a `subtree` access that reached placeholder substitution or the
78
+ * prompt builder unexpanded. Neither walks the hierarchy: binding only the seed
79
+ * `rootIds` would silently drop every descendant, and a prompt naming only the
80
+ * root's placeholder would invite the model to filter descendant roots through it.
81
+ */
82
+ export function unexpandedSubtreeError(tenantRoot, caller) {
83
+ return new TenantScopeError(`tenantScope.access is an unexpanded 'subtree' of '${tenantRoot}'. ${caller} does not walk ` +
84
+ "the hierarchy. Expand the subtree first: ask() does this when you pass " +
85
+ "resolveTenantDescendants; a direct caller passes a 'multi_root' access with each tenant " +
86
+ "root's IDs under that root (or an 'ids' access when the subtree is one root table).", "SUBTREE_NOT_RESOLVABLE");
87
+ }
65
88
  // ---------------------------------------------------------------------------
66
- // Replace placeholders — SQL-only mode (inline literals)
89
+ // Substitution — token-aware, one edit per code-region occurrence
67
90
  // ---------------------------------------------------------------------------
68
- function escapeSqlLiteral(value) {
69
- return "'" + value.replace(/'/g, "''") + "'";
91
+ function escapeTenantId(value, dialect) {
92
+ if (dialect === undefined) {
93
+ return escapeSqlLiteralLegacy(value);
94
+ }
95
+ let backslashEscapes = dialect.backslashEscapes;
96
+ if (backslashEscapes === undefined && dialect.id !== undefined && isBuiltInDialectId(dialect.id)) {
97
+ backslashEscapes = getDialectSpec(dialect.id).backslashEscapes;
98
+ }
99
+ if (backslashEscapes === undefined && value.includes("\\")) {
100
+ // Doubling quotes alone leaves `\'` able to close the literal on an engine
101
+ // that reads backslash escapes (MySQL's default), so don't guess.
102
+ throw new TenantScopeError(`A tenant ID contains a backslash, and dialect '${dialect.id ?? "(no id)"}' does not say ` +
103
+ "whether backslash escapes inside string literals. Set backslashEscapes on the dialect, " +
104
+ "or use tenantSqlMode 'sql-params'.", "UNESCAPABLE_TENANT_ID");
105
+ }
106
+ return escapeSqlLiteral(value, { backslashEscapes });
70
107
  }
71
- export function replacePlaceholdersWithLiterals(sql, resolved) {
72
- let result = sql;
73
- for (const r of resolved) {
74
- if (r.ids.length === 0)
75
- continue;
76
- const literal = r.ids.length === 1
77
- ? escapeSqlLiteral(r.ids[0])
78
- : `(${r.ids.map(escapeSqlLiteral).join(", ")})`;
79
- result = replaceOperatorAware(result, r.placeholder, literal, r.ids.length > 1);
108
+ /**
109
+ * Replace every tenant placeholder that sits in a code region of `sql`.
110
+ * Placeholder text inside string literals, quoted identifiers, or comments is
111
+ * left alone (the shared lexer never reports it), so a substituted value can
112
+ * never land inside — or close — a surrounding literal. With a dialect `id`,
113
+ * those regions are lexed the way that engine reads them (e.g. MySQL backslash
114
+ * escapes, where `'it\'s :tenant_x_ids'` is one string literal).
115
+ *
116
+ * `render` runs once per occurrence, in source order, and returns one SQL
117
+ * fragment (literal or driver marker) per tenant ID. Rendering in source order
118
+ * is what keeps positional `?` markers aligned with their parameters.
119
+ *
120
+ * Fails closed: an occurrence whose placeholder has no IDs in scope, or matches
121
+ * no tenant root, throws instead of shipping the raw `:tenant_*` text. So does a
122
+ * placeholder in any casing other than the exact lowercase form.
123
+ */
124
+ function substituteTenantPlaceholders(sql, resolved, render, dialect) {
125
+ rejectCaseVariantTenantPlaceholders(sql, dialect);
126
+ const byPlaceholder = new Map(resolved.map((r) => [r.placeholder, r]));
127
+ const edits = [];
128
+ for (const occ of scanTenantPlaceholders(sql, lexerDialect(dialect))) {
129
+ const r = byPlaceholder.get(occ.placeholder);
130
+ if (!r || r.ids.length === 0) {
131
+ throw new TenantScopeError(r
132
+ ? `Generated SQL references ${occ.placeholder} but the current scope provides no IDs ` +
133
+ `for tenant root '${r.rootId}'. Refusing to emit SQL with an unsubstituted tenant placeholder.`
134
+ : `Generated SQL references ${occ.placeholder}, which matches no tenant root in the policy. ` +
135
+ `Refusing to emit SQL with an unsubstituted tenant placeholder.`, "UNRESOLVED_TENANT_PLACEHOLDER");
136
+ }
137
+ edits.push(planEdit(sql, occ, render(r)));
138
+ }
139
+ let out = sql;
140
+ for (const edit of edits.sort((a, b) => b.start - a.start)) {
141
+ out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
80
142
  }
81
- return result;
143
+ return out;
82
144
  }
83
- // ---------------------------------------------------------------------------
84
- // Replace placeholders — SQL+params mode (positional $N)
85
- // ---------------------------------------------------------------------------
86
- export function replacePlaceholdersWithParams(sql, resolved, startIndex = 1) {
87
- let result = sql;
88
- const params = [];
89
- let idx = startIndex;
90
- for (const r of resolved) {
91
- if (r.ids.length === 0)
145
+ // `(?<!:)` so a `::type` cast is not read as a placeholder (matches the substituter's scanner).
146
+ const ANY_CASE_PLACEHOLDER_RE = /(?<!:):([a-z][a-z0-9_]*)/gi;
147
+ /**
148
+ * Tenant placeholders are case-sensitive: the prompt names the exact lowercase
149
+ * form, and the substituter (like `bindPreparedQuery()`) only recognizes that
150
+ * form. Any other casing (`:TENANT_AGENCY_IDS`) would otherwise pass through
151
+ * unsubstituted, so reject it rather than return SQL with a raw placeholder.
152
+ * Scans the same code regions as the substituter.
153
+ */
154
+ function rejectCaseVariantTenantPlaceholders(sql, dialect) {
155
+ // Same code regions the substituter scans, so both agree on what is a placeholder.
156
+ for (const span of tokenizeSqlSpans(sql, lexerDialect(dialect))) {
157
+ if (span.kind !== "code")
92
158
  continue;
93
- if (r.ids.length === 1) {
94
- const paramRef = `$${idx}`;
95
- result = replaceOperatorAware(result, r.placeholder, paramRef, false);
96
- params.push(r.ids[0]);
97
- idx++;
159
+ for (const m of sql.slice(span.start, span.end).matchAll(ANY_CASE_PLACEHOLDER_RE)) {
160
+ const name = m[1];
161
+ const lower = name.toLowerCase();
162
+ if (name === lower || !/^tenant_[a-z0-9_]+_ids$/.test(lower))
163
+ continue;
164
+ throw new TenantScopeError(`Generated SQL references ${m[0]}, but tenant placeholders are case-sensitive and must be ` +
165
+ `written :${lower}. Refusing to emit SQL with an unsubstituted tenant placeholder.`, "UNRESOLVED_TENANT_PLACEHOLDER");
98
166
  }
99
- else {
100
- const paramRefs = r.ids.map(() => `$${idx++}`);
101
- const paramList = `(${paramRefs.join(", ")})`;
102
- result = replaceOperatorAware(result, r.placeholder, paramList, true);
103
- params.push(...r.ids);
167
+ }
168
+ }
169
+ const IN_LIST_BEFORE = /\bIN\s*\(\s*$/i;
170
+ const CLOSE_PAREN_AFTER = /^\s*\)/;
171
+ const QUANTIFIED_BEFORE = /(?<![<>!=])(==|=|<>|!=)\s*(ANY|SOME|ALL)\s*\(\s*$/i;
172
+ const COMPARISON_BEFORE = /(?<![<>!=])(<>|!=|<=|>=|==|=|<|>)\s*$/;
173
+ /**
174
+ * Decide how one occurrence is rewritten, from the operator in front of it.
175
+ *
176
+ * - Sole element of `IN (…)` / `NOT IN (…)`: the placeholder becomes the list.
177
+ * - `= ANY(…)` / `= SOME(…)` → `IN (…)`; `<> ALL(…)` / `!= ALL(…)` → `NOT IN (…)`.
178
+ * - `=` with several IDs → `IN (…)`; `!=` / `<>` with several IDs → `NOT IN (…)`.
179
+ * With one ID the operator is kept and only the placeholder is replaced.
180
+ * - `<`, `>`, `<=`, `>=`, or any other position, with several IDs has no list
181
+ * meaning, so it throws rather than emit SQL that means something else.
182
+ */
183
+ function planEdit(sql, occ, items) {
184
+ const before = sql.slice(0, occ.start);
185
+ const after = sql.slice(occ.end);
186
+ const list = items.join(", ");
187
+ const close = CLOSE_PAREN_AFTER.exec(after);
188
+ if (close && IN_LIST_BEFORE.test(before)) {
189
+ return { start: occ.start, end: occ.end, text: list };
190
+ }
191
+ const quantified = close ? QUANTIFIED_BEFORE.exec(before) : null;
192
+ if (quantified && close) {
193
+ const op = quantified[1];
194
+ const quantifier = quantified[2].toUpperCase();
195
+ const positive = (op === "=" || op === "==") && quantifier !== "ALL";
196
+ const negative = (op === "<>" || op === "!=") && quantifier === "ALL";
197
+ if (!positive && !negative) {
198
+ throw unsupportedPredicate(occ, `${op} ${quantifier}(…)`);
104
199
  }
200
+ const start = occ.start - quantified[0].length;
201
+ return {
202
+ start,
203
+ end: occ.end + close[0].length,
204
+ text: spaced(sql, start, `${negative ? "NOT IN" : "IN"} (${list})`),
205
+ };
206
+ }
207
+ if (items.length === 1) {
208
+ return { start: occ.start, end: occ.end, text: items[0] };
105
209
  }
106
- return { sql: result, params, nextIndex: idx };
210
+ const comparison = COMPARISON_BEFORE.exec(before);
211
+ if (comparison) {
212
+ const op = comparison[1];
213
+ const start = occ.start - comparison[0].length;
214
+ if (op === "=" || op === "==") {
215
+ return { start, end: occ.end, text: spaced(sql, start, `IN (${list})`) };
216
+ }
217
+ if (op === "!=" || op === "<>") {
218
+ return { start, end: occ.end, text: spaced(sql, start, `NOT IN (${list})`) };
219
+ }
220
+ throw unsupportedPredicate(occ, op);
221
+ }
222
+ throw unsupportedPredicate(occ, undefined);
223
+ }
224
+ /** Prefix a space when the rewritten fragment would otherwise touch the preceding token. */
225
+ function spaced(sql, start, text) {
226
+ return start > 0 && !/\s/.test(sql[start - 1]) ? ` ${text}` : text;
227
+ }
228
+ function unsupportedPredicate(occ, op) {
229
+ const where = op === undefined ? "outside a comparison or IN list" : `with operator '${op}'`;
230
+ return new TenantScopeError(`Generated SQL uses ${occ.placeholder} ${where}, but the current scope has several tenant IDs ` +
231
+ `and that predicate has no list form. Only =, !=, <>, IN (…), NOT IN (…), = ANY(…) and ` +
232
+ `<> ALL(…) can bind multiple IDs. Refusing to emit SQL.`, "UNSUPPORTED_TENANT_PREDICATE");
107
233
  }
108
234
  // ---------------------------------------------------------------------------
109
- // Operator-aware replacement: = → IN when multiple values
235
+ // Replace placeholders — SQL-only mode (inline literals)
110
236
  // ---------------------------------------------------------------------------
111
- function replaceOperatorAware(sql, placeholder, replacement, isMultiple) {
112
- if (isMultiple) {
113
- const eqPattern = new RegExp(`=\\s*${escapeRegex(placeholder)}`, "g");
114
- sql = sql.replace(eqPattern, `IN ${replacement}`);
115
- const inPattern = new RegExp(`IN\\s*\\(\\s*${escapeRegex(placeholder)}\\s*\\)`, "gi");
116
- sql = sql.replace(inPattern, `IN ${replacement}`);
117
- }
118
- sql = sql.replace(new RegExp(escapeRegex(placeholder), "g"), replacement);
119
- return sql;
237
+ export function replacePlaceholdersWithLiterals(sql, resolved, dialect) {
238
+ return substituteTenantPlaceholders(sql, resolved, (r) => r.ids.map((id) => escapeTenantId(id, dialect)), dialect);
239
+ }
240
+ // ---------------------------------------------------------------------------
241
+ // Replace placeholders — SQL+params mode (dialect driver markers)
242
+ // ---------------------------------------------------------------------------
243
+ function tenantMarkerStyle(dialect) {
244
+ const id = dialect?.id;
245
+ return id !== undefined && isBuiltInDialectId(id) ? markerStyleForDialect(id) : "dollar";
120
246
  }
121
- function escapeRegex(s) {
122
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
247
+ /**
248
+ * Marker for the value at 1-based position `ordinal` of the params array the SQL
249
+ * runs with. `$N` is 1-based and `@pN` 0-based (as in `bindPreparedQuery`);
250
+ * `?` is positional by occurrence.
251
+ */
252
+ function tenantMarker(style, ordinal) {
253
+ return style === "atp" ? formatMarker("atp", ordinal - 1) : formatMarker(style, ordinal);
254
+ }
255
+ /**
256
+ * Replace tenant placeholders with dialect driver markers: `$N` for
257
+ * Postgres/CockroachDB (and when no dialect id is given), `?` for
258
+ * MySQL/MariaDB/SQLite, `@pN` for SQL Server. Each occurrence gets its own
259
+ * markers, allocated in source order, and `params` lists the IDs in that same
260
+ * order — so for `?` dialects `params` lines up with the markers left to right.
261
+ *
262
+ * `startIndex` is the 1-based position of the first tenant value in the params
263
+ * array the SQL will run with (`1` when tenant values are the only params).
264
+ */
265
+ export function replacePlaceholdersWithParams(sql, resolved, startIndex = 1, dialect) {
266
+ const style = tenantMarkerStyle(dialect);
267
+ const params = [];
268
+ let idx = startIndex;
269
+ const out = substituteTenantPlaceholders(sql, resolved, (r) => r.ids.map((id) => {
270
+ params.push(id);
271
+ return tenantMarker(style, idx++);
272
+ }), dialect);
273
+ return { sql: out, params };
123
274
  }
124
275
  // ---------------------------------------------------------------------------
125
276
  // High-level entry point
126
277
  // ---------------------------------------------------------------------------
127
- export function resolveTenantSql(sql, policy, scope, mode = "sql-only", paramStartIndex = 1) {
278
+ /**
279
+ * Substitute the `:tenant_<root>_ids` placeholders in `sql` with the IDs from
280
+ * `scope` — as escaped literals (`"sql-only"`), or as dialect driver markers plus
281
+ * a `params` array (`"sql-params"`). Only placeholders in code regions are
282
+ * touched; text inside string literals and quoted identifiers is left as-is.
283
+ *
284
+ * A `subtree` access must already be expanded into per-root IDs (a `multi_root`
285
+ * access, or `ids` when the subtree is one root table): `ask()` does this via its
286
+ * `resolveTenantDescendants` option. This function does not walk the hierarchy, so
287
+ * an unexpanded `subtree` throws (`SUBTREE_NOT_RESOLVABLE`) rather than bind the
288
+ * seed IDs only.
289
+ *
290
+ * Also throws `TenantScopeError` when a placeholder cannot be resolved
291
+ * (`UNRESOLVED_TENANT_PLACEHOLDER`) or when a multi-ID scope meets a predicate
292
+ * with no list form (`UNSUPPORTED_TENANT_PREDICATE`), and in `sql-only` mode when
293
+ * a tenant ID holds a backslash but the dialect's escaping is unknown
294
+ * (`UNESCAPABLE_TENANT_ID`). Throws `SchemaParseError` when two of the policy's roots
295
+ * derive the same placeholder, rather than bind one root's IDs through the other's. `global` scope returns
296
+ * `sql` unchanged.
297
+ */
298
+ export function resolveTenantSql(sql, policy, scope, mode = "sql-only", paramStartIndex = 1, dialect) {
299
+ if (scope.access.kind === "subtree") {
300
+ throw unexpandedSubtreeError(scope.access.tenantRoot, "resolveTenantSql()");
301
+ }
128
302
  if (scope.access.kind === "global") {
129
303
  return mode === "sql-only"
130
304
  ? { mode: "sql-only", sql, bindings: [] }
131
305
  : { mode: "sql-params", sql, params: [], bindings: [], paramStartIndex };
132
306
  }
133
- const resolved = resolvePlaceholders(sql, policy, scope);
307
+ const resolved = resolvePlaceholders(sql, policy, scope, dialect);
134
308
  const bindings = resolved.map((r) => ({
135
309
  placeholder: r.placeholder,
136
310
  rootLabel: r.rootLabel,
@@ -140,11 +314,11 @@ export function resolveTenantSql(sql, policy, scope, mode = "sql-only", paramSta
140
314
  if (mode === "sql-only") {
141
315
  return {
142
316
  mode: "sql-only",
143
- sql: replacePlaceholdersWithLiterals(sql, resolved),
317
+ sql: replacePlaceholdersWithLiterals(sql, resolved, dialect),
144
318
  bindings,
145
319
  };
146
320
  }
147
- const paramResult = replacePlaceholdersWithParams(sql, resolved, paramStartIndex);
321
+ const paramResult = replacePlaceholdersWithParams(sql, resolved, paramStartIndex, dialect);
148
322
  return {
149
323
  mode: "sql-params",
150
324
  sql: paramResult.sql,
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-placeholders.js","sourceRoot":"","sources":["../../src/sql/tenant-placeholders.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tenant-placeholders.js","sourceRoot":"","sources":["../../src/sql/tenant-placeholders.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,10 @@ import type { TenantScope } from "../schema/v2/tenant-policy.js";
3
3
  /**
4
4
  * Build the tenant policy + runtime scope block for NL→SQL prompts.
5
5
  * This block is always injected when a tenant policy exists (security boundary).
6
+ *
7
+ * A `subtree` scope must be expanded into per-root IDs first (`ask()` does this
8
+ * through `resolveTenantDescendants`); an unexpanded one throws
9
+ * `SUBTREE_NOT_RESOLVABLE`.
6
10
  */
7
11
  export declare function buildTenantPromptBlock(policy: NormalizedTenantPolicy, scope: TenantScope): string;
8
12
  //# sourceMappingURL=tenant-prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-prompt.d.ts","sourceRoot":"","sources":["../../src/sql/tenant-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,EAC9B,KAAK,EAAE,WAAW,GACjB,MAAM,CA4HR"}
1
+ {"version":3,"file":"tenant-prompt.d.ts","sourceRoot":"","sources":["../../src/sql/tenant-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAQjE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,EAC9B,KAAK,EAAE,WAAW,GACjB,MAAM,CAiJR"}
@@ -1,6 +1,14 @@
1
+ import { placeholderForTenantRoot } from "../schema/v2/tenant-policy.js";
2
+ import { unexpandedSubtreeError } from "./tenant-placeholders.js";
3
+ const NEVER_CROSS_ROOTS = " The same ID value can name different tenants in different root tables: " +
4
+ "never compare one root's placeholder with another root's column.";
1
5
  /**
2
6
  * Build the tenant policy + runtime scope block for NL→SQL prompts.
3
7
  * This block is always injected when a tenant policy exists (security boundary).
8
+ *
9
+ * A `subtree` scope must be expanded into per-root IDs first (`ask()` does this
10
+ * through `resolveTenantDescendants`); an unexpanded one throws
11
+ * `SUBTREE_NOT_RESOLVABLE`.
4
12
  */
5
13
  export function buildTenantPromptBlock(policy, scope) {
6
14
  const lines = [];
@@ -58,25 +66,45 @@ export function buildTenantPromptBlock(policy, scope) {
58
66
  switch (access.kind) {
59
67
  case "ids": {
60
68
  const rootLabel = policy.roots.find((r) => r.id === access.tenantRoot)?.label ?? access.tenantRoot;
61
- const placeholder = `:tenant_${rootLabel.toLowerCase().replace(/[^a-z0-9]+/g, "_")}_ids`;
69
+ const placeholder = placeholderForTenantRoot({ id: access.tenantRoot, label: rootLabel });
62
70
  lines.push(` Access: ${rootLabel} IDs = ${placeholder}`);
71
+ // With several roots the model also sees other roots' columns (an expanded
72
+ // subtree with IDs at its root only is an `ids` scope), so pair this one too.
73
+ // A single-root policy keeps its block unchanged.
74
+ const pairColumns = policy.roots.length > 1;
75
+ if (pairColumns) {
76
+ const columns = columnsHoldingRootIds(policy, access.tenantRoot);
77
+ if (columns.length > 0)
78
+ lines.push(` columns: ${columns.join(", ")}`);
79
+ }
63
80
  lines.push(` Use ${placeholder} as the parameter placeholder for tenant predicates.`);
81
+ if (pairColumns)
82
+ lines.push(NEVER_CROSS_ROOTS);
64
83
  break;
65
84
  }
66
- case "subtree": {
67
- const rootLabel = policy.roots.find((r) => r.id === access.tenantRoot)?.label ?? access.tenantRoot;
68
- const placeholder = `:tenant_${rootLabel.toLowerCase().replace(/[^a-z0-9]+/g, "_")}_ids`;
69
- lines.push(` Access: ${rootLabel} subtree from IDs = ${placeholder} (include all descendants)`);
70
- lines.push(` Use ${placeholder} as the parameter placeholder for tenant predicates.`);
71
- break;
72
- }
85
+ case "subtree":
86
+ throw unexpandedSubtreeError(access.tenantRoot, "buildTenantPromptBlock()");
73
87
  case "multi_root": {
74
- lines.push(" Access: multiple roots —");
88
+ // Each root table has its own ID space (an expanded subtree is a multi_root
89
+ // scope), so name the columns each placeholder may be compared with.
90
+ lines.push(" Access: multiple roots. Each placeholder holds the IDs of one tenant root; " +
91
+ "compare it only with the columns listed under it:");
92
+ // The tenant guardrail checks every root the scope names as a root table (#341), so a
93
+ // child root in the scope can't be filtered through its parent's foreign key: don't
94
+ // offer that column under the parent's placeholder.
95
+ const covered = new Set(access.scopes.map((s) => s.tenantRoot));
75
96
  for (const s of access.scopes) {
76
97
  const rootLabel = policy.roots.find((r) => r.id === s.tenantRoot)?.label ?? s.tenantRoot;
77
- const placeholder = `:tenant_${rootLabel.toLowerCase().replace(/[^a-z0-9]+/g, "_")}_ids`;
78
- lines.push(` - ${rootLabel} IDs = ${placeholder}`);
98
+ // A level an expanded subtree covers with no IDs: its placeholder binds nothing.
99
+ const empty = s.ids.length === 0 ? " (no IDs in this scope: don't read this table)" : "";
100
+ lines.push(` - ${rootLabel} IDs = ${placeholderForTenantRoot({ id: s.tenantRoot, label: rootLabel })}${empty}`);
101
+ const columns = columnsHoldingRootIds(policy, s.tenantRoot, covered);
102
+ if (columns.length > 0)
103
+ lines.push(` columns: ${columns.join(", ")}`);
79
104
  }
105
+ lines.push(NEVER_CROSS_ROOTS);
106
+ lines.push(" A root table listed here that the query reads must itself be filtered with its own placeholder; " +
107
+ "filtering it only through its parent's foreign key or a joined ancestor is not enough.");
80
108
  break;
81
109
  }
82
110
  case "global":
@@ -124,4 +152,44 @@ export function buildTenantPromptBlock(policy, scope) {
124
152
  lines.push("--- END TENANT POLICY ---");
125
153
  return lines.join("\n");
126
154
  }
155
+ /**
156
+ * The columns whose values are IDs of `rootId`, per the policy, in a stable order:
157
+ * the root's own `tenantIdColumn`; each child root's foreign key to it
158
+ * (`roots[].parent`, `hierarchy[]`), unless that child is in `coveredRoots` (the guardrail
159
+ * checks a covered child on its own placeholder); each scoped table's direct column for it;
160
+ * and each polymorphic ID column, with the discriminator value that points at it.
161
+ * A table scoped through a join path is filtered on the root's own column after
162
+ * the join, as the join-path lines of the prompt say, so it adds no column here.
163
+ */
164
+ function columnsHoldingRootIds(policy, rootId, coveredRoots = new Set()) {
165
+ const root = policy.roots.find((r) => r.id === rootId);
166
+ const columns = [];
167
+ const add = (column) => {
168
+ if (!columns.includes(column))
169
+ columns.push(column);
170
+ };
171
+ if (root)
172
+ add(root.tenantIdColumn);
173
+ for (const child of policy.roots) {
174
+ if (child.parent?.root === rootId && !coveredRoots.has(child.id))
175
+ add(child.parent.foreignKey);
176
+ }
177
+ for (const edge of policy.hierarchy) {
178
+ if (edge.parent === rootId && !coveredRoots.has(edge.child))
179
+ add(edge.foreignKey);
180
+ }
181
+ for (const st of policy.scopedTables) {
182
+ for (const path of st.scopeThrough) {
183
+ if (path.root === rootId && "column" in path)
184
+ add(path.column);
185
+ }
186
+ }
187
+ for (const pt of policy.polymorphicTables) {
188
+ for (const [typeValue, target] of Object.entries(pt.mapping)) {
189
+ if (target === rootId)
190
+ add(`${pt.idColumn} (where ${pt.typeColumn} = '${typeValue}')`);
191
+ }
192
+ }
193
+ return columns;
194
+ }
127
195
  //# sourceMappingURL=tenant-prompt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-prompt.js","sourceRoot":"","sources":["../../src/sql/tenant-prompt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tenant-prompt.js","sourceRoot":"","sources":["../../src/sql/tenant-prompt.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,9 @@ import type { NormalizedTenantPolicy } from "../schema/v2/tenant-policy.js";
2
2
  import { type TenantScope } from "../schema/v2/tenant-policy.js";
3
3
  /**
4
4
  * Validate a `TenantScope` input against a tenant policy before prompt generation.
5
- * Throws `TenantScopeError` on failure.
5
+ * Throws `TenantScopeError` on failure, and `SchemaParseError` when two of the
6
+ * policy's roots derive the same placeholder (the loader rejects that too; this
7
+ * covers a policy built in code).
6
8
  */
7
9
  export declare function validateTenantScope(tenantPolicy: NormalizedTenantPolicy, tenantScope: TenantScope | undefined): void;
8
10
  //# sourceMappingURL=tenant-scope-validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-scope-validate.d.ts","sourceRoot":"","sources":["../../src/sql/tenant-scope-validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,sBAAsB,EACpC,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,IAAI,CA2DN"}
1
+ {"version":3,"file":"tenant-scope-validate.d.ts","sourceRoot":"","sources":["../../src/sql/tenant-scope-validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,sBAAsB,EACpC,WAAW,EAAE,WAAW,GAAG,SAAS,GACnC,IAAI,CA6DN"}
@@ -1,10 +1,13 @@
1
1
  import { TenantScopeError } from "../errors.js";
2
- import { tenantScopeSchema } from "../schema/v2/tenant-policy.js";
2
+ import { assertDistinctRootPlaceholders, tenantScopeSchema, } from "../schema/v2/tenant-policy.js";
3
3
  /**
4
4
  * Validate a `TenantScope` input against a tenant policy before prompt generation.
5
- * Throws `TenantScopeError` on failure.
5
+ * Throws `TenantScopeError` on failure, and `SchemaParseError` when two of the
6
+ * policy's roots derive the same placeholder (the loader rejects that too; this
7
+ * covers a policy built in code).
6
8
  */
7
9
  export function validateTenantScope(tenantPolicy, tenantScope) {
10
+ assertDistinctRootPlaceholders(tenantPolicy.roots);
8
11
  // Fail closed: policy exists but no scope
9
12
  if (!tenantScope) {
10
13
  throw new TenantScopeError("Tenant policy is active but no tenantScope was provided. " +
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-scope-validate.js","sourceRoot":"","sources":["../../src/sql/tenant-scope-validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAoB,MAAM,+BAA+B,CAAC;AAEpF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAoC,EACpC,WAAoC;IAEpC,0CAA0C;IAC1C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,2DAA2D;YACzD,gFAAgF,EAClF,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,gBAAgB,CACxB,8BAA8B,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EACpD,qBAAqB,CACtB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;IAElC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,KAAK;YACR,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,gBAAgB,CACxB,sDAAsD,MAAM,CAAC,UAAU,KAAK;oBAC1E,gBAAgB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3C,qBAAqB,CACtB,CAAC;YACJ,CAAC;YACD,MAAM;QAER,KAAK,SAAS;YACZ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,gBAAgB,CACxB,sDAAsD,MAAM,CAAC,UAAU,KAAK;oBAC1E,gBAAgB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3C,qBAAqB,CACtB,CAAC;YACJ,CAAC;YACD,MAAM;QAER,KAAK,YAAY;YACf,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;oBACnC,MAAM,IAAI,gBAAgB,CACxB,6DAA6D,KAAK,CAAC,UAAU,KAAK;wBAChF,gBAAgB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3C,qBAAqB,CACtB,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM;QAER,KAAK,QAAQ;YACX,kDAAkD;YAClD,MAAM;IACV,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"tenant-scope-validate.js","sourceRoot":"","sources":["../../src/sql/tenant-scope-validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EACL,8BAA8B,EAC9B,iBAAiB,GAElB,MAAM,+BAA+B,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAoC,EACpC,WAAoC;IAEpC,8BAA8B,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAEnD,0CAA0C;IAC1C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,2DAA2D;YACzD,gFAAgF,EAClF,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,gBAAgB,CACxB,8BAA8B,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EACpD,qBAAqB,CACtB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;IAElC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,KAAK;YACR,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,gBAAgB,CACxB,sDAAsD,MAAM,CAAC,UAAU,KAAK;oBAC1E,gBAAgB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3C,qBAAqB,CACtB,CAAC;YACJ,CAAC;YACD,MAAM;QAER,KAAK,SAAS;YACZ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,gBAAgB,CACxB,sDAAsD,MAAM,CAAC,UAAU,KAAK;oBAC1E,gBAAgB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3C,qBAAqB,CACtB,CAAC;YACJ,CAAC;YACD,MAAM;QAER,KAAK,YAAY;YACf,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;oBACnC,MAAM,IAAI,gBAAgB,CACxB,6DAA6D,KAAK,CAAC,UAAU,KAAK;wBAChF,gBAAgB,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAC3C,qBAAqB,CACtB,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM;QAER,KAAK,QAAQ;YACX,kDAAkD;YAClD,MAAM;IACV,CAAC;AACH,CAAC"}