taurusdb-core 0.2.0 → 0.3.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.
@@ -3,6 +3,7 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { parseEnvProfile } from "./env-source.js";
5
5
  import { parseProfilesFile } from "./file-source.js";
6
+ import { withRedactedToString } from "./parsing.js";
6
7
  export function resolveDefaultProfilePath(config) {
7
8
  if (config.profilesPath) {
8
9
  return config.profilesPath;
@@ -67,6 +68,13 @@ export class SqlProfileLoader {
67
68
  mergedProfiles.set(name, profile);
68
69
  }
69
70
  }
71
+ if (mergedProfiles.size === 0) {
72
+ mergedProfiles.set("taurus_mcp", withRedactedToString({
73
+ name: "taurus_mcp",
74
+ engine: "mysql",
75
+ port: 3306,
76
+ }));
77
+ }
70
78
  const defaultDatasource = this.config.defaultDatasource ??
71
79
  fileDefaultDatasource ??
72
80
  (mergedProfiles.size === 1 ? mergedProfiles.keys().next().value : undefined);
@@ -5,6 +5,7 @@ export declare class RuntimeOverrideProfileLoader implements RuntimeTargetProfil
5
5
  private readonly runtimeTargets;
6
6
  constructor(base: ProfileLoader);
7
7
  setRuntimeTarget(name: string, target: RuntimeDataSourceTarget): void;
8
+ clearRuntimeUser(name: string): void;
8
9
  clearRuntimeTarget(name: string): void;
9
10
  clearAllRuntimeTargets(): void;
10
11
  getRuntimeTarget(name: string): RuntimeDataSourceTarget | undefined;
@@ -29,6 +29,14 @@ export class RuntimeOverrideProfileLoader {
29
29
  };
30
30
  this.runtimeTargets.set(name, next);
31
31
  }
32
+ clearRuntimeUser(name) {
33
+ const current = this.runtimeTargets.get(name);
34
+ if (!current) {
35
+ return;
36
+ }
37
+ const { user: _user, ...next } = current;
38
+ this.runtimeTargets.set(name, next);
39
+ }
32
40
  clearRuntimeTarget(name) {
33
41
  this.runtimeTargets.delete(name);
34
42
  }
@@ -31,7 +31,7 @@ export interface DataSourceProfile {
31
31
  host?: string;
32
32
  port: number;
33
33
  database?: string;
34
- user: UserCredential;
34
+ user?: UserCredential;
35
35
  tls?: TlsOptions;
36
36
  poolSize?: number;
37
37
  toString(): string;
@@ -51,6 +51,7 @@ export interface RuntimeDataSourceTarget {
51
51
  }
52
52
  export interface RuntimeTargetProfileLoader extends ProfileLoader {
53
53
  setRuntimeTarget(name: string, target: RuntimeDataSourceTarget): void;
54
+ clearRuntimeUser(name: string): void;
54
55
  clearRuntimeTarget(name: string): void;
55
56
  clearAllRuntimeTargets(): void;
56
57
  getRuntimeTarget(name: string): RuntimeDataSourceTarget | undefined;
@@ -1,6 +1,6 @@
1
1
  import { UnsupportedFeatureError } from "../capability/types.js";
2
2
  import { InMemoryConfirmationStore } from "../safety/confirmation-store.js";
3
- import { buildFlashbackSql, FlashbackNoViewError, flashbackReadonlyOptions, formatTimestamp, resolveRelativeTimestampFromBase, resolveFlashbackTimestamp, } from "../taurus/flashback.js";
3
+ import { buildFlashbackSql, FlashbackNoViewError, flashbackReadonlyOptions, formatTimestamp, normalizeFlashbackWhereClause, resolveRelativeTimestampFromBase, resolveFlashbackTimestamp, } from "../taurus/flashback.js";
4
4
  import { buildListRecycleBinSql, buildRestoreRecycleBinTableSql, recycleBinMutationOptions, recycleBinReadonlyOptions } from "../taurus/recycle-bin.js";
5
5
  import { normalizeSql, sqlHash } from "../utils/hash.js";
6
6
  function resolveConfirmationSql(input) {
@@ -90,7 +90,8 @@ async function buildFlashbackNoViewError(engine, ctx, input, database, requested
90
90
  }
91
91
  if (input.where?.trim()) {
92
92
  try {
93
- const updatedAtResult = await engine.executor.executeReadonly(`SELECT ${quoteIdentifier("updated_at")} FROM ${quoteIdentifier(database)}.${quoteIdentifier(input.table)} WHERE (${input.where.trim()}) LIMIT 1`, ctx, { maxRows: 1, maxColumns: 1, maxFieldChars: 128 });
93
+ const where = normalizeFlashbackWhereClause(input.where);
94
+ const updatedAtResult = await engine.executor.executeReadonly(`SELECT ${quoteIdentifier("updated_at")} FROM ${quoteIdentifier(database)}.${quoteIdentifier(input.table)} WHERE (${where}) LIMIT 1`, ctx, { maxRows: 1, maxColumns: 1, maxFieldChars: 128 });
94
95
  const updatedAtRow = firstRowAsObject(updatedAtResult);
95
96
  if (typeof updatedAtRow?.updated_at === "string") {
96
97
  details.current_row_updated_at = updatedAtRow.updated_at;
@@ -33,6 +33,10 @@ async function resolveTls(tls, secretResolver) {
33
33
  return resolved;
34
34
  }
35
35
  function selectCredential(profile, mode, _opts = {}) {
36
+ void mode;
37
+ if (!profile.user) {
38
+ throw new ConnectionPoolError(`Datasource "${profile.name}" does not define SQL credentials. Call begin_sql_login and complete the secure local login form before executing SQL.`);
39
+ }
36
40
  return profile.user;
37
41
  }
38
42
  async function resolveCredentialValue(ref, secretResolver, context) {
@@ -13,6 +13,7 @@ export interface FlashbackInput {
13
13
  columns?: string[];
14
14
  limit?: number;
15
15
  }
16
+ export declare function normalizeFlashbackWhereClause(where: string): string;
16
17
  export type FlashbackNoViewDetails = {
17
18
  database?: string;
18
19
  table?: string;
@@ -1,3 +1,5 @@
1
+ import { createSqlParser } from "../safety/parser/index.js";
2
+ import { normalizeSql } from "../utils/hash.js";
1
3
  const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
2
4
  const SQL_TIMESTAMP_PATTERN = /^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2}:\d{2})(?:\.\d{1,6})?$/;
3
5
  const RELATIVE_DURATION_PATTERN = /(\d+)\s*(ms|milliseconds?|s|sec|secs|seconds?|m|min|mins|minutes?|h|hr|hrs|hours?|d|days?)/gi;
@@ -30,6 +32,15 @@ function quoteIdentifier(identifier, fieldName) {
30
32
  }
31
33
  return `\`${identifier}\``;
32
34
  }
35
+ export function normalizeFlashbackWhereClause(where) {
36
+ const parser = createSqlParser("mysql");
37
+ const candidate = parser.normalize(`SELECT 1 FROM placeholder WHERE (${where})`);
38
+ const parsed = parser.parse(candidate.normalizedSql);
39
+ if (!parsed.ok || parsed.isMultiStatement || parsed.ast.kind !== "select") {
40
+ throw new Error("Invalid flashback where clause. Provide a single SQL expression.");
41
+ }
42
+ return normalizeSql(where);
43
+ }
33
44
  export class FlashbackNoViewError extends Error {
34
45
  details;
35
46
  constructor(message, details) {
@@ -129,7 +140,7 @@ export function buildFlashbackSql(input, defaultDatabase, now = Date.now) {
129
140
  ];
130
141
  const whereClause = input.where?.trim();
131
142
  if (whereClause) {
132
- clauses.push(`WHERE (${whereClause})`);
143
+ clauses.push(`WHERE (${normalizeFlashbackWhereClause(whereClause)})`);
133
144
  }
134
145
  if (input.limit !== undefined) {
135
146
  if (!Number.isInteger(input.limit) || input.limit <= 0) {
@@ -137,7 +148,7 @@ export function buildFlashbackSql(input, defaultDatabase, now = Date.now) {
137
148
  }
138
149
  clauses.push(`LIMIT ${input.limit}`);
139
150
  }
140
- return clauses.join(" ");
151
+ return normalizeSql(clauses.join(" "));
141
152
  }
142
153
  export function flashbackReadonlyOptions(limit) {
143
154
  if (limit === undefined) {
@@ -8,6 +8,14 @@ const REDACT_PATHS = [
8
8
  "secret",
9
9
  "token",
10
10
  "*.token",
11
+ "accessKeyId",
12
+ "*.accessKeyId",
13
+ "secretAccessKey",
14
+ "*.secretAccessKey",
15
+ "securityToken",
16
+ "*.securityToken",
17
+ "authToken",
18
+ "*.authToken",
11
19
  ];
12
20
  function createDefaultDestination() {
13
21
  return pino.destination({ fd: 2, sync: false });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taurusdb-core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Shared TaurusDB data-plane engine for schema, SQL execution, guardrails, and diagnostics.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",