@askdb/core 1.0.0-beta.44 → 1.0.0-beta.46
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/README.md +20 -19
- package/dist/ask.d.ts +45 -45
- package/dist/ask.d.ts.map +1 -1
- package/dist/ask.js +153 -310
- package/dist/ask.js.map +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/enrichment/index.d.ts.map +1 -1
- package/dist/enrichment/prompt.d.ts +3 -3
- package/dist/enrichment/prompt.d.ts.map +1 -1
- package/dist/enrichment/prompt.js +43 -17
- package/dist/enrichment/prompt.js.map +1 -1
- package/dist/enrichment/suggest.d.ts +2 -2
- package/dist/enrichment/suggest.d.ts.map +1 -1
- package/dist/enrichment/suggest.js +3 -1
- package/dist/enrichment/suggest.js.map +1 -1
- package/dist/enrichment/types.d.ts +29 -9
- package/dist/enrichment/types.d.ts.map +1 -1
- package/dist/errors.d.ts +97 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/logging/askdb-logger.d.ts +2 -0
- package/dist/logging/askdb-logger.d.ts.map +1 -1
- package/dist/logging/create-askdb-logger.d.ts.map +1 -1
- package/dist/logging/create-askdb-logger.js +3 -0
- package/dist/logging/create-askdb-logger.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 +7 -1
- package/dist/retrieval/synthesize-ddl.d.ts.map +1 -1
- package/dist/retrieval/synthesize-ddl.js +36 -33
- package/dist/retrieval/synthesize-ddl.js.map +1 -1
- package/dist/schema/normalize.d.ts +6 -2
- package/dist/schema/normalize.d.ts.map +1 -1
- package/dist/schema/normalize.js +1 -4
- package/dist/schema/normalize.js.map +1 -1
- package/dist/schema/v2/format.d.ts +28 -1
- package/dist/schema/v2/format.d.ts.map +1 -1
- package/dist/schema/v2/format.js +53 -19
- package/dist/schema/v2/format.js.map +1 -1
- package/dist/schema/v2/index.d.ts +4 -3
- package/dist/schema/v2/index.d.ts.map +1 -1
- package/dist/schema/v2/index.js +4 -3
- package/dist/schema/v2/index.js.map +1 -1
- package/dist/schema/v2/loader.d.ts +28 -7
- package/dist/schema/v2/loader.d.ts.map +1 -1
- package/dist/schema/v2/loader.js +220 -96
- package/dist/schema/v2/loader.js.map +1 -1
- package/dist/schema/v2/mentions.d.ts +12 -0
- package/dist/schema/v2/mentions.d.ts.map +1 -1
- package/dist/schema/v2/mentions.js +65 -11
- package/dist/schema/v2/mentions.js.map +1 -1
- package/dist/schema/v2/save-file.d.ts +56 -0
- package/dist/schema/v2/save-file.d.ts.map +1 -0
- package/dist/schema/v2/save-file.js +138 -0
- package/dist/schema/v2/save-file.js.map +1 -0
- package/dist/schema/v2/tenant-policy-loader.d.ts +1 -10
- package/dist/schema/v2/tenant-policy-loader.d.ts.map +1 -1
- package/dist/schema/v2/tenant-policy-loader.js +65 -51
- package/dist/schema/v2/tenant-policy-loader.js.map +1 -1
- package/dist/schema/v2/tenant-policy.d.ts +7 -2
- package/dist/schema/v2/tenant-policy.d.ts.map +1 -1
- package/dist/schema/v2/tenant-policy.js +26 -0
- package/dist/schema/v2/tenant-policy.js.map +1 -1
- package/dist/sql/bind.d.ts +27 -7
- package/dist/sql/bind.d.ts.map +1 -1
- package/dist/sql/bind.js +33 -18
- package/dist/sql/bind.js.map +1 -1
- package/dist/sql/dialect-spec.d.ts +1 -0
- package/dist/sql/dialect-spec.d.ts.map +1 -1
- package/dist/sql/dialect-spec.js +7 -2
- package/dist/sql/dialect-spec.js.map +1 -1
- package/dist/sql/generate.d.ts +17 -11
- package/dist/sql/generate.d.ts.map +1 -1
- package/dist/sql/generate.js +65 -33
- package/dist/sql/generate.js.map +1 -1
- package/dist/sql/guardrail-decide.d.ts +58 -0
- package/dist/sql/guardrail-decide.d.ts.map +1 -0
- package/dist/sql/guardrail-decide.js +161 -0
- package/dist/sql/guardrail-decide.js.map +1 -0
- package/dist/sql/guardrails.d.ts +52 -0
- package/dist/sql/guardrails.d.ts.map +1 -0
- package/dist/sql/guardrails.js +100 -0
- package/dist/sql/guardrails.js.map +1 -0
- package/dist/sql/index.d.ts +2 -1
- package/dist/sql/index.d.ts.map +1 -1
- package/dist/sql/index.js +2 -1
- package/dist/sql/index.js.map +1 -1
- package/dist/sql/lexer.d.ts +13 -0
- package/dist/sql/lexer.d.ts.map +1 -1
- package/dist/sql/lexer.js +42 -7
- package/dist/sql/lexer.js.map +1 -1
- package/dist/sql/prompt-identifiers.d.ts +9 -0
- package/dist/sql/prompt-identifiers.d.ts.map +1 -1
- package/dist/sql/prompt-identifiers.js +22 -3
- package/dist/sql/prompt-identifiers.js.map +1 -1
- package/dist/sql/prompt.d.ts.map +1 -1
- package/dist/sql/prompt.js +5 -2
- package/dist/sql/prompt.js.map +1 -1
- package/dist/sql/rebind.d.ts +116 -0
- package/dist/sql/rebind.d.ts.map +1 -0
- package/dist/sql/rebind.js +215 -0
- package/dist/sql/rebind.js.map +1 -0
- package/dist/sql/sensitive-guardrail.d.ts +7 -2
- package/dist/sql/sensitive-guardrail.d.ts.map +1 -1
- package/dist/sql/sensitive-guardrail.js +14 -33
- package/dist/sql/sensitive-guardrail.js.map +1 -1
- package/dist/sql/tenant-guardrail.d.ts +3 -14
- package/dist/sql/tenant-guardrail.d.ts.map +1 -1
- package/dist/sql/tenant-guardrail.js +16 -65
- package/dist/sql/tenant-guardrail.js.map +1 -1
- package/dist/sql/tenant-placeholders.d.ts +40 -14
- package/dist/sql/tenant-placeholders.d.ts.map +1 -1
- package/dist/sql/tenant-placeholders.js +92 -48
- package/dist/sql/tenant-placeholders.js.map +1 -1
- package/dist/sql/tenant-policy-warnings.d.ts +8 -0
- package/dist/sql/tenant-policy-warnings.d.ts.map +1 -0
- package/dist/sql/tenant-policy-warnings.js +21 -0
- package/dist/sql/tenant-policy-warnings.js.map +1 -0
- package/dist/sql/tenant-scope-expand.d.ts +46 -0
- package/dist/sql/tenant-scope-expand.d.ts.map +1 -0
- package/dist/sql/tenant-scope-expand.js +101 -0
- package/dist/sql/tenant-scope-expand.js.map +1 -0
- package/dist/sql/tsql-batch.d.ts +56 -0
- package/dist/sql/tsql-batch.d.ts.map +1 -0
- package/dist/sql/tsql-batch.js +246 -0
- package/dist/sql/tsql-batch.js.map +1 -0
- package/dist/sql/validate.d.ts +18 -3
- package/dist/sql/validate.d.ts.map +1 -1
- package/dist/sql/validate.js +127 -8
- package/dist/sql/validate.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# `@askdb/core`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
NL→SQL pipeline for AskDB. Provides `ask()` orchestration, schema/IR types, modes, logging, retrieval input, and the built-in dialect specs (PostgreSQL, CockroachDB, MySQL, MariaDB, SQLite, SQL Server). Pass a dialect id such as `"postgres"` and bring your own model; the engine packages (`@askdb/postgres`, …) add introspection, not SQL generation ([ADR 0029](https://github.com/Ygilany/AskDB/blob/main/docs/adrs/0029-core-owns-built-in-dialect-specs.md)).
|
|
4
4
|
|
|
5
5
|
> **Status:** pre-release beta (`1.0.0-beta.x`); public APIs may still change before 1.0. `0.3.0` moved the Postgres dialect and `@askdb/core/postgres` to `@askdb/postgres`. Runtime AI provider construction lives in `@askdb/ai`; core remains BYO-model and does not read `process.env`. See [`docs/adrs/0002-integration-package-layout.md`](https://github.com/Ygilany/AskDB/blob/main/docs/adrs/0002-integration-package-layout.md), [`docs/adrs/0005-askdb-config-and-env-bootstrap.md`](https://github.com/Ygilany/AskDB/blob/main/docs/adrs/0005-askdb-config-and-env-bootstrap.md), and [`docs/adrs/0006-ai-provider-integration-strategy.md`](https://github.com/Ygilany/AskDB/blob/main/docs/adrs/0006-ai-provider-integration-strategy.md).
|
|
6
6
|
|
|
7
|
-
> **Security model:** `ask()` returns SQL and never executes it. Its SQL checks (single `SELECT`/`WITH` statement (with a SQL Server caveat), no comments, no write/DDL keywords, no known side-effecting functions, and the tenant and sensitive-column checks) are heuristic defense in depth that catch common model mistakes. The read-only and sensitive-column checks use a dialect-aware lexer; the tenant check uses a simpler scanner of its own ([#342](https://github.com/Ygilany/AskDB/issues/342)). They are not a SQL parser and not a security boundary. Run generated SQL under a read-only, least-privilege database role and enforce tenant isolation in the database. See [Safety boundaries](https://askdb.tools/concepts/safety-boundaries/).
|
|
7
|
+
> **Security model:** `ask()` returns SQL and never executes it. Its SQL checks (single `SELECT`/`WITH` statement (with a SQL Server caveat), no comments, no write/DDL keywords, no locking reads, no known side-effecting functions, and the tenant and sensitive-column checks) are heuristic defense in depth that catch common model mistakes. The read-only and sensitive-column checks use a dialect-aware lexer; the tenant check uses a simpler scanner of its own ([#342](https://github.com/Ygilany/AskDB/issues/342)). They are not a SQL parser and not a security boundary. Run generated SQL under a read-only, least-privilege database role and enforce tenant isolation in the database. See [Safety boundaries](https://askdb.tools/concepts/safety-boundaries/).
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
# `ai` (Vercel AI SDK) is a required peer dependency — your app owns its version
|
|
13
13
|
pnpm add @askdb/core ai
|
|
14
|
-
#
|
|
14
|
+
# Only to introspect a database (live, or offline from an export), its engine package:
|
|
15
15
|
pnpm add @askdb/postgres
|
|
16
16
|
# Plus a model — either construct one directly with an AI SDK provider:
|
|
17
17
|
pnpm add @ai-sdk/openai
|
|
@@ -21,7 +21,7 @@ pnpm add @askdb/ai
|
|
|
21
21
|
|
|
22
22
|
`ai` is a **peer dependency** (`^6.0.0 || ^7.0.51`: AI SDK 6, or 7.0.51 and later), not a bundled dependency: `ask()` receives a `LanguageModel` your app constructs, so core must use the same `ai` instance your app does. Hosts on AI SDK 6 (e.g. `@ai-sdk/openai@3`) and AI SDK 7 (`@ai-sdk/openai@4`) are both supported. The config-driven path (`@askdb/ai` and `@askdb/client`) currently requires AI SDK 7.
|
|
23
23
|
|
|
24
|
-
`ai` is a required peer, not an optional one, and core loads it at import time. So any package that depends on `@askdb/core` needs `ai` installed, even for introspection only. That covers `@askdb/client`, `@askdb/
|
|
24
|
+
`ai` is a required peer, not an optional one, and core loads it at import time. So any package that depends on `@askdb/core` needs `ai` installed, even for introspection only. That covers `@askdb/client`, `@askdb/introspect`, the engine packages, `@askdb/prisma`, `@askdb/enrich` and `@askdb/rag`. npm 7+ and pnpm install the missing peer for you. Yarn doesn't, so add `ai` yourself. ADR 0006 records why (2026-09 amendment).
|
|
25
25
|
|
|
26
26
|
`@askdb/core` itself does not depend on `pg`. The optional `pg` peer lives on `@askdb/postgres` for live Postgres introspection.
|
|
27
27
|
|
|
@@ -50,11 +50,10 @@ import { loadSchema } from "@askdb/core";
|
|
|
50
50
|
const schema = loadSchema("./fixtures/schemas/orders-users.schema");
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
## Minimal example — with the Postgres dialect
|
|
53
|
+
## Minimal example — with the built-in Postgres dialect
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
56
|
import { ask, loadSchema } from "@askdb/core";
|
|
57
|
-
import { postgresDialect } from "@askdb/postgres";
|
|
58
57
|
import { openai } from "@ai-sdk/openai";
|
|
59
58
|
|
|
60
59
|
const schema = loadSchema("./fixtures/schemas/orders-users.schema");
|
|
@@ -63,7 +62,7 @@ const { sql } = await ask({
|
|
|
63
62
|
question: "Top 5 customers by lifetime value?",
|
|
64
63
|
schema,
|
|
65
64
|
model: openai("gpt-4o"),
|
|
66
|
-
dialect:
|
|
65
|
+
dialect: "postgres", // or POSTGRES_DIALECT from @askdb/core
|
|
67
66
|
});
|
|
68
67
|
```
|
|
69
68
|
|
|
@@ -82,16 +81,14 @@ const result = await ask({
|
|
|
82
81
|
tenantScope,
|
|
83
82
|
});
|
|
84
83
|
|
|
85
|
-
// Execute either form.
|
|
86
|
-
await pool.query(result.sql);
|
|
87
|
-
await pool.query(result.unboundSql!, result.params); //
|
|
84
|
+
// Execute either form. Tenant IDs are driver parameters by default (tenantSqlMode: "sql-params").
|
|
85
|
+
await pool.query(result.sql, result.tenantParams); // business values inlined, tenant IDs bound
|
|
86
|
+
await pool.query(result.unboundSql!, result.params); // every value bound
|
|
88
87
|
|
|
89
88
|
// Render a form from result.parameters, then rebind locally — no model call.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
await pool.query(rebound.sql);
|
|
89
|
+
// The guardrails run again on the template under this schema and scope.
|
|
90
|
+
const rebound = bindPreparedQuery(result.preparedQuery!, { state_name: "Utah" }, { schema, tenantScope });
|
|
91
|
+
await pool.query(rebound.sql, rebound.tenantParams ?? []); // like ask(): tenant IDs bound
|
|
95
92
|
```
|
|
96
93
|
|
|
97
94
|
Key rules:
|
|
@@ -99,19 +96,23 @@ Key rules:
|
|
|
99
96
|
- `parameterize` defaults to **true**. Set `parameterize: false` when the extra output tokens are not worth it.
|
|
100
97
|
- If the model's extra blocks are missing or inconsistent, the extras are omitted and `result.sql` is unaffected. No new error reaches a caller who does not call `bindPreparedQuery()`.
|
|
101
98
|
- The model decides what to parameterize, so a mistake changes the *form*, not the query. Constrain form inputs using returned `type` and `description`.
|
|
102
|
-
- `bindPreparedQuery()`
|
|
103
|
-
-
|
|
99
|
+
- `bindPreparedQuery(prepared, values, { schema, tenantScope?, sensitiveGuardrailMode?, acceptWarnings?, dialect? })` re-runs the read-only, tenant and sensitive checks on the stored template under the schema, scope and modes you pass, then binds. Pass `dialect`, the `DialectSpec` you gave `ask()`, when `preparedQuery.dialectRules` is `"host"` or missing: the rebind refuses rather than fall back to the built-in rules. Tenant IDs come from `tenantScope`, never from `values`. Under a policy with `enforcement: warn`, a template that fails the tenant check is refused unless `acceptWarnings` includes `"tenant"`. Authorizing `tenantScope` is the host's job, exactly as for `ask()`. Expand a `subtree` scope with `expandTenantScope()` first.
|
|
100
|
+
- Each SQL form runs with its own array: `unboundSql` with `params` (tenant IDs included), `sql` with `tenantParams`. Never swap or concatenate them. `tenantSqlMode: "sql-only"` inlines the tenant IDs in `sql` instead, and refuses one that isn't printable ASCII or contains a backslash. With a tenant scope and a custom `AskDialect`, or a `DialectSpec` whose `id` isn't built in, set `tenantSqlMode` yourself: there's no default.
|
|
104
101
|
- List parameters are arity-stable in `unboundSql` only on PostgreSQL/CockroachDB (`= ANY($n)`); elsewhere a changed list length changes the marker count — rebind through `bindPreparedQuery()` rather than swapping the array.
|
|
105
102
|
- Markers: `$N` (Postgres/CockroachDB), `?` (MySQL/MariaDB/SQLite), `@pN` (SQL Server, 0-based). Map values via `parameters[].markers` for SQL Server.
|
|
106
103
|
|
|
107
104
|
## What you get
|
|
108
105
|
|
|
109
106
|
- `ask({ question, schema, model, dialect })` — generate checked SQL (plus optional `unboundSql` / `params` / `parameters` / `preparedQuery`).
|
|
110
|
-
- `bindPreparedQuery(prepared, values)` —
|
|
107
|
+
- `bindPreparedQuery(prepared, values, guardrails)` — local rebind of a `PreparedQuery` (no model call), re-checked under the given schema and tenant scope.
|
|
108
|
+
- `expandTenantScope(policy, scope, resolveTenantDescendants)` — the `subtree` expansion `ask()` runs, for a rebind.
|
|
111
109
|
- `AskDbLanguageModel` — AskDB's public name for the AI SDK language model contract.
|
|
112
|
-
- `
|
|
110
|
+
- `DialectSpec` and the built-in specs (`POSTGRES_DIALECT`, `MYSQL_DIALECT`, …, `BUILT_IN_DIALECTS`) — the SQL rules for each built-in engine. The engine packages re-export them.
|
|
111
|
+
- `AskDialect` — the escape-hatch contract for a fully custom generator.
|
|
113
112
|
- `loadSchema(path)` — load a Schema v2 directory, bundled JSON, or `schema.json` path.
|
|
114
113
|
- `loadSchemaFromJson(raw)` — parse a Schema v2 bundled JSON string (e.g. from an env var).
|
|
114
|
+
- `readSchemaDirectory(dir)` — read a Schema v2 directory into its bundle form (what `askdb bundle` writes).
|
|
115
|
+
- `saveSchemaFile(schemaDir, relativePath, content)` — write one file inside a schema directory; refuses a symbolic link and replaces the target through a temp file.
|
|
115
116
|
- `parseTableMarkdown` / `writeTableMarkdown` — round-trippable describable-layer parser/writer.
|
|
116
117
|
- `extractSqlFromModelText` — generic fenced-code extractor (dialect-agnostic).
|
|
117
118
|
- Structured logging hooks (`createAskDbLogger`, log-event contract).
|
package/dist/ask.d.ts
CHANGED
|
@@ -7,8 +7,11 @@ import type { Retriever } from "./retrieval/types.js";
|
|
|
7
7
|
import type { TenantScope } from "./schema/v2/tenant-policy.js";
|
|
8
8
|
import { type BuiltInDialectId, type DialectSpec } from "./sql/dialect-spec.js";
|
|
9
9
|
import { type TenantSqlOutputMode, type TenantBinding } from "./sql/tenant-placeholders.js";
|
|
10
|
+
import { type ResolveTenantDescendants, type TenantIdsByRoot } from "./sql/tenant-scope-expand.js";
|
|
10
11
|
import { type SensitiveGuardrailMode, type SensitiveGuardrailResult } from "./sql/sensitive-guardrail.js";
|
|
12
|
+
import { type GuardrailVerdict } from "./errors.js";
|
|
11
13
|
import { type PreparedQuery, type QueryParameterBinding, type QueryParamSlot } from "./sql/bind.js";
|
|
14
|
+
export type { ResolveTenantDescendants, TenantIdsByRoot };
|
|
12
15
|
/** Options forwarded to a dialect's generator. Stable across dialects. */
|
|
13
16
|
export type AskDialectGenerateOptions = {
|
|
14
17
|
logger?: AskDbLogger;
|
|
@@ -39,9 +42,11 @@ export type AskDialectGenerateResult = {
|
|
|
39
42
|
explain?: unknown;
|
|
40
43
|
/**
|
|
41
44
|
* Optional tenant guardrail result from a custom generator. When the schema has a
|
|
42
|
-
* tenant policy,
|
|
43
|
-
* `
|
|
44
|
-
*
|
|
45
|
+
* tenant policy, its warnings become tenant findings (`form: "generator"`) next to
|
|
46
|
+
* `ask()`'s own check of `sql` and `unboundNamedSql`, decided under the policy's
|
|
47
|
+
* `enforcement`; `passed: false` with no warnings becomes one `UNPROVABLE_SCOPE`
|
|
48
|
+
* finding. It can add findings but never replace or relax that check. Without a tenant
|
|
49
|
+
* policy it is passed through to `result.tenantGuardrail` unchanged.
|
|
45
50
|
*/
|
|
46
51
|
tenantGuardrail?: import("./sql/tenant-guardrail.js").TenantGuardrailResult;
|
|
47
52
|
usage?: AskUsage;
|
|
@@ -67,8 +72,10 @@ export type AskDialectGenerateResult = {
|
|
|
67
72
|
* policy, `ask()` runs the tenant guardrail on the returned `sql` (and
|
|
68
73
|
* `unboundNamedSql`), with its `:tenant_<root>_ids` placeholders still in place,
|
|
69
74
|
* then substitutes them, regardless of dialect; `strict` policies throw
|
|
70
|
-
* `TenantGuardrailError`. A `tenantGuardrail` your
|
|
71
|
-
*
|
|
75
|
+
* `TenantGuardrailError`. A `tenantGuardrail` your generator returns adds
|
|
76
|
+
* findings to that check (`form: "generator"`), never replaces it.
|
|
77
|
+
* - The sensitive-identifier check runs on your SQL too, unless
|
|
78
|
+
* `sensitiveGuardrailMode` is `"off"`.
|
|
72
79
|
*/
|
|
73
80
|
export type AskDialect = {
|
|
74
81
|
generate(question: string, schema: AnyNormalizedSchema, model: AskDbLanguageModel, options?: AskDialectGenerateOptions): Promise<AskDialectGenerateResult>;
|
|
@@ -92,28 +99,6 @@ export type AskGenerateDeps = {
|
|
|
92
99
|
*/
|
|
93
100
|
providerOptions?: Record<string, unknown>;
|
|
94
101
|
};
|
|
95
|
-
/**
|
|
96
|
-
* A subtree's IDs grouped by tenant root: each key is a root table id from the
|
|
97
|
-
* tenant policy (`"table:public.clients"`), and its value holds IDs of that root's
|
|
98
|
-
* own `tenantIdColumn`, never IDs of another root.
|
|
99
|
-
*
|
|
100
|
-
* Root tables have separate ID spaces, so client `5` and agency `5` are different
|
|
101
|
-
* tenants. Keying the IDs by root lets `ask()` bind each root's IDs to that root's
|
|
102
|
-
* own `:tenant_<label>_ids` placeholder.
|
|
103
|
-
*/
|
|
104
|
-
export type TenantIdsByRoot = Readonly<Record<string, readonly string[]>>;
|
|
105
|
-
/**
|
|
106
|
-
* Host callback that expands a `subtree` tenant scope. Given the tenant root id and
|
|
107
|
-
* the seed IDs, return the subtree's IDs grouped by root ({@link TenantIdsByRoot}):
|
|
108
|
-
*
|
|
109
|
-
* - under `tenantRoot`: the seeds and any same-table descendants (e.g. child
|
|
110
|
-
* agencies through `agencies.parent_agency_id`);
|
|
111
|
-
* - under each descendant root the policy declares (`roots[].parent` or
|
|
112
|
-
* `hierarchy[]`): that root's IDs in the subtree.
|
|
113
|
-
*
|
|
114
|
-
* Pass it to `ask()` as {@link AskPipelineOptions.resolveTenantDescendants}.
|
|
115
|
-
*/
|
|
116
|
-
export type ResolveTenantDescendants = (tenantRoot: string, seedIds: readonly string[]) => Promise<TenantIdsByRoot> | TenantIdsByRoot;
|
|
117
102
|
export type AskPipelineOptions = {
|
|
118
103
|
question: string;
|
|
119
104
|
schema: AnyNormalizedSchema;
|
|
@@ -179,11 +164,19 @@ export type AskPipelineOptions = {
|
|
|
179
164
|
*/
|
|
180
165
|
tenantScope?: TenantScope;
|
|
181
166
|
/**
|
|
182
|
-
* SQL output mode for tenant placeholders. Default `"sql-
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* `
|
|
167
|
+
* SQL output mode for tenant placeholders. Default `"sql-params"`
|
|
168
|
+
* ({@link DEFAULT_TENANT_SQL_MODE}) replaces them with the dialect's driver
|
|
169
|
+
* markers (`$N` for Postgres/CockroachDB, `?` for MySQL/MariaDB/SQLite, `@pN` for
|
|
170
|
+
* SQL Server): run `sql` with `tenantParams`, or `unboundSql` with `params`, never
|
|
171
|
+
* with the two arrays concatenated. A custom `AskDialect`, or a `DialectSpec` whose
|
|
172
|
+
* `id` isn't built in, has no default: with a tenant scope it must set this
|
|
173
|
+
* (`"sql-params"` gives `$N` markers), or `ask()` throws `TenantScopeError`
|
|
174
|
+
* (`TENANT_SQL_MODE_REQUIRED`) before the model call, as it does on every dialect for
|
|
175
|
+
* a value that is neither `"sql-params"` nor `"sql-only"`.
|
|
176
|
+
* `"sql-only"` inlines the IDs as escaped literals, for a caller that can't bind
|
|
177
|
+
* parameters (a tool that takes one SQL string); it throws `TenantScopeError`
|
|
178
|
+
* (`UNESCAPABLE_TENANT_ID`) for a tenant ID that isn't printable ASCII or that
|
|
179
|
+
* contains a backslash.
|
|
187
180
|
*/
|
|
188
181
|
tenantSqlMode?: TenantSqlOutputMode;
|
|
189
182
|
/**
|
|
@@ -217,16 +210,17 @@ export type AskPipelineOptions = {
|
|
|
217
210
|
* `askdb.pipeline.sensitive_sql_warning` is logged, but `ask()` still resolves.
|
|
218
211
|
* `"strict"` throws {@link SensitiveReferenceError} instead. `"off"` skips the check.
|
|
219
212
|
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
213
|
+
* Covers SQL produced by *this* call. A rebind with `bindPreparedQuery()` runs the
|
|
214
|
+
* check again on the stored template; hosts that store and replay `sql` itself should
|
|
215
|
+
* call `validateSensitiveReferences` on that path.
|
|
222
216
|
*/
|
|
223
217
|
sensitiveGuardrailMode?: SensitiveGuardrailMode | "off";
|
|
224
218
|
};
|
|
225
219
|
export type AskPipelineResult = {
|
|
226
220
|
/**
|
|
227
221
|
* The model's bound SQL: business values inlined as literals. Tenant IDs are
|
|
228
|
-
*
|
|
229
|
-
*
|
|
222
|
+
* driver markers numbered from the first slot (`tenantSqlMode: "sql-params"`, the
|
|
223
|
+
* default) — execute it with `tenantParams` — or inlined literals (`"sql-only"`).
|
|
230
224
|
*/
|
|
231
225
|
sql: string;
|
|
232
226
|
/**
|
|
@@ -247,18 +241,24 @@ export type AskPipelineResult = {
|
|
|
247
241
|
preparedQuery?: PreparedQuery;
|
|
248
242
|
explain?: unknown;
|
|
249
243
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
244
|
+
* Every guardrail finding for the model's SQL, checked before AskDB renders it (its
|
|
245
|
+
* bound `sql` and `sql-unbound` block, with the `:tenant_<root>_ids` placeholders still
|
|
246
|
+
* in place), and the outcome: `allow`, or `warn` when a `warn`-mode check found
|
|
247
|
+
* something. A `deny` throws the check's typed error instead, with this verdict on it.
|
|
248
|
+
*/
|
|
249
|
+
verdict: GuardrailVerdict;
|
|
250
|
+
/**
|
|
251
|
+
* Sensitive-identifier guardrail result, derived from {@link verdict}. Present when the
|
|
252
|
+
* schema declares at least one `sensitive` table/column and `sensitiveGuardrailMode` is
|
|
253
|
+
* not `"off"`.
|
|
252
254
|
*/
|
|
253
255
|
sensitiveGuardrail?: SensitiveGuardrailResult;
|
|
254
256
|
/**
|
|
255
|
-
* Tenant guardrail result
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* throws `TenantGuardrailError` instead, so a returned result is always `passed`
|
|
261
|
-
* under `strict`.
|
|
257
|
+
* Tenant guardrail result, derived from {@link verdict}: its tenant findings, once each.
|
|
258
|
+
* `sql` and `unboundSql` differ from the checked forms only by the substituted tenant IDs
|
|
259
|
+
* or markers. Present whenever the schema has a tenant policy, for every dialect form.
|
|
260
|
+
* In `strict` mode a failure throws `TenantGuardrailError` instead, so a returned result
|
|
261
|
+
* is always `passed` under `strict`.
|
|
262
262
|
*/
|
|
263
263
|
tenantGuardrail?: import("./sql/tenant-guardrail.js").TenantGuardrailResult;
|
|
264
264
|
/**
|
package/dist/ask.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../src/ask.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,IAAI,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAKtD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../src/ask.ts"],"names":[],"mappings":"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"}
|