taurusdb-core 0.1.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 (170) hide show
  1. package/README.md +21 -0
  2. package/dist/auth/secret-resolver.d.ts +16 -0
  3. package/dist/auth/secret-resolver.js +64 -0
  4. package/dist/auth/sql-profile-loader/env-source.d.ts +3 -0
  5. package/dist/auth/sql-profile-loader/env-source.js +94 -0
  6. package/dist/auth/sql-profile-loader/file-source.d.ts +6 -0
  7. package/dist/auth/sql-profile-loader/file-source.js +40 -0
  8. package/dist/auth/sql-profile-loader/loader.d.ts +16 -0
  9. package/dist/auth/sql-profile-loader/loader.js +81 -0
  10. package/dist/auth/sql-profile-loader/parsing.d.ts +14 -0
  11. package/dist/auth/sql-profile-loader/parsing.js +216 -0
  12. package/dist/auth/sql-profile-loader/runtime-override.d.ts +14 -0
  13. package/dist/auth/sql-profile-loader/runtime-override.js +52 -0
  14. package/dist/auth/sql-profile-loader/types.d.ts +64 -0
  15. package/dist/auth/sql-profile-loader/types.js +1 -0
  16. package/dist/auth/sql-profile-loader.d.ts +4 -0
  17. package/dist/auth/sql-profile-loader.js +3 -0
  18. package/dist/capability/feature-matrix.d.ts +5 -0
  19. package/dist/capability/feature-matrix.js +237 -0
  20. package/dist/capability/probe.d.ts +19 -0
  21. package/dist/capability/probe.js +139 -0
  22. package/dist/capability/types.d.ts +49 -0
  23. package/dist/capability/types.js +16 -0
  24. package/dist/capability/version.d.ts +3 -0
  25. package/dist/capability/version.js +47 -0
  26. package/dist/cloud/auth.d.ts +26 -0
  27. package/dist/cloud/auth.js +198 -0
  28. package/dist/cloud/instances.d.ts +46 -0
  29. package/dist/cloud/instances.js +224 -0
  30. package/dist/config/env.d.ts +1 -0
  31. package/dist/config/env.js +194 -0
  32. package/dist/config/index.d.ts +6 -0
  33. package/dist/config/index.js +21 -0
  34. package/dist/config/redaction.d.ts +2 -0
  35. package/dist/config/redaction.js +19 -0
  36. package/dist/config/schema.d.ts +417 -0
  37. package/dist/config/schema.js +100 -0
  38. package/dist/context/datasource-resolver.d.ts +19 -0
  39. package/dist/context/datasource-resolver.js +71 -0
  40. package/dist/context/session-context.d.ts +26 -0
  41. package/dist/context/session-context.js +1 -0
  42. package/dist/diagnostics/metrics-source.d.ts +65 -0
  43. package/dist/diagnostics/metrics-source.js +280 -0
  44. package/dist/diagnostics/slow-sql-source/das-source.d.ts +43 -0
  45. package/dist/diagnostics/slow-sql-source/das-source.js +170 -0
  46. package/dist/diagnostics/slow-sql-source/factory.d.ts +5 -0
  47. package/dist/diagnostics/slow-sql-source/factory.js +87 -0
  48. package/dist/diagnostics/slow-sql-source/parsers.d.ts +7 -0
  49. package/dist/diagnostics/slow-sql-source/parsers.js +125 -0
  50. package/dist/diagnostics/slow-sql-source/taurus-api-source.d.ts +42 -0
  51. package/dist/diagnostics/slow-sql-source/taurus-api-source.js +149 -0
  52. package/dist/diagnostics/slow-sql-source/types.d.ts +40 -0
  53. package/dist/diagnostics/slow-sql-source/types.js +1 -0
  54. package/dist/diagnostics/slow-sql-source/utils.d.ts +20 -0
  55. package/dist/diagnostics/slow-sql-source/utils.js +170 -0
  56. package/dist/diagnostics/slow-sql-source.d.ts +4 -0
  57. package/dist/diagnostics/slow-sql-source.js +3 -0
  58. package/dist/diagnostics/types.d.ts +189 -0
  59. package/dist/diagnostics/types.js +39 -0
  60. package/dist/engine/data-access/locks.d.ts +8 -0
  61. package/dist/engine/data-access/locks.js +146 -0
  62. package/dist/engine/data-access/processlist.d.ts +4 -0
  63. package/dist/engine/data-access/processlist.js +56 -0
  64. package/dist/engine/data-access/statements.d.ts +10 -0
  65. package/dist/engine/data-access/statements.js +203 -0
  66. package/dist/engine/data-access/storage.d.ts +6 -0
  67. package/dist/engine/data-access/storage.js +96 -0
  68. package/dist/engine/data-access.d.ts +4 -0
  69. package/dist/engine/data-access.js +4 -0
  70. package/dist/engine/diagnostics.d.ts +7 -0
  71. package/dist/engine/diagnostics.js +7 -0
  72. package/dist/engine/helper-modules/diagnostics.d.ts +57 -0
  73. package/dist/engine/helper-modules/diagnostics.js +322 -0
  74. package/dist/engine/helper-modules/parsers.d.ts +13 -0
  75. package/dist/engine/helper-modules/parsers.js +283 -0
  76. package/dist/engine/helper-modules/sql.d.ts +12 -0
  77. package/dist/engine/helper-modules/sql.js +119 -0
  78. package/dist/engine/helper-modules/types.d.ts +103 -0
  79. package/dist/engine/helper-modules/types.js +1 -0
  80. package/dist/engine/helpers.d.ts +4 -0
  81. package/dist/engine/helpers.js +4 -0
  82. package/dist/engine/runtime.d.ts +20 -0
  83. package/dist/engine/runtime.js +385 -0
  84. package/dist/engine/types.d.ts +125 -0
  85. package/dist/engine/types.js +1 -0
  86. package/dist/engine/workflows/connection-spike.d.ts +4 -0
  87. package/dist/engine/workflows/connection-spike.js +316 -0
  88. package/dist/engine/workflows/db-hotspot.d.ts +4 -0
  89. package/dist/engine/workflows/db-hotspot.js +182 -0
  90. package/dist/engine/workflows/lock-contention-helpers/entities.d.ts +9 -0
  91. package/dist/engine/workflows/lock-contention-helpers/entities.js +58 -0
  92. package/dist/engine/workflows/lock-contention-helpers/no-match.d.ts +3 -0
  93. package/dist/engine/workflows/lock-contention-helpers/no-match.js +65 -0
  94. package/dist/engine/workflows/lock-contention-helpers/report.d.ts +21 -0
  95. package/dist/engine/workflows/lock-contention-helpers/report.js +104 -0
  96. package/dist/engine/workflows/lock-contention-helpers/root-cause.d.ts +4 -0
  97. package/dist/engine/workflows/lock-contention-helpers/root-cause.js +79 -0
  98. package/dist/engine/workflows/lock-contention-helpers/signals.d.ts +22 -0
  99. package/dist/engine/workflows/lock-contention-helpers/signals.js +34 -0
  100. package/dist/engine/workflows/lock-contention-helpers.d.ts +5 -0
  101. package/dist/engine/workflows/lock-contention-helpers.js +5 -0
  102. package/dist/engine/workflows/lock-contention.d.ts +4 -0
  103. package/dist/engine/workflows/lock-contention.js +67 -0
  104. package/dist/engine/workflows/service-latency.d.ts +4 -0
  105. package/dist/engine/workflows/service-latency.js +262 -0
  106. package/dist/engine/workflows/slow-query-helpers.d.ts +41 -0
  107. package/dist/engine/workflows/slow-query-helpers.js +253 -0
  108. package/dist/engine/workflows/slow-query.d.ts +4 -0
  109. package/dist/engine/workflows/slow-query.js +156 -0
  110. package/dist/engine/workflows/storage-pressure-helpers.d.ts +12 -0
  111. package/dist/engine/workflows/storage-pressure-helpers.js +281 -0
  112. package/dist/engine/workflows/storage-pressure.d.ts +4 -0
  113. package/dist/engine/workflows/storage-pressure.js +27 -0
  114. package/dist/engine/workflows/top-slow-sql.d.ts +4 -0
  115. package/dist/engine/workflows/top-slow-sql.js +222 -0
  116. package/dist/engine.d.ts +77 -0
  117. package/dist/engine.js +240 -0
  118. package/dist/executor/adapters/mysql.d.ts +2 -0
  119. package/dist/executor/adapters/mysql.js +114 -0
  120. package/dist/executor/connection-pool.d.ts +105 -0
  121. package/dist/executor/connection-pool.js +236 -0
  122. package/dist/executor/explain.d.ts +5 -0
  123. package/dist/executor/explain.js +119 -0
  124. package/dist/executor/query-tracker.d.ts +45 -0
  125. package/dist/executor/query-tracker.js +83 -0
  126. package/dist/executor/result-normalizer.d.ts +6 -0
  127. package/dist/executor/result-normalizer.js +47 -0
  128. package/dist/executor/sql-executor.d.ts +32 -0
  129. package/dist/executor/sql-executor.js +250 -0
  130. package/dist/executor/types.d.ts +70 -0
  131. package/dist/executor/types.js +1 -0
  132. package/dist/index.d.ts +40 -0
  133. package/dist/index.js +21 -0
  134. package/dist/safety/confirmation-store.d.ts +44 -0
  135. package/dist/safety/confirmation-store.js +130 -0
  136. package/dist/safety/guardrail.d.ts +39 -0
  137. package/dist/safety/guardrail.js +99 -0
  138. package/dist/safety/parser/adapter.d.ts +10 -0
  139. package/dist/safety/parser/adapter.js +72 -0
  140. package/dist/safety/parser/ast-utils.d.ts +10 -0
  141. package/dist/safety/parser/ast-utils.js +167 -0
  142. package/dist/safety/parser/features.d.ts +12 -0
  143. package/dist/safety/parser/features.js +113 -0
  144. package/dist/safety/parser/index.d.ts +2 -0
  145. package/dist/safety/parser/index.js +1 -0
  146. package/dist/safety/parser/types.d.ts +76 -0
  147. package/dist/safety/parser/types.js +1 -0
  148. package/dist/safety/redaction.d.ts +34 -0
  149. package/dist/safety/redaction.js +186 -0
  150. package/dist/safety/sql-classifier.d.ts +19 -0
  151. package/dist/safety/sql-classifier.js +43 -0
  152. package/dist/safety/sql-validator.d.ts +19 -0
  153. package/dist/safety/sql-validator.js +143 -0
  154. package/dist/schema/adapters/mysql.d.ts +16 -0
  155. package/dist/schema/adapters/mysql.js +287 -0
  156. package/dist/schema/introspector.d.ts +70 -0
  157. package/dist/schema/introspector.js +40 -0
  158. package/dist/taurus/flashback.d.ts +36 -0
  159. package/dist/taurus/flashback.js +149 -0
  160. package/dist/taurus/recycle-bin.d.ts +14 -0
  161. package/dist/taurus/recycle-bin.js +61 -0
  162. package/dist/utils/formatter.d.ts +70 -0
  163. package/dist/utils/formatter.js +60 -0
  164. package/dist/utils/hash.d.ts +2 -0
  165. package/dist/utils/hash.js +247 -0
  166. package/dist/utils/id.d.ts +2 -0
  167. package/dist/utils/id.js +11 -0
  168. package/dist/utils/logger.d.ts +9 -0
  169. package/dist/utils/logger.js +39 -0
  170. package/package.json +46 -0
@@ -0,0 +1,40 @@
1
+ export { TaurusDBEngine } from "./engine.js";
2
+ export type { EnhancedExplainResult, ConfirmationOutcome, DataSourceInfo, IssueConfirmationInput, TaurusDBEngineCreateOptions, TaurusDBEngineDeps, } from "./engine.js";
3
+ export { createConfigFromEnv, getConfig, redactConfigForLog, resetConfigForTests, } from "./config/index.js";
4
+ export type { Config } from "./config/index.js";
5
+ export { createSqlProfileLoader, RuntimeOverrideProfileLoader, } from "./auth/sql-profile-loader.js";
6
+ export type { DataSourceProfile, DatabaseEngine, ProfileLoader, RuntimeDataSourceTarget, RuntimeTargetProfileLoader, TlsOptions, } from "./auth/sql-profile-loader.js";
7
+ export { DatasourceResolutionError } from "./context/datasource-resolver.js";
8
+ export type { DatasourceResolveInput, DatasourceResolver, RuntimeLimits, SessionContext, } from "./context/session-context.js";
9
+ export { ConnectionPoolError } from "./executor/connection-pool.js";
10
+ export type { CancelResult, ExplainResult, MutationOptions, MutationResult, QueryResult, QueryStatus, ReadonlyOptions, SqlExecutor, } from "./executor/sql-executor.js";
11
+ export { createCapabilityProbe } from "./capability/probe.js";
12
+ export type { CapabilityProbe } from "./capability/probe.js";
13
+ export { UnsupportedFeatureError } from "./capability/types.js";
14
+ export type { CapabilitySnapshot, FeatureMatrix, FeatureStatus, KernelInfo, TaurusFeatureName, } from "./capability/types.js";
15
+ export { createConfirmationStore, InMemoryConfirmationStore, } from "./safety/confirmation-store.js";
16
+ export type { ConfirmationStore, ConfirmationToken, ConfirmationValidationResult, IssueInput, } from "./safety/confirmation-store.js";
17
+ export { createGuardrail } from "./safety/guardrail.js";
18
+ export type { Guardrail, GuardrailDecision, GuardrailRuntimeLimits, InspectInput, } from "./safety/guardrail.js";
19
+ export type { ExplainRiskSummary, RiskLevel, ValidationResult } from "./safety/sql-validator.js";
20
+ export { SchemaIntrospectionError } from "./schema/introspector.js";
21
+ export type { ColumnInfo, DatabaseInfo, IndexInfo, SchemaIntrospector, TableInfo, TableSchema, } from "./schema/introspector.js";
22
+ export { ErrorCode, formatBlocked, formatConfirmationRequired, formatError, formatSuccess, } from "./utils/formatter.js";
23
+ export type { ErrorCode as ErrorCodeValue, ResponseMetadata, StatementType, ToolError, ToolResponse, } from "./utils/formatter.js";
24
+ export { normalizeSql, sqlHash } from "./utils/hash.js";
25
+ export { generateQueryId, generateTaskId } from "./utils/id.js";
26
+ export { logger, withTaskContext } from "./utils/logger.js";
27
+ export { CloudTaurusInstanceClient, createCloudTaurusInstanceClient, } from "./cloud/instances.js";
28
+ export type { CloudTaurusInstanceSummary, ListCloudTaurusInstancesInput, } from "./cloud/instances.js";
29
+ export { canAuthenticateHuaweiCloudRequests, fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, hasHuaweiCloudCredentialAuth, inferHuaweiCloudRegionFromEndpoint, resolveHuaweiCloudProjectId, } from "./cloud/auth.js";
30
+ export type { HuaweiCloudAuthOptions } from "./cloud/auth.js";
31
+ export { FlashbackNoViewError, formatTimestamp as formatFlashbackTimestamp, } from "./taurus/flashback.js";
32
+ export type { FlashbackInput, FlashbackNoViewDetails, } from "./taurus/flashback.js";
33
+ export { buildListRecycleBinSql, buildRestoreRecycleBinTableSql, RECYCLE_BIN_DATABASE, } from "./taurus/recycle-bin.js";
34
+ export type { RecycleBinListResult, RecycleBinRestoreResult, RestoreRecycleBinTableInput, } from "./taurus/recycle-bin.js";
35
+ export { createPlaceholderDiagnosticResult } from "./diagnostics/types.js";
36
+ export { buildResolveSlowSqlInput, createSlowSqlSource, TaurusApiSlowSqlSource, } from "./diagnostics/slow-sql-source.js";
37
+ export { CesMetricsSource, createMetricsSource, } from "./diagnostics/metrics-source.js";
38
+ export type { DbHotspotItem, DbHotspotResult, DiagnosticBaseInput, DiagnosticConfidence, DiagnosticEvidenceItem, DiagnosticEvidenceLevel, DiagnosticNextToolInput, DiagnoseDbHotspotInput, DiagnoseServiceLatencyInput, FindTopSlowSqlInput, FindTopSlowSqlResult, DiagnosticResult, DiagnosticRootCauseCandidate, DiagnosticSeverity, DiagnosticStatus, ServiceLatencyCandidate, ServiceLatencyResult, ServiceLatencySuspectedCategory, DiagnosticSuspiciousEntities, DiagnosticToolName, DiagnosisWindow, DiagnoseConnectionSpikeInput, DiagnoseLockContentionInput, DiagnoseSlowQueryInput, DiagnoseStoragePressureInput, PlaceholderDiagnosticOptions, TopSlowSqlItem, } from "./diagnostics/types.js";
39
+ export type { ExternalSlowSqlSample, ResolveSlowSqlInput, SlowSqlSource, } from "./diagnostics/slow-sql-source.js";
40
+ export type { MetricAlias, MetricPoint, MetricSummary, MetricsSource, QueryMetricsInput, } from "./diagnostics/metrics-source.js";
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ export { TaurusDBEngine } from "./engine.js";
2
+ export { createConfigFromEnv, getConfig, redactConfigForLog, resetConfigForTests, } from "./config/index.js";
3
+ export { createSqlProfileLoader, RuntimeOverrideProfileLoader, } from "./auth/sql-profile-loader.js";
4
+ export { DatasourceResolutionError } from "./context/datasource-resolver.js";
5
+ export { ConnectionPoolError } from "./executor/connection-pool.js";
6
+ export { createCapabilityProbe } from "./capability/probe.js";
7
+ export { UnsupportedFeatureError } from "./capability/types.js";
8
+ export { createConfirmationStore, InMemoryConfirmationStore, } from "./safety/confirmation-store.js";
9
+ export { createGuardrail } from "./safety/guardrail.js";
10
+ export { SchemaIntrospectionError } from "./schema/introspector.js";
11
+ export { ErrorCode, formatBlocked, formatConfirmationRequired, formatError, formatSuccess, } from "./utils/formatter.js";
12
+ export { normalizeSql, sqlHash } from "./utils/hash.js";
13
+ export { generateQueryId, generateTaskId } from "./utils/id.js";
14
+ export { logger, withTaskContext } from "./utils/logger.js";
15
+ export { CloudTaurusInstanceClient, createCloudTaurusInstanceClient, } from "./cloud/instances.js";
16
+ export { canAuthenticateHuaweiCloudRequests, fetchHuaweiCloud, getHuaweiCloudAuthFromConfig, hasHuaweiCloudCredentialAuth, inferHuaweiCloudRegionFromEndpoint, resolveHuaweiCloudProjectId, } from "./cloud/auth.js";
17
+ export { FlashbackNoViewError, formatTimestamp as formatFlashbackTimestamp, } from "./taurus/flashback.js";
18
+ export { buildListRecycleBinSql, buildRestoreRecycleBinTableSql, RECYCLE_BIN_DATABASE, } from "./taurus/recycle-bin.js";
19
+ export { createPlaceholderDiagnosticResult } from "./diagnostics/types.js";
20
+ export { buildResolveSlowSqlInput, createSlowSqlSource, TaurusApiSlowSqlSource, } from "./diagnostics/slow-sql-source.js";
21
+ export { CesMetricsSource, createMetricsSource, } from "./diagnostics/metrics-source.js";
@@ -0,0 +1,44 @@
1
+ import type { SessionContext } from "../context/session-context.js";
2
+ import type { RiskLevel, ValidationResult } from "./sql-validator.js";
3
+ export type IssueInput = {
4
+ sqlHash: string;
5
+ normalizedSql: string;
6
+ context: SessionContext;
7
+ riskLevel: RiskLevel;
8
+ ttlSeconds?: number;
9
+ };
10
+ export type ConfirmationToken = {
11
+ token: string;
12
+ issuedAt: number;
13
+ expiresAt: number;
14
+ };
15
+ export type ConfirmationValidationResult = ValidationResult & {
16
+ valid: boolean;
17
+ reason?: string;
18
+ };
19
+ export interface ConfirmationStore {
20
+ issue(input: IssueInput): Promise<ConfirmationToken>;
21
+ validate(token: string, currentSql: string, ctx: SessionContext): Promise<ConfirmationValidationResult>;
22
+ revoke(token: string): Promise<void>;
23
+ }
24
+ export type ConfirmationStoreOptions = {
25
+ ttlSeconds?: number;
26
+ cleanupIntervalMs?: number;
27
+ now?: () => number;
28
+ randomBytesFn?: (size: number) => Buffer;
29
+ };
30
+ export declare class InMemoryConfirmationStore implements ConfirmationStore {
31
+ private readonly entries;
32
+ private readonly now;
33
+ private readonly ttlSeconds;
34
+ private readonly randomBytesFn;
35
+ private cleanupTimer?;
36
+ constructor(options?: ConfirmationStoreOptions);
37
+ issue(input: IssueInput): Promise<ConfirmationToken>;
38
+ validate(token: string, currentSql: string, ctx: SessionContext): Promise<ConfirmationValidationResult>;
39
+ revoke(token: string): Promise<void>;
40
+ stop(): void;
41
+ cleanupExpired(now?: number): void;
42
+ private generateUniqueToken;
43
+ }
44
+ export declare function createConfirmationStore(options?: ConfirmationStoreOptions): ConfirmationStore;
@@ -0,0 +1,130 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { normalizeSql, sqlHash } from "../utils/hash.js";
3
+ const DEFAULT_TTL_SECONDS = 300;
4
+ const DEFAULT_CLEANUP_INTERVAL_MS = 60_000;
5
+ const TOKEN_PREFIX = "ctok_";
6
+ function allowResult() {
7
+ return {
8
+ valid: true,
9
+ action: "allow",
10
+ riskLevel: "low",
11
+ reasonCodes: [],
12
+ riskHints: [],
13
+ };
14
+ }
15
+ function blockResult(code, message) {
16
+ return {
17
+ valid: false,
18
+ action: "block",
19
+ riskLevel: "blocked",
20
+ reason: message,
21
+ reasonCodes: [code],
22
+ riskHints: [message],
23
+ };
24
+ }
25
+ function normalizeDatabase(value) {
26
+ if (typeof value !== "string") {
27
+ return undefined;
28
+ }
29
+ const trimmed = value.trim();
30
+ return trimmed.length > 0 ? trimmed : undefined;
31
+ }
32
+ function parseTtlSeconds(ttlSeconds, fallback) {
33
+ const resolved = ttlSeconds ?? fallback;
34
+ if (!Number.isInteger(resolved) || resolved <= 0) {
35
+ throw new Error(`Invalid ttlSeconds: ${ttlSeconds}. It must be a positive integer.`);
36
+ }
37
+ return resolved;
38
+ }
39
+ export class InMemoryConfirmationStore {
40
+ entries = new Map();
41
+ now;
42
+ ttlSeconds;
43
+ randomBytesFn;
44
+ cleanupTimer;
45
+ constructor(options = {}) {
46
+ this.now = options.now ?? Date.now;
47
+ this.ttlSeconds = parseTtlSeconds(options.ttlSeconds, DEFAULT_TTL_SECONDS);
48
+ this.randomBytesFn = options.randomBytesFn ?? randomBytes;
49
+ const cleanupIntervalMs = options.cleanupIntervalMs ?? DEFAULT_CLEANUP_INTERVAL_MS;
50
+ if (cleanupIntervalMs > 0) {
51
+ this.cleanupTimer = setInterval(() => this.cleanupExpired(), cleanupIntervalMs);
52
+ this.cleanupTimer.unref();
53
+ }
54
+ }
55
+ async issue(input) {
56
+ this.cleanupExpired();
57
+ const ttlSeconds = parseTtlSeconds(input.ttlSeconds, this.ttlSeconds);
58
+ const issuedAt = this.now();
59
+ const expiresAt = issuedAt + ttlSeconds * 1000;
60
+ const token = this.generateUniqueToken();
61
+ this.entries.set(token, {
62
+ token,
63
+ sqlHash: input.sqlHash,
64
+ normalizedSql: input.normalizedSql,
65
+ datasource: input.context.datasource,
66
+ database: normalizeDatabase(input.context.database),
67
+ riskLevel: input.riskLevel,
68
+ issuedAt,
69
+ expiresAt,
70
+ });
71
+ return {
72
+ token,
73
+ issuedAt,
74
+ expiresAt,
75
+ };
76
+ }
77
+ async validate(token, currentSql, ctx) {
78
+ const entry = this.entries.get(token);
79
+ if (!entry) {
80
+ return blockResult("CF001", "Confirmation token not found.");
81
+ }
82
+ const now = this.now();
83
+ if (entry.expiresAt <= now) {
84
+ this.entries.delete(token);
85
+ return blockResult("CF002", "Confirmation token has expired.");
86
+ }
87
+ if (entry.usedAt !== undefined) {
88
+ return blockResult("CF005", "Confirmation token has already been used.");
89
+ }
90
+ const normalizedCurrentSql = normalizeSql(currentSql);
91
+ const currentSqlHash = sqlHash(normalizedCurrentSql);
92
+ if (currentSqlHash !== entry.sqlHash) {
93
+ return blockResult("CF003", "SQL hash mismatch for confirmation token.");
94
+ }
95
+ const currentDatabase = normalizeDatabase(ctx.database);
96
+ if (ctx.datasource !== entry.datasource || currentDatabase !== entry.database) {
97
+ return blockResult("CF004", "Datasource or database mismatch for confirmation token.");
98
+ }
99
+ entry.usedAt = now;
100
+ return allowResult();
101
+ }
102
+ async revoke(token) {
103
+ this.entries.delete(token);
104
+ }
105
+ stop() {
106
+ if (this.cleanupTimer) {
107
+ clearInterval(this.cleanupTimer);
108
+ this.cleanupTimer = undefined;
109
+ }
110
+ }
111
+ cleanupExpired(now = this.now()) {
112
+ for (const [token, entry] of this.entries.entries()) {
113
+ if (entry.expiresAt <= now) {
114
+ this.entries.delete(token);
115
+ }
116
+ }
117
+ }
118
+ generateUniqueToken() {
119
+ for (let i = 0; i < 5; i += 1) {
120
+ const token = `${TOKEN_PREFIX}${this.randomBytesFn(32).toString("base64url")}`;
121
+ if (!this.entries.has(token)) {
122
+ return token;
123
+ }
124
+ }
125
+ throw new Error("Unable to generate unique confirmation token.");
126
+ }
127
+ }
128
+ export function createConfirmationStore(options = {}) {
129
+ return new InMemoryConfirmationStore(options);
130
+ }
@@ -0,0 +1,39 @@
1
+ import type { DatabaseEngine } from "../auth/sql-profile-loader.js";
2
+ import type { SessionContext } from "../context/session-context.js";
3
+ import { type SqlParser } from "./parser/index.js";
4
+ import { type RiskLevel } from "./sql-validator.js";
5
+ export interface GuardrailRuntimeLimits {
6
+ readonly: boolean;
7
+ timeoutMs: number;
8
+ maxRows: number;
9
+ maxColumns: number;
10
+ maxFieldChars: number;
11
+ }
12
+ export interface GuardrailDecision {
13
+ action: "allow" | "confirm" | "block";
14
+ riskLevel: RiskLevel;
15
+ reasonCodes: string[];
16
+ riskHints: string[];
17
+ normalizedSql: string;
18
+ sqlHash: string;
19
+ requiresExplain: boolean;
20
+ requiresConfirmation: boolean;
21
+ runtimeLimits: GuardrailRuntimeLimits;
22
+ }
23
+ export interface InspectInput {
24
+ toolName: string;
25
+ sql: string;
26
+ context: SessionContext;
27
+ }
28
+ export interface Guardrail {
29
+ inspect(input: InspectInput): Promise<GuardrailDecision>;
30
+ }
31
+ export type GuardrailOptions = {
32
+ parserFactory?: (engine: DatabaseEngine) => SqlParser;
33
+ };
34
+ export declare class GuardrailImpl implements Guardrail {
35
+ private readonly parserFactory;
36
+ constructor(options?: GuardrailOptions);
37
+ inspect(input: InspectInput): Promise<GuardrailDecision>;
38
+ }
39
+ export declare function createGuardrail(options?: GuardrailOptions): Guardrail;
@@ -0,0 +1,99 @@
1
+ import { createSqlParser } from "./parser/index.js";
2
+ import { classifySql } from "./sql-classifier.js";
3
+ import { validateStaticRules, validateToolScope, } from "./sql-validator.js";
4
+ function dedupeCaseInsensitive(values) {
5
+ const output = [];
6
+ const seen = new Set();
7
+ for (const raw of values) {
8
+ const value = raw.trim();
9
+ if (!value) {
10
+ continue;
11
+ }
12
+ const key = value.toLowerCase();
13
+ if (seen.has(key)) {
14
+ continue;
15
+ }
16
+ seen.add(key);
17
+ output.push(value);
18
+ }
19
+ return output;
20
+ }
21
+ function pickHigherRisk(a, b) {
22
+ const order = {
23
+ low: 0,
24
+ medium: 1,
25
+ high: 2,
26
+ blocked: 3,
27
+ };
28
+ return order[b] > order[a] ? b : a;
29
+ }
30
+ function pickDominantAction(current, next) {
31
+ if (current === "block" || next === "block") {
32
+ return "block";
33
+ }
34
+ if (current === "confirm" || next === "confirm") {
35
+ return "confirm";
36
+ }
37
+ return "allow";
38
+ }
39
+ function buildBaseDecision(input, normalizedSql, sqlHash, action, riskLevel, reasonCodes, riskHints, requiresExplain) {
40
+ const readonlyByTool = input.toolName === "execute_readonly_sql" || input.toolName === "explain_sql";
41
+ const readonly = input.context.limits.readonly || readonlyByTool;
42
+ return {
43
+ action,
44
+ riskLevel,
45
+ reasonCodes: dedupeCaseInsensitive(reasonCodes),
46
+ riskHints: dedupeCaseInsensitive(riskHints),
47
+ normalizedSql,
48
+ sqlHash,
49
+ requiresExplain,
50
+ requiresConfirmation: action === "confirm",
51
+ runtimeLimits: {
52
+ readonly,
53
+ timeoutMs: input.context.limits.timeoutMs,
54
+ maxRows: input.context.limits.maxRows,
55
+ maxColumns: input.context.limits.maxColumns,
56
+ maxFieldChars: input.context.limits.maxFieldChars ?? 2048,
57
+ },
58
+ };
59
+ }
60
+ function mergeDecision(input, normalizedSql, sqlHash, validations, requiresExplain) {
61
+ let action = "allow";
62
+ let riskLevel = "low";
63
+ const reasonCodes = [];
64
+ const riskHints = [];
65
+ for (const validation of validations) {
66
+ action = pickDominantAction(action, validation.action);
67
+ riskLevel = pickHigherRisk(riskLevel, validation.riskLevel);
68
+ reasonCodes.push(...validation.reasonCodes);
69
+ riskHints.push(...validation.riskHints);
70
+ }
71
+ return buildBaseDecision(input, normalizedSql, sqlHash, action, riskLevel, reasonCodes, riskHints, requiresExplain);
72
+ }
73
+ export class GuardrailImpl {
74
+ parserFactory;
75
+ constructor(options = {}) {
76
+ this.parserFactory = options.parserFactory ?? ((engine) => createSqlParser(engine));
77
+ }
78
+ async inspect(input) {
79
+ const parser = this.parserFactory(input.context.engine);
80
+ const normalized = parser.normalize(input.sql);
81
+ const parseResult = parser.parse(normalized.normalizedSql);
82
+ if (!parseResult.ok) {
83
+ return buildBaseDecision(input, normalized.normalizedSql, normalized.sqlHash, "block", "blocked", ["G001"], [`SQL parse failed: ${parseResult.error.message}`], false);
84
+ }
85
+ const cls = classifySql(parseResult.ast, normalized, input.context.engine);
86
+ const d1 = validateToolScope(input.toolName, cls);
87
+ if (d1.action === "block") {
88
+ return mergeDecision(input, normalized.normalizedSql, normalized.sqlHash, [d1], false);
89
+ }
90
+ const d2 = validateStaticRules(cls);
91
+ if (d2.action === "block") {
92
+ return mergeDecision(input, normalized.normalizedSql, normalized.sqlHash, [d1, d2], false);
93
+ }
94
+ return mergeDecision(input, normalized.normalizedSql, normalized.sqlHash, [d1, d2], false);
95
+ }
96
+ }
97
+ export function createGuardrail(options = {}) {
98
+ return new GuardrailImpl(options);
99
+ }
@@ -0,0 +1,10 @@
1
+ import type { DatabaseEngine } from "../../auth/sql-profile-loader.js";
2
+ import type { NormalizedSql, ParseResult, SqlParser } from "./types.js";
3
+ export declare class NodeSqlParserAdapter implements SqlParser {
4
+ private readonly parser;
5
+ private readonly engine;
6
+ constructor(engine: DatabaseEngine);
7
+ normalize(sql: string): NormalizedSql;
8
+ parse(sql: string): ParseResult;
9
+ }
10
+ export declare function createSqlParser(engine: DatabaseEngine): SqlParser;
@@ -0,0 +1,72 @@
1
+ import nodeSqlParser from "node-sql-parser";
2
+ import { normalizeSql, sqlHash } from "../../utils/hash.js";
3
+ import { collectStructuredFeatures, scanAst } from "./features.js";
4
+ import { isObject, mapStatementType, normalizeColumnList, normalizeTableList, toParseError } from "./ast-utils.js";
5
+ const { Parser } = nodeSqlParser;
6
+ export class NodeSqlParserAdapter {
7
+ parser;
8
+ engine;
9
+ constructor(engine) {
10
+ this.parser = new Parser();
11
+ this.engine = engine;
12
+ }
13
+ normalize(sql) {
14
+ const normalizedSql = normalizeSql(sql);
15
+ return {
16
+ normalizedSql,
17
+ sqlHash: sqlHash(normalizedSql),
18
+ };
19
+ }
20
+ parse(sql) {
21
+ try {
22
+ const rawAst = this.parser.astify(sql, { database: this.engine });
23
+ const astEntries = Array.isArray(rawAst) ? rawAst : [rawAst];
24
+ const statements = astEntries
25
+ .filter((entry) => isObject(entry))
26
+ .map((entry) => entry);
27
+ const isMultiStatement = statements.length > 1;
28
+ const statementKind = statements.length === 1 ? mapStatementType(statements[0].type) : "unknown";
29
+ const tableList = this.parser.tableList(sql, { database: this.engine });
30
+ const columnList = this.parser.columnList(sql, { database: this.engine });
31
+ const scan = scanAst(statements);
32
+ const structured = collectStructuredFeatures(statements);
33
+ const ast = {
34
+ kind: statementKind,
35
+ tables: normalizeTableList(tableList),
36
+ columns: normalizeColumnList(columnList),
37
+ hasAggregate: scan.hasAggregate,
38
+ hasSubquery: scan.hasSubquery,
39
+ isMultiStatement,
40
+ };
41
+ if (structured.where) {
42
+ ast.where = structured.where;
43
+ }
44
+ if (structured.limit) {
45
+ ast.limit = structured.limit;
46
+ }
47
+ if (structured.joins.length > 0) {
48
+ ast.joins = structured.joins;
49
+ }
50
+ if (structured.orderBy.length > 0) {
51
+ ast.orderBy = structured.orderBy;
52
+ }
53
+ if (structured.groupBy.length > 0) {
54
+ ast.groupBy = structured.groupBy;
55
+ }
56
+ return {
57
+ ok: true,
58
+ ast,
59
+ isMultiStatement,
60
+ };
61
+ }
62
+ catch (error) {
63
+ return {
64
+ ok: false,
65
+ error: toParseError(error),
66
+ };
67
+ }
68
+ }
69
+ }
70
+ export function createSqlParser(engine) {
71
+ return new NodeSqlParserAdapter(engine);
72
+ }
@@ -0,0 +1,10 @@
1
+ import type { AstNode, ColumnRef, LimitNode, ParseError, StatementType, TableRef, WhereNode } from "./types.js";
2
+ export declare function isObject(value: unknown): value is AstNode;
3
+ export declare function mapStatementType(value: unknown): StatementType;
4
+ export declare function normalizeTableList(tableList: string[]): TableRef[];
5
+ export declare function normalizeColumnList(columnList: string[]): ColumnRef[];
6
+ export declare function readNumericValue(value: unknown): number | undefined;
7
+ export declare function toWhereNode(raw: unknown): WhereNode;
8
+ export declare function toLimitNode(raw: unknown): LimitNode;
9
+ export declare function getFunctionName(value: unknown): string | undefined;
10
+ export declare function toParseError(error: unknown): ParseError;
@@ -0,0 +1,167 @@
1
+ export function isObject(value) {
2
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3
+ }
4
+ export function mapStatementType(value) {
5
+ const type = typeof value === "string" ? value.toLowerCase() : "";
6
+ switch (type) {
7
+ case "select":
8
+ return "select";
9
+ case "show":
10
+ return "show";
11
+ case "explain":
12
+ return "explain";
13
+ case "desc":
14
+ case "describe":
15
+ return "describe";
16
+ case "insert":
17
+ case "replace":
18
+ return "insert";
19
+ case "update":
20
+ return "update";
21
+ case "delete":
22
+ return "delete";
23
+ case "alter":
24
+ return "alter";
25
+ case "drop":
26
+ return "drop";
27
+ case "create":
28
+ return "create";
29
+ case "grant":
30
+ return "grant";
31
+ case "revoke":
32
+ return "revoke";
33
+ case "truncate":
34
+ return "truncate";
35
+ case "set":
36
+ return "set";
37
+ case "use":
38
+ return "use";
39
+ default:
40
+ return "unknown";
41
+ }
42
+ }
43
+ export function normalizeTableList(tableList) {
44
+ const items = [];
45
+ const seen = new Set();
46
+ for (const entry of tableList) {
47
+ const parts = entry.split("::");
48
+ if (parts.length < 3) {
49
+ continue;
50
+ }
51
+ const schema = parts[1] && parts[1] !== "null" ? parts[1] : undefined;
52
+ const name = parts.slice(2).join("::").trim();
53
+ if (!name) {
54
+ continue;
55
+ }
56
+ const dedupeKey = `${schema ?? ""}.${name}`.toLowerCase();
57
+ if (seen.has(dedupeKey)) {
58
+ continue;
59
+ }
60
+ seen.add(dedupeKey);
61
+ items.push({ name, schema });
62
+ }
63
+ return items;
64
+ }
65
+ export function normalizeColumnList(columnList) {
66
+ const items = [];
67
+ const seen = new Set();
68
+ for (const entry of columnList) {
69
+ const parts = entry.split("::");
70
+ if (parts.length < 3) {
71
+ continue;
72
+ }
73
+ const table = parts[1] && parts[1] !== "null" ? parts[1] : undefined;
74
+ const name = parts.slice(2).join("::").trim();
75
+ if (!name) {
76
+ continue;
77
+ }
78
+ const dedupeKey = `${table ?? ""}.${name}`.toLowerCase();
79
+ if (seen.has(dedupeKey)) {
80
+ continue;
81
+ }
82
+ seen.add(dedupeKey);
83
+ items.push({ name, table });
84
+ }
85
+ return items;
86
+ }
87
+ export function readNumericValue(value) {
88
+ if (typeof value === "number" && Number.isFinite(value)) {
89
+ return value;
90
+ }
91
+ if (typeof value === "string" && value.trim().length > 0) {
92
+ const parsed = Number(value);
93
+ if (Number.isFinite(parsed)) {
94
+ return parsed;
95
+ }
96
+ }
97
+ return undefined;
98
+ }
99
+ export function toWhereNode(raw) {
100
+ const type = isObject(raw) && typeof raw.type === "string" ? raw.type.toLowerCase() : "";
101
+ return {
102
+ kind: type === "binary_expr" ? "binary" : "expression",
103
+ raw,
104
+ };
105
+ }
106
+ export function toLimitNode(raw) {
107
+ if (!isObject(raw) || !Array.isArray(raw.value)) {
108
+ return { raw };
109
+ }
110
+ const values = raw.value
111
+ .map((entry) => {
112
+ if (!isObject(entry)) {
113
+ return undefined;
114
+ }
115
+ return readNumericValue(entry.value);
116
+ })
117
+ .filter((value) => value !== undefined);
118
+ const rowCount = values.length > 0 ? values[values.length - 1] : undefined;
119
+ const offset = values.length > 1 ? values[0] : undefined;
120
+ return {
121
+ raw,
122
+ rowCount,
123
+ offset,
124
+ };
125
+ }
126
+ export function getFunctionName(value) {
127
+ if (typeof value === "string") {
128
+ return value;
129
+ }
130
+ if (Array.isArray(value)) {
131
+ const token = value
132
+ .map((entry) => {
133
+ if (typeof entry === "string") {
134
+ return entry;
135
+ }
136
+ if (isObject(entry) && typeof entry.value === "string") {
137
+ return entry.value;
138
+ }
139
+ return undefined;
140
+ })
141
+ .find((entry) => typeof entry === "string");
142
+ return token;
143
+ }
144
+ if (isObject(value)) {
145
+ if (typeof value.name === "string") {
146
+ return value.name;
147
+ }
148
+ return getFunctionName(value.name);
149
+ }
150
+ return undefined;
151
+ }
152
+ export function toParseError(error) {
153
+ const typed = error;
154
+ const message = typed instanceof Error ? typed.message : String(error);
155
+ const line = typed.location?.start?.line;
156
+ const column = typed.location?.start?.column;
157
+ return {
158
+ code: "SQL_PARSE_ERROR",
159
+ message,
160
+ position: typeof line === "number" && typeof column === "number"
161
+ ? {
162
+ line,
163
+ column,
164
+ }
165
+ : undefined,
166
+ };
167
+ }
@@ -0,0 +1,12 @@
1
+ import type { AstNode, GroupByNode, JoinNode, LimitNode, OrderByNode, WhereNode } from "./types.js";
2
+ export declare function scanAst(statements: AstNode[]): {
3
+ hasAggregate: boolean;
4
+ hasSubquery: boolean;
5
+ };
6
+ export declare function collectStructuredFeatures(statements: AstNode[]): {
7
+ where?: WhereNode;
8
+ limit?: LimitNode;
9
+ joins: JoinNode[];
10
+ orderBy: OrderByNode[];
11
+ groupBy: GroupByNode[];
12
+ };