@askdb/core 0.5.0-beta.12 → 0.5.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ask.d.ts +19 -0
- package/dist/ask.d.ts.map +1 -1
- package/dist/ask.js +32 -2
- package/dist/ask.js.map +1 -1
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/logging/log-events.d.ts +8 -0
- package/dist/logging/log-events.d.ts.map +1 -1
- package/dist/logging/log-events.js +8 -0
- package/dist/logging/log-events.js.map +1 -1
- package/dist/retrieval/synthesize-ddl.d.ts.map +1 -1
- package/dist/retrieval/synthesize-ddl.js +12 -0
- package/dist/retrieval/synthesize-ddl.js.map +1 -1
- package/dist/retrieval/types.d.ts +1 -1
- package/dist/retrieval/types.d.ts.map +1 -1
- package/dist/schema/v2/index.d.ts +3 -0
- package/dist/schema/v2/index.d.ts.map +1 -1
- package/dist/schema/v2/index.js +2 -0
- package/dist/schema/v2/index.js.map +1 -1
- package/dist/schema/v2/loader.d.ts.map +1 -1
- package/dist/schema/v2/loader.js +29 -5
- package/dist/schema/v2/loader.js.map +1 -1
- package/dist/schema/v2/normalized.d.ts +2 -0
- package/dist/schema/v2/normalized.d.ts.map +1 -1
- package/dist/schema/v2/tenant-policy-loader.d.ts +13 -0
- package/dist/schema/v2/tenant-policy-loader.d.ts.map +1 -0
- package/dist/schema/v2/tenant-policy-loader.js +282 -0
- package/dist/schema/v2/tenant-policy-loader.js.map +1 -0
- package/dist/schema/v2/tenant-policy.d.ts +715 -0
- package/dist/schema/v2/tenant-policy.d.ts.map +1 -0
- package/dist/schema/v2/tenant-policy.js +135 -0
- package/dist/schema/v2/tenant-policy.js.map +1 -0
- package/dist/sql/generate.d.ts +7 -0
- package/dist/sql/generate.d.ts.map +1 -1
- package/dist/sql/generate.js +23 -2
- package/dist/sql/generate.js.map +1 -1
- package/dist/sql/prompt.d.ts +2 -1
- package/dist/sql/prompt.d.ts.map +1 -1
- package/dist/sql/prompt.js +8 -1
- package/dist/sql/prompt.js.map +1 -1
- package/dist/sql/tenant-guardrail.d.ts +18 -0
- package/dist/sql/tenant-guardrail.d.ts.map +1 -0
- package/dist/sql/tenant-guardrail.js +132 -0
- package/dist/sql/tenant-guardrail.js.map +1 -0
- package/dist/sql/tenant-placeholders.d.ts +37 -0
- package/dist/sql/tenant-placeholders.d.ts.map +1 -0
- package/dist/sql/tenant-placeholders.js +156 -0
- package/dist/sql/tenant-placeholders.js.map +1 -0
- package/dist/sql/tenant-prompt.d.ts +8 -0
- package/dist/sql/tenant-prompt.d.ts.map +1 -0
- package/dist/sql/tenant-prompt.js +127 -0
- package/dist/sql/tenant-prompt.js.map +1 -0
- package/dist/sql/tenant-scope-validate.d.ts +8 -0
- package/dist/sql/tenant-scope-validate.d.ts.map +1 -0
- package/dist/sql/tenant-scope-validate.js +46 -0
- package/dist/sql/tenant-scope-validate.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Placeholder naming convention (matches tenant-prompt.ts)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
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
|
+
}
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Extract placeholders found in SQL
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
export function extractTenantPlaceholders(sql) {
|
|
12
|
+
const matches = new Set();
|
|
13
|
+
for (const m of sql.matchAll(PLACEHOLDER_RE)) {
|
|
14
|
+
matches.add(m[0]);
|
|
15
|
+
}
|
|
16
|
+
return [...matches];
|
|
17
|
+
}
|
|
18
|
+
export function resolvePlaceholders(sql, policy, scope) {
|
|
19
|
+
const placeholders = extractTenantPlaceholders(sql);
|
|
20
|
+
if (placeholders.length === 0)
|
|
21
|
+
return [];
|
|
22
|
+
const rootsByPlaceholder = new Map();
|
|
23
|
+
for (const root of policy.roots) {
|
|
24
|
+
rootsByPlaceholder.set(placeholderForRoot(root.label), {
|
|
25
|
+
rootId: root.id,
|
|
26
|
+
label: root.label,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const idsByRoot = buildIdsByRoot(scope.access);
|
|
30
|
+
const resolved = [];
|
|
31
|
+
for (const ph of placeholders) {
|
|
32
|
+
const rootInfo = rootsByPlaceholder.get(ph);
|
|
33
|
+
if (!rootInfo)
|
|
34
|
+
continue;
|
|
35
|
+
const ids = idsByRoot.get(rootInfo.rootId) ?? [];
|
|
36
|
+
resolved.push({
|
|
37
|
+
placeholder: ph,
|
|
38
|
+
rootLabel: rootInfo.label,
|
|
39
|
+
rootId: rootInfo.rootId,
|
|
40
|
+
ids,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
function buildIdsByRoot(access) {
|
|
46
|
+
const m = new Map();
|
|
47
|
+
switch (access.kind) {
|
|
48
|
+
case "ids":
|
|
49
|
+
m.set(access.tenantRoot, access.ids);
|
|
50
|
+
break;
|
|
51
|
+
case "subtree":
|
|
52
|
+
m.set(access.tenantRoot, access.rootIds);
|
|
53
|
+
break;
|
|
54
|
+
case "multi_root":
|
|
55
|
+
for (const s of access.scopes) {
|
|
56
|
+
const existing = m.get(s.tenantRoot) ?? [];
|
|
57
|
+
m.set(s.tenantRoot, [...existing, ...s.ids]);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case "global":
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return m;
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Replace placeholders — SQL-only mode (inline literals)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
function escapeSqlLiteral(value) {
|
|
69
|
+
return "'" + value.replace(/'/g, "''") + "'";
|
|
70
|
+
}
|
|
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);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
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)
|
|
92
|
+
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++;
|
|
98
|
+
}
|
|
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);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { sql: result, params, nextIndex: idx };
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Operator-aware replacement: = → IN when multiple values
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
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;
|
|
120
|
+
}
|
|
121
|
+
function escapeRegex(s) {
|
|
122
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
123
|
+
}
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// High-level entry point
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
export function resolveTenantSql(sql, policy, scope, mode = "sql-only", paramStartIndex = 1) {
|
|
128
|
+
if (scope.access.kind === "global") {
|
|
129
|
+
return mode === "sql-only"
|
|
130
|
+
? { mode: "sql-only", sql, bindings: [] }
|
|
131
|
+
: { mode: "sql-params", sql, params: [], bindings: [], paramStartIndex };
|
|
132
|
+
}
|
|
133
|
+
const resolved = resolvePlaceholders(sql, policy, scope);
|
|
134
|
+
const bindings = resolved.map((r) => ({
|
|
135
|
+
placeholder: r.placeholder,
|
|
136
|
+
rootLabel: r.rootLabel,
|
|
137
|
+
rootId: r.rootId,
|
|
138
|
+
ids: r.ids,
|
|
139
|
+
}));
|
|
140
|
+
if (mode === "sql-only") {
|
|
141
|
+
return {
|
|
142
|
+
mode: "sql-only",
|
|
143
|
+
sql: replacePlaceholdersWithLiterals(sql, resolved),
|
|
144
|
+
bindings,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const paramResult = replacePlaceholdersWithParams(sql, resolved, paramStartIndex);
|
|
148
|
+
return {
|
|
149
|
+
mode: "sql-params",
|
|
150
|
+
sql: paramResult.sql,
|
|
151
|
+
params: paramResult.params,
|
|
152
|
+
bindings,
|
|
153
|
+
paramStartIndex,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=tenant-placeholders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-placeholders.js","sourceRoot":"","sources":["../../src/sql/tenant-placeholders.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NormalizedTenantPolicy } from "../schema/v2/tenant-policy.js";
|
|
2
|
+
import type { TenantScope } from "../schema/v2/tenant-policy.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build the tenant policy + runtime scope block for NL→SQL prompts.
|
|
5
|
+
* This block is always injected when a tenant policy exists (security boundary).
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildTenantPromptBlock(policy: NormalizedTenantPolicy, scope: TenantScope): string;
|
|
8
|
+
//# sourceMappingURL=tenant-prompt.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the tenant policy + runtime scope block for NL→SQL prompts.
|
|
3
|
+
* This block is always injected when a tenant policy exists (security boundary).
|
|
4
|
+
*/
|
|
5
|
+
export function buildTenantPromptBlock(policy, scope) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
lines.push("--- TENANT POLICY (mandatory — every tenant-scoped table MUST be filtered) ---");
|
|
8
|
+
lines.push("");
|
|
9
|
+
// Hierarchy
|
|
10
|
+
lines.push("Tenant hierarchy:");
|
|
11
|
+
for (const root of policy.roots) {
|
|
12
|
+
const parentNote = root.parent
|
|
13
|
+
? ` (child of ${policy.roots.find((r) => r.id === root.parent.root)?.label ?? root.parent.root})`
|
|
14
|
+
: " (top-level)";
|
|
15
|
+
lines.push(` - ${root.label} [${root.id}]${parentNote}`);
|
|
16
|
+
}
|
|
17
|
+
lines.push("");
|
|
18
|
+
// Scoped tables
|
|
19
|
+
if (policy.scopedTables.length > 0) {
|
|
20
|
+
lines.push("Tenant-scoped tables (MUST include tenant predicate):");
|
|
21
|
+
for (const st of policy.scopedTables) {
|
|
22
|
+
for (const path of st.scopeThrough) {
|
|
23
|
+
const rootLabel = policy.roots.find((r) => r.id === path.root)?.label ?? path.root;
|
|
24
|
+
if ("column" in path) {
|
|
25
|
+
lines.push(` - ${st.id} → filter via ${path.column} (${rootLabel} scope)`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const joinPath = path.join.map((j) => `${j.from} → ${j.to}`).join(" → ");
|
|
29
|
+
lines.push(` - ${st.id} → join path: ${joinPath} (${rootLabel} scope)`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
lines.push("");
|
|
34
|
+
}
|
|
35
|
+
// Polymorphic tables
|
|
36
|
+
if (policy.polymorphicTables.length > 0) {
|
|
37
|
+
lines.push("Polymorphic tables (MUST include type discriminator AND id filter):");
|
|
38
|
+
for (const pt of policy.polymorphicTables) {
|
|
39
|
+
lines.push(` - ${pt.id}: type column = ${pt.typeColumn}, id column = ${pt.idColumn}`);
|
|
40
|
+
for (const [typeVal, targetRoot] of Object.entries(pt.mapping)) {
|
|
41
|
+
const rootLabel = policy.roots.find((r) => r.id === targetRoot)?.label ?? targetRoot;
|
|
42
|
+
lines.push(` - '${typeVal}' → ${rootLabel}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
lines.push("");
|
|
46
|
+
}
|
|
47
|
+
// Global tables
|
|
48
|
+
if (policy.globalTables.length > 0) {
|
|
49
|
+
lines.push("Global tables (no tenant filter needed):");
|
|
50
|
+
for (const gt of policy.globalTables) {
|
|
51
|
+
lines.push(` - ${gt}`);
|
|
52
|
+
}
|
|
53
|
+
lines.push("");
|
|
54
|
+
}
|
|
55
|
+
// Runtime scope
|
|
56
|
+
lines.push("Current user scope:");
|
|
57
|
+
const access = scope.access;
|
|
58
|
+
switch (access.kind) {
|
|
59
|
+
case "ids": {
|
|
60
|
+
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`;
|
|
62
|
+
lines.push(` Access: ${rootLabel} IDs = ${placeholder}`);
|
|
63
|
+
lines.push(` Use ${placeholder} as the parameter placeholder for tenant predicates.`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
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
|
+
}
|
|
73
|
+
case "multi_root": {
|
|
74
|
+
lines.push(" Access: multiple roots —");
|
|
75
|
+
for (const s of access.scopes) {
|
|
76
|
+
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}`);
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case "global":
|
|
83
|
+
lines.push(` Access: GLOBAL (reason: ${access.reason}) — no tenant filtering required`);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
lines.push("");
|
|
87
|
+
// Advisory context
|
|
88
|
+
if (scope.context) {
|
|
89
|
+
const ctx = scope.context;
|
|
90
|
+
const parts = [];
|
|
91
|
+
if (ctx.role)
|
|
92
|
+
parts.push(`role: ${ctx.role}`);
|
|
93
|
+
if (ctx.department)
|
|
94
|
+
parts.push(`department: ${ctx.department}`);
|
|
95
|
+
if (ctx.region)
|
|
96
|
+
parts.push(`region: ${ctx.region}`);
|
|
97
|
+
if (ctx.label)
|
|
98
|
+
parts.push(`user: ${ctx.label}`);
|
|
99
|
+
if (ctx.description)
|
|
100
|
+
parts.push(ctx.description);
|
|
101
|
+
if (ctx.attributes) {
|
|
102
|
+
for (const [k, v] of Object.entries(ctx.attributes)) {
|
|
103
|
+
parts.push(`${k}: ${v}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (parts.length > 0) {
|
|
107
|
+
lines.push("User context (advisory — not enforced):");
|
|
108
|
+
for (const part of parts) {
|
|
109
|
+
lines.push(` - ${part}`);
|
|
110
|
+
}
|
|
111
|
+
lines.push("");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Enforcement instructions
|
|
115
|
+
lines.push("Enforcement rules:");
|
|
116
|
+
lines.push("- Every query on a tenant-scoped table MUST include the tenant predicate.");
|
|
117
|
+
lines.push("- Use named placeholders for tenant IDs (e.g., :tenant_agency_ids).");
|
|
118
|
+
lines.push("- For inherited scope, JOIN through the specified path to reach the tenant root.");
|
|
119
|
+
lines.push("- For polymorphic tables, always include the type discriminator column in WHERE.");
|
|
120
|
+
lines.push("- Global/reference tables do NOT need tenant predicates.");
|
|
121
|
+
if (scope.access.kind === "global") {
|
|
122
|
+
lines.push("- GLOBAL scope is active — tenant predicates are optional for this query.");
|
|
123
|
+
}
|
|
124
|
+
lines.push("--- END TENANT POLICY ---");
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=tenant-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-prompt.js","sourceRoot":"","sources":["../../src/sql/tenant-prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NormalizedTenantPolicy } from "../schema/v2/tenant-policy.js";
|
|
2
|
+
import { type TenantScope } from "../schema/v2/tenant-policy.js";
|
|
3
|
+
/**
|
|
4
|
+
* Validate a `TenantScope` input against a tenant policy before prompt generation.
|
|
5
|
+
* Throws `TenantScopeError` on failure.
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateTenantScope(tenantPolicy: NormalizedTenantPolicy, tenantScope: TenantScope | undefined): void;
|
|
8
|
+
//# sourceMappingURL=tenant-scope-validate.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TenantScopeError } from "../errors.js";
|
|
2
|
+
import { tenantScopeSchema } from "../schema/v2/tenant-policy.js";
|
|
3
|
+
/**
|
|
4
|
+
* Validate a `TenantScope` input against a tenant policy before prompt generation.
|
|
5
|
+
* Throws `TenantScopeError` on failure.
|
|
6
|
+
*/
|
|
7
|
+
export function validateTenantScope(tenantPolicy, tenantScope) {
|
|
8
|
+
// Fail closed: policy exists but no scope
|
|
9
|
+
if (!tenantScope) {
|
|
10
|
+
throw new TenantScopeError("Tenant policy is active but no tenantScope was provided. " +
|
|
11
|
+
"Pass a TenantScope to ask() or remove tenant-policy.md to disable enforcement.", "MISSING_SCOPE");
|
|
12
|
+
}
|
|
13
|
+
// Validate shape with zod
|
|
14
|
+
const parsed = tenantScopeSchema.safeParse(tenantScope);
|
|
15
|
+
if (!parsed.success) {
|
|
16
|
+
throw new TenantScopeError(`Invalid tenantScope shape: ${parsed.error.message}`, "INVALID_SCOPE_SHAPE");
|
|
17
|
+
}
|
|
18
|
+
const rootIds = new Set(tenantPolicy.roots.map((r) => r.id));
|
|
19
|
+
const access = tenantScope.access;
|
|
20
|
+
switch (access.kind) {
|
|
21
|
+
case "ids":
|
|
22
|
+
if (!rootIds.has(access.tenantRoot)) {
|
|
23
|
+
throw new TenantScopeError(`tenantScope.access references unknown tenant root '${access.tenantRoot}'. ` +
|
|
24
|
+
`Known roots: ${[...rootIds].join(", ")}`, "UNKNOWN_TENANT_ROOT");
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
case "subtree":
|
|
28
|
+
if (!rootIds.has(access.tenantRoot)) {
|
|
29
|
+
throw new TenantScopeError(`tenantScope.access references unknown tenant root '${access.tenantRoot}'. ` +
|
|
30
|
+
`Known roots: ${[...rootIds].join(", ")}`, "UNKNOWN_TENANT_ROOT");
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
case "multi_root":
|
|
34
|
+
for (const scope of access.scopes) {
|
|
35
|
+
if (!rootIds.has(scope.tenantRoot)) {
|
|
36
|
+
throw new TenantScopeError(`tenantScope.access.scopes references unknown tenant root '${scope.tenantRoot}'. ` +
|
|
37
|
+
`Known roots: ${[...rootIds].join(", ")}`, "UNKNOWN_TENANT_ROOT");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
case "global":
|
|
42
|
+
// reason is already validated as non-empty by zod
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=tenant-scope-validate.js.map
|
|
@@ -0,0 +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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askdb/core",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.14",
|
|
4
4
|
"description": "AskDB core: dialect-agnostic NL→SQL pipeline. Provides ask() orchestration, schema/IR types, modes, logging, and retrieval input. Bring your own dialect (e.g. @askdb/postgres).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"askdb",
|