taurusdb-core 0.1.0 → 0.2.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.
- package/dist/auth/sql-profile-loader/env-source.js +15 -30
- package/dist/auth/sql-profile-loader/parsing.js +10 -9
- package/dist/auth/sql-profile-loader/runtime-override.js +13 -7
- package/dist/auth/sql-profile-loader/types.d.ts +4 -3
- package/dist/capability/types.d.ts +2 -0
- package/dist/capability/types.js +2 -0
- package/dist/config/env.js +0 -1
- package/dist/config/schema.d.ts +0 -3
- package/dist/config/schema.js +0 -1
- package/dist/engine/runtime.js +3 -1
- package/dist/engine/types.d.ts +0 -1
- package/dist/engine.js +0 -1
- package/dist/executor/connection-pool.d.ts +0 -2
- package/dist/executor/connection-pool.js +3 -32
- package/dist/executor/sql-executor.js +0 -1
- package/dist/executor/types.d.ts +0 -1
- package/package.json +1 -1
|
@@ -17,18 +17,18 @@ export function parseEnvProfile(env) {
|
|
|
17
17
|
let host;
|
|
18
18
|
let port;
|
|
19
19
|
let database;
|
|
20
|
-
let
|
|
21
|
-
let
|
|
20
|
+
let username;
|
|
21
|
+
let passwordRef;
|
|
22
22
|
if (dsn) {
|
|
23
23
|
const url = new URL(dsn);
|
|
24
24
|
engine = parseEngineFromDsnProtocol(url.protocol);
|
|
25
25
|
host = url.hostname;
|
|
26
26
|
port = url.port ? Number.parseInt(url.port, 10) : defaultPortForEngine(engine);
|
|
27
27
|
database = asString(url.pathname.replace(/^\//, ""));
|
|
28
|
-
|
|
28
|
+
username = asString(decodeURIComponent(url.username));
|
|
29
29
|
const dsnPassword = asString(decodeURIComponent(url.password));
|
|
30
30
|
if (dsnPassword) {
|
|
31
|
-
|
|
31
|
+
passwordRef = parseCredentialRef(dsnPassword, "TAURUSDB_SQL_DSN.password");
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
@@ -38,41 +38,27 @@ export function parseEnvProfile(env) {
|
|
|
38
38
|
port = explicitPort ?? defaultPortForEngine(engine);
|
|
39
39
|
database = asString(env.TAURUSDB_SQL_DATABASE);
|
|
40
40
|
}
|
|
41
|
-
const mutationUserName = asString(env.TAURUSDB_SQL_MUTATION_USER);
|
|
42
|
-
const mutationPasswordRaw = asString(env.TAURUSDB_SQL_MUTATION_PASSWORD);
|
|
43
41
|
if (!dsn &&
|
|
44
42
|
!explicitHost &&
|
|
45
43
|
!asString(env.TAURUSDB_SQL_USER) &&
|
|
46
44
|
!asString(env.TAURUSDB_SQL_PASSWORD) &&
|
|
47
|
-
!database
|
|
48
|
-
!mutationUserName &&
|
|
49
|
-
!mutationPasswordRaw) {
|
|
45
|
+
!database) {
|
|
50
46
|
return undefined;
|
|
51
47
|
}
|
|
52
48
|
if (!port || !Number.isFinite(port) || port <= 0) {
|
|
53
49
|
throw new Error("Failed to resolve SQL port from environment.");
|
|
54
50
|
}
|
|
55
|
-
|
|
56
|
-
if (!
|
|
57
|
-
throw new Error("Missing
|
|
51
|
+
username = username ?? asString(env.TAURUSDB_SQL_USER);
|
|
52
|
+
if (!username) {
|
|
53
|
+
throw new Error("Missing SQL username in environment. Set TAURUSDB_SQL_USER or include it in DSN.");
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
passwordRef =
|
|
56
|
+
passwordRef ??
|
|
61
57
|
(Object.hasOwn(env, "TAURUSDB_SQL_PASSWORD")
|
|
62
58
|
? parseCredentialRef(env.TAURUSDB_SQL_PASSWORD, "TAURUSDB_SQL_PASSWORD")
|
|
63
59
|
: undefined);
|
|
64
|
-
if (!
|
|
65
|
-
throw new Error("Missing
|
|
66
|
-
}
|
|
67
|
-
let mutationUser;
|
|
68
|
-
if (mutationUserName || mutationPasswordRaw) {
|
|
69
|
-
if (!mutationUserName || !mutationPasswordRaw) {
|
|
70
|
-
throw new Error("Invalid mutation credentials in environment: TAURUSDB_SQL_MUTATION_USER and TAURUSDB_SQL_MUTATION_PASSWORD must be set together.");
|
|
71
|
-
}
|
|
72
|
-
mutationUser = {
|
|
73
|
-
username: mutationUserName,
|
|
74
|
-
password: parseCredentialRef(mutationPasswordRaw, "TAURUSDB_SQL_MUTATION_PASSWORD"),
|
|
75
|
-
};
|
|
60
|
+
if (!passwordRef) {
|
|
61
|
+
throw new Error("Missing SQL password in environment. Set TAURUSDB_SQL_PASSWORD or include it in DSN.");
|
|
76
62
|
}
|
|
77
63
|
const poolSize = asInteger(env.TAURUSDB_SQL_POOL_SIZE);
|
|
78
64
|
if (poolSize !== undefined && poolSize <= 0) {
|
|
@@ -84,11 +70,10 @@ export function parseEnvProfile(env) {
|
|
|
84
70
|
host,
|
|
85
71
|
port,
|
|
86
72
|
database,
|
|
87
|
-
|
|
88
|
-
username
|
|
89
|
-
password:
|
|
73
|
+
user: {
|
|
74
|
+
username,
|
|
75
|
+
password: passwordRef,
|
|
90
76
|
},
|
|
91
|
-
mutationUser,
|
|
92
77
|
poolSize,
|
|
93
78
|
});
|
|
94
79
|
}
|
|
@@ -194,13 +194,15 @@ export function parseProfileRecord(name, value, context) {
|
|
|
194
194
|
if (poolSize !== undefined && poolSize <= 0) {
|
|
195
195
|
throw new Error(`Invalid datasource profile ${name} in ${context}: poolSize must be positive.`);
|
|
196
196
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
// Backward compatibility for older profile field names. New configs should use `user`.
|
|
198
|
+
const userRaw = value.user ??
|
|
199
|
+
value.readonlyUser ??
|
|
200
|
+
value.readonly ??
|
|
201
|
+
value.readOnlyUser;
|
|
202
|
+
if (userRaw === undefined) {
|
|
203
|
+
throw new Error(`Invalid datasource profile ${name} in ${context}: missing user.`);
|
|
204
|
+
}
|
|
205
|
+
const user = parseUserCredential(userRaw, `${context}.${name}.user`);
|
|
204
206
|
const tls = parseTlsOptions(value.tls, `${context}.${name}.tls`);
|
|
205
207
|
return withRedactedToString({
|
|
206
208
|
name,
|
|
@@ -208,8 +210,7 @@ export function parseProfileRecord(name, value, context) {
|
|
|
208
210
|
host,
|
|
209
211
|
port,
|
|
210
212
|
database,
|
|
211
|
-
|
|
212
|
-
mutationUser,
|
|
213
|
+
user,
|
|
213
214
|
tls,
|
|
214
215
|
poolSize,
|
|
215
216
|
});
|
|
@@ -5,8 +5,10 @@ export function applyRuntimeTarget(profile, target) {
|
|
|
5
5
|
}
|
|
6
6
|
return withRedactedToString({
|
|
7
7
|
...profile,
|
|
8
|
-
host: target.host,
|
|
8
|
+
host: target.host ?? profile.host,
|
|
9
9
|
port: target.port ?? profile.port,
|
|
10
|
+
database: target.database ?? profile.database,
|
|
11
|
+
user: target.user ?? profile.user,
|
|
10
12
|
});
|
|
11
13
|
}
|
|
12
14
|
export class RuntimeOverrideProfileLoader {
|
|
@@ -16,12 +18,16 @@ export class RuntimeOverrideProfileLoader {
|
|
|
16
18
|
this.base = base;
|
|
17
19
|
}
|
|
18
20
|
setRuntimeTarget(name, target) {
|
|
19
|
-
this.runtimeTargets.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const current = this.runtimeTargets.get(name);
|
|
22
|
+
const next = {
|
|
23
|
+
host: target.host ?? current?.host,
|
|
24
|
+
port: target.port ?? current?.port,
|
|
25
|
+
database: target.database ?? current?.database,
|
|
26
|
+
user: target.user ?? current?.user,
|
|
27
|
+
instanceId: target.instanceId ?? current?.instanceId,
|
|
28
|
+
nodeId: target.nodeId ?? current?.nodeId,
|
|
29
|
+
};
|
|
30
|
+
this.runtimeTargets.set(name, next);
|
|
25
31
|
}
|
|
26
32
|
clearRuntimeTarget(name) {
|
|
27
33
|
this.runtimeTargets.delete(name);
|
|
@@ -31,8 +31,7 @@ export interface DataSourceProfile {
|
|
|
31
31
|
host?: string;
|
|
32
32
|
port: number;
|
|
33
33
|
database?: string;
|
|
34
|
-
|
|
35
|
-
mutationUser?: UserCredential;
|
|
34
|
+
user: UserCredential;
|
|
36
35
|
tls?: TlsOptions;
|
|
37
36
|
poolSize?: number;
|
|
38
37
|
toString(): string;
|
|
@@ -43,8 +42,10 @@ export interface ProfileLoader {
|
|
|
43
42
|
get(name: string): Promise<DataSourceProfile | undefined>;
|
|
44
43
|
}
|
|
45
44
|
export interface RuntimeDataSourceTarget {
|
|
46
|
-
host
|
|
45
|
+
host?: string;
|
|
47
46
|
port?: number;
|
|
47
|
+
database?: string;
|
|
48
|
+
user?: UserCredential;
|
|
48
49
|
instanceId?: string;
|
|
49
50
|
nodeId?: string;
|
|
50
51
|
}
|
|
@@ -41,9 +41,11 @@ export declare class UnsupportedFeatureError extends Error {
|
|
|
41
41
|
readonly feature: TaurusFeatureName;
|
|
42
42
|
readonly requiredVersion?: string;
|
|
43
43
|
readonly currentVersion?: string;
|
|
44
|
+
readonly parameterHint?: string;
|
|
44
45
|
constructor(feature: TaurusFeatureName, message: string, options?: {
|
|
45
46
|
requiredVersion?: string;
|
|
46
47
|
currentVersion?: string;
|
|
48
|
+
parameterHint?: string;
|
|
47
49
|
cause?: unknown;
|
|
48
50
|
});
|
|
49
51
|
}
|
package/dist/capability/types.js
CHANGED
|
@@ -3,12 +3,14 @@ export class UnsupportedFeatureError extends Error {
|
|
|
3
3
|
feature;
|
|
4
4
|
requiredVersion;
|
|
5
5
|
currentVersion;
|
|
6
|
+
parameterHint;
|
|
6
7
|
constructor(feature, message, options = {}) {
|
|
7
8
|
super(message);
|
|
8
9
|
this.name = "UnsupportedFeatureError";
|
|
9
10
|
this.feature = feature;
|
|
10
11
|
this.requiredVersion = options.requiredVersion;
|
|
11
12
|
this.currentVersion = options.currentVersion;
|
|
13
|
+
this.parameterHint = options.parameterHint;
|
|
12
14
|
if (options.cause !== undefined) {
|
|
13
15
|
this.cause = options.cause;
|
|
14
16
|
}
|
package/dist/config/env.js
CHANGED
|
@@ -120,7 +120,6 @@ export function buildRawConfigFromEnv(env) {
|
|
|
120
120
|
return {
|
|
121
121
|
defaultDatasource: readString(env.TAURUSDB_DEFAULT_DATASOURCE) ?? inferredDatasourceName,
|
|
122
122
|
profilesPath: expandTildePath(readString(env.TAURUSDB_SQL_PROFILES)),
|
|
123
|
-
enableMutations: parseBoolean(env.TAURUSDB_MCP_ENABLE_MUTATIONS, "TAURUSDB_MCP_ENABLE_MUTATIONS"),
|
|
124
123
|
cloud: {
|
|
125
124
|
provider: "huaweicloud",
|
|
126
125
|
region: cloudRegion,
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const ConfigSchema: z.ZodObject<{
|
|
3
3
|
defaultDatasource: z.ZodOptional<z.ZodString>;
|
|
4
4
|
profilesPath: z.ZodOptional<z.ZodString>;
|
|
5
|
-
enableMutations: z.ZodDefault<z.ZodBoolean>;
|
|
6
5
|
cloud: z.ZodDefault<z.ZodObject<{
|
|
7
6
|
provider: z.ZodDefault<z.ZodEnum<["huaweicloud"]>>;
|
|
8
7
|
region: z.ZodOptional<z.ZodString>;
|
|
@@ -270,7 +269,6 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
270
269
|
} | undefined;
|
|
271
270
|
}>>;
|
|
272
271
|
}, "strip", z.ZodTypeAny, {
|
|
273
|
-
enableMutations: boolean;
|
|
274
272
|
cloud: {
|
|
275
273
|
provider: "huaweicloud";
|
|
276
274
|
domainSuffix: string;
|
|
@@ -344,7 +342,6 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
344
342
|
}, {
|
|
345
343
|
defaultDatasource?: string | undefined;
|
|
346
344
|
profilesPath?: string | undefined;
|
|
347
|
-
enableMutations?: boolean | undefined;
|
|
348
345
|
cloud?: {
|
|
349
346
|
provider?: "huaweicloud" | undefined;
|
|
350
347
|
region?: string | undefined;
|
package/dist/config/schema.js
CHANGED
|
@@ -82,7 +82,6 @@ const CesMetricsSourceSchema = z
|
|
|
82
82
|
export const ConfigSchema = z.object({
|
|
83
83
|
defaultDatasource: z.string().min(1).optional(),
|
|
84
84
|
profilesPath: z.string().min(1).optional(),
|
|
85
|
-
enableMutations: z.boolean().default(true),
|
|
86
85
|
cloud: CloudSchema,
|
|
87
86
|
limits: LimitsSchema,
|
|
88
87
|
audit: AuditSchema,
|
package/dist/engine/runtime.js
CHANGED
|
@@ -286,6 +286,7 @@ export async function flashbackQuery(engine, input, ctx, opts) {
|
|
|
286
286
|
throw new UnsupportedFeatureError("flashback_query", flashbackFeature.reason ??
|
|
287
287
|
`Flashback query requires kernel version >= ${flashbackFeature.minVersion ?? "unknown"}.`, {
|
|
288
288
|
requiredVersion: flashbackFeature.minVersion,
|
|
289
|
+
parameterHint: flashbackFeature.param,
|
|
289
290
|
currentVersion: (await engine.capabilityProbe.getKernelInfo(ctx))
|
|
290
291
|
.kernelVersion,
|
|
291
292
|
});
|
|
@@ -318,6 +319,7 @@ export async function listRecycleBin(engine, ctx, opts) {
|
|
|
318
319
|
throw new UnsupportedFeatureError("recycle_bin", recycleBinFeature.reason ??
|
|
319
320
|
`Recycle bin requires kernel version >= ${recycleBinFeature.minVersion ?? "unknown"}.`, {
|
|
320
321
|
requiredVersion: recycleBinFeature.minVersion,
|
|
322
|
+
parameterHint: recycleBinFeature.param,
|
|
321
323
|
currentVersion: (await engine.capabilityProbe.getKernelInfo(ctx))
|
|
322
324
|
.kernelVersion,
|
|
323
325
|
});
|
|
@@ -331,13 +333,13 @@ export async function restoreRecycleBinTable(engine, input, ctx, opts) {
|
|
|
331
333
|
throw new UnsupportedFeatureError("recycle_bin", recycleBinFeature.reason ??
|
|
332
334
|
`Recycle bin requires kernel version >= ${recycleBinFeature.minVersion ?? "unknown"}.`, {
|
|
333
335
|
requiredVersion: recycleBinFeature.minVersion,
|
|
336
|
+
parameterHint: recycleBinFeature.param,
|
|
334
337
|
currentVersion: (await engine.capabilityProbe.getKernelInfo(ctx))
|
|
335
338
|
.kernelVersion,
|
|
336
339
|
});
|
|
337
340
|
}
|
|
338
341
|
return engine.executor.executeMutation(buildRestoreRecycleBinTableSql(input), ctx, {
|
|
339
342
|
...recycleBinMutationOptions(opts),
|
|
340
|
-
allowWithoutGlobalMutations: true,
|
|
341
343
|
allowReadonlyFallbackForMutations: true,
|
|
342
344
|
});
|
|
343
345
|
}
|
package/dist/engine/types.d.ts
CHANGED
package/dist/engine.js
CHANGED
|
@@ -22,7 +22,6 @@ function toDataSourceInfo(profile, defaultDatasource) {
|
|
|
22
22
|
host: profile.host,
|
|
23
23
|
port: profile.port,
|
|
24
24
|
database: profile.database,
|
|
25
|
-
hasMutationUser: profile.mutationUser !== undefined,
|
|
26
25
|
poolSize: profile.poolSize,
|
|
27
26
|
isDefault: profile.name === defaultDatasource,
|
|
28
27
|
};
|
|
@@ -35,7 +35,6 @@ export interface PoolHealth {
|
|
|
35
35
|
}
|
|
36
36
|
export interface ConnectionPool {
|
|
37
37
|
acquire(datasource: string, mode: PoolMode, opts?: {
|
|
38
|
-
allowWithoutGlobalMutations?: boolean;
|
|
39
38
|
allowReadonlyFallbackForMutations?: boolean;
|
|
40
39
|
}): Promise<Session>;
|
|
41
40
|
release(session: Session): Promise<void>;
|
|
@@ -92,7 +91,6 @@ export declare class ConnectionPoolManager implements ConnectionPool {
|
|
|
92
91
|
private readonly activeSessions;
|
|
93
92
|
constructor(options: ConnectionPoolManagerOptions);
|
|
94
93
|
acquire(datasource: string, mode: PoolMode, opts?: {
|
|
95
|
-
allowWithoutGlobalMutations?: boolean;
|
|
96
94
|
allowReadonlyFallbackForMutations?: boolean;
|
|
97
95
|
}): Promise<Session>;
|
|
98
96
|
release(session: Session): Promise<void>;
|
|
@@ -32,22 +32,8 @@ async function resolveTls(tls, secretResolver) {
|
|
|
32
32
|
}
|
|
33
33
|
return resolved;
|
|
34
34
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
throw new ConnectionPoolError("Mutation mode is disabled by configuration.");
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function selectCredential(profile, mode, opts = {}) {
|
|
41
|
-
if (mode === "ro") {
|
|
42
|
-
return profile.readonlyUser;
|
|
43
|
-
}
|
|
44
|
-
if (!profile.mutationUser) {
|
|
45
|
-
if (opts.allowReadonlyFallbackForMutations) {
|
|
46
|
-
return profile.readonlyUser;
|
|
47
|
-
}
|
|
48
|
-
throw new ConnectionPoolError(`Mutation user is not configured for datasource "${profile.name}".`);
|
|
49
|
-
}
|
|
50
|
-
return profile.mutationUser;
|
|
35
|
+
function selectCredential(profile, mode, _opts = {}) {
|
|
36
|
+
return profile.user;
|
|
51
37
|
}
|
|
52
38
|
async function resolveCredentialValue(ref, secretResolver, context) {
|
|
53
39
|
try {
|
|
@@ -75,9 +61,6 @@ export class ConnectionPoolManager {
|
|
|
75
61
|
if (!profile) {
|
|
76
62
|
throw new ConnectionPoolError(`Datasource profile not found: "${datasource}".`);
|
|
77
63
|
}
|
|
78
|
-
if (mode === "rw" && !opts.allowWithoutGlobalMutations) {
|
|
79
|
-
ensureMutationAllowed(this.config);
|
|
80
|
-
}
|
|
81
64
|
const entry = await this.getOrCreatePool(profile, mode, opts);
|
|
82
65
|
let driverSession;
|
|
83
66
|
try {
|
|
@@ -122,16 +105,7 @@ export class ConnectionPoolManager {
|
|
|
122
105
|
async healthCheck(datasource) {
|
|
123
106
|
const modes = [];
|
|
124
107
|
modes.push(await this.healthCheckMode(datasource, "ro"));
|
|
125
|
-
|
|
126
|
-
modes.push({
|
|
127
|
-
mode: "rw",
|
|
128
|
-
status: "skipped",
|
|
129
|
-
message: "Mutation mode disabled by config.",
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
modes.push(await this.healthCheckMode(datasource, "rw"));
|
|
134
|
-
}
|
|
108
|
+
modes.push(await this.healthCheckMode(datasource, "rw"));
|
|
135
109
|
return {
|
|
136
110
|
datasource,
|
|
137
111
|
checkedAt: new Date().toISOString(),
|
|
@@ -166,9 +140,6 @@ export class ConnectionPoolManager {
|
|
|
166
140
|
return { mode, status: "ok" };
|
|
167
141
|
}
|
|
168
142
|
catch (error) {
|
|
169
|
-
if (mode === "rw" && error instanceof ConnectionPoolError && /not configured/.test(error.message)) {
|
|
170
|
-
return { mode, status: "skipped", message: error.message };
|
|
171
|
-
}
|
|
172
143
|
return {
|
|
173
144
|
mode,
|
|
174
145
|
status: "error",
|
|
@@ -114,7 +114,6 @@ export class SqlExecutorImpl {
|
|
|
114
114
|
const startedAt = this.now();
|
|
115
115
|
const timeoutMs = opts.timeoutMs ?? ctx.limits.timeoutMs;
|
|
116
116
|
const session = await this.connectionPool.acquire(ctx.datasource, "rw", {
|
|
117
|
-
allowWithoutGlobalMutations: opts.allowWithoutGlobalMutations,
|
|
118
117
|
allowReadonlyFallbackForMutations: opts.allowReadonlyFallbackForMutations,
|
|
119
118
|
});
|
|
120
119
|
const active = this.beginQuery(queryId, session, ctx, "rw", startedAt);
|
package/dist/executor/types.d.ts
CHANGED