nomoreide 0.1.26 → 0.1.27
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/core/config-store.d.ts +3 -1
- package/dist/core/config-store.js +23 -0
- package/dist/core/config-store.js.map +1 -1
- package/dist/core/db/driver.d.ts +43 -0
- package/dist/core/db/driver.js +41 -0
- package/dist/core/db/driver.js.map +1 -0
- package/dist/core/db/mysql-driver.d.ts +19 -0
- package/dist/core/db/mysql-driver.js +95 -0
- package/dist/core/db/mysql-driver.js.map +1 -0
- package/dist/core/db/postgres-driver.d.ts +19 -0
- package/dist/core/db/postgres-driver.js +109 -0
- package/dist/core/db/postgres-driver.js.map +1 -0
- package/dist/core/db/sqlite-driver.d.ts +17 -0
- package/dist/core/db/sqlite-driver.js +68 -0
- package/dist/core/db/sqlite-driver.js.map +1 -0
- package/dist/core/db-peek.d.ts +49 -0
- package/dist/core/db-peek.js +150 -0
- package/dist/core/db-peek.js.map +1 -0
- package/dist/core/types.d.ts +12 -0
- package/dist/mcp/server.js +3 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/context.d.ts +2 -0
- package/dist/mcp/tools/context.js.map +1 -1
- package/dist/mcp/tools/database.d.ts +8 -0
- package/dist/mcp/tools/database.js +52 -0
- package/dist/mcp/tools/database.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +1 -1
- package/dist/mcp/tools/index.js +3 -0
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/web/client/assets/index-BSh_mD42.css +1 -0
- package/dist/web/client/assets/index-CD6SvvAp.js +26 -0
- package/dist/web/client/index.html +2 -2
- package/dist/web/routes/context.d.ts +2 -0
- package/dist/web/routes/context.js.map +1 -1
- package/dist/web/routes/database-routes.d.ts +3 -0
- package/dist/web/routes/database-routes.js +78 -0
- package/dist/web/routes/database-routes.js.map +1 -0
- package/dist/web/routes/index.js +2 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/shell-routes.js +1 -1
- package/dist/web/routes/shell-routes.js.map +1 -1
- package/dist/web/server.js +3 -0
- package/dist/web/server.js.map +1 -1
- package/package.json +8 -2
- package/dist/web/client/assets/index-CgtUFH5U.css +0 -1
- package/dist/web/client/assets/index-DQdQ7efQ.js +0 -24
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BundleDefinition, NoMoreIdeConfig, GitRepositoryDefinition, ServiceDefinition } from "./types.js";
|
|
1
|
+
import type { BundleDefinition, DatabaseConnection, NoMoreIdeConfig, GitRepositoryDefinition, ServiceDefinition } from "./types.js";
|
|
2
2
|
export declare function defaultGlobalConfigPath(): string;
|
|
3
3
|
export declare class ConfigStore {
|
|
4
4
|
private readonly configPath;
|
|
@@ -9,6 +9,8 @@ export declare class ConfigStore {
|
|
|
9
9
|
registerBundle(bundle: BundleDefinition, previousName?: string): Promise<NoMoreIdeConfig>;
|
|
10
10
|
registerGitRepository(repository: GitRepositoryDefinition): Promise<NoMoreIdeConfig>;
|
|
11
11
|
selectGitRepository(name: string): Promise<NoMoreIdeConfig>;
|
|
12
|
+
registerDatabase(database: DatabaseConnection): Promise<NoMoreIdeConfig>;
|
|
13
|
+
removeDatabase(name: string): Promise<NoMoreIdeConfig>;
|
|
12
14
|
}
|
|
13
15
|
export declare class ConfigValidationError extends Error {
|
|
14
16
|
}
|
|
@@ -46,18 +46,25 @@ const gitRepositorySchema = z.object({
|
|
|
46
46
|
name: z.string().min(1),
|
|
47
47
|
path: z.string().min(1),
|
|
48
48
|
});
|
|
49
|
+
const databaseSchema = z.object({
|
|
50
|
+
name: z.string().min(1),
|
|
51
|
+
engine: z.enum(["postgres", "mysql", "sqlite"]),
|
|
52
|
+
url: z.string().min(1),
|
|
53
|
+
});
|
|
49
54
|
const configSchema = z.object({
|
|
50
55
|
version: z.literal(1),
|
|
51
56
|
services: z.array(serviceSchema),
|
|
52
57
|
bundles: z.array(bundleSchema),
|
|
53
58
|
gitRepositories: z.array(gitRepositorySchema).default([]),
|
|
54
59
|
selectedGitRepository: z.string().min(1).optional(),
|
|
60
|
+
databases: z.array(databaseSchema).default([]),
|
|
55
61
|
});
|
|
56
62
|
const defaultConfig = {
|
|
57
63
|
version: 1,
|
|
58
64
|
services: [],
|
|
59
65
|
bundles: [],
|
|
60
66
|
gitRepositories: [],
|
|
67
|
+
databases: [],
|
|
61
68
|
};
|
|
62
69
|
export function defaultGlobalConfigPath() {
|
|
63
70
|
const base = process.env.XDG_CONFIG_HOME?.trim();
|
|
@@ -127,6 +134,22 @@ export class ConfigStore {
|
|
|
127
134
|
await this.save(config);
|
|
128
135
|
return config;
|
|
129
136
|
}
|
|
137
|
+
async registerDatabase(database) {
|
|
138
|
+
const parsed = databaseSchema.parse(database);
|
|
139
|
+
const config = await this.load();
|
|
140
|
+
config.databases = [
|
|
141
|
+
...config.databases.filter((item) => item.name !== parsed.name),
|
|
142
|
+
parsed,
|
|
143
|
+
];
|
|
144
|
+
await this.save(config);
|
|
145
|
+
return config;
|
|
146
|
+
}
|
|
147
|
+
async removeDatabase(name) {
|
|
148
|
+
const config = await this.load();
|
|
149
|
+
config.databases = config.databases.filter((item) => item.name !== name);
|
|
150
|
+
await this.save(config);
|
|
151
|
+
return config;
|
|
152
|
+
}
|
|
130
153
|
}
|
|
131
154
|
export class ConfigValidationError extends Error {
|
|
132
155
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/core/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/core/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;IACvD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAClC,2EAA2E;IAC3E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACnC,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAClD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE;IACnC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtB,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,MAAM,CAAC;IACnD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC;IACjC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACzC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtB,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACxC,OAAO,EAAE,yCAAyC;KACnD,CAAC;IACJ,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC;IAC5B,kBAAkB;IAClB,mBAAmB;IACnB,gBAAgB;CACjB,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC/C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC;IAChC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;IAC9B,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACzD,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnD,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC/C,CAAC,CAAC;AAEH,MAAM,aAAa,GAAoB;IACrC,OAAO,EAAE,CAAC;IACV,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,EAAE;IACX,eAAe,EAAE,EAAE;IACnB,SAAS,EAAE,EAAE;CACd,CAAC;AAEF,MAAM,UAAU,uBAAuB;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACjD,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,aAAa,uBAAuB,EAAE;QAAtC,eAAU,GAAV,UAAU,CAA4B;IAAG,CAAC;IAEvE,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACpD,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,eAAe,CAAC,aAAa,CAAC,CAAC;YACxC,CAAC;YAED,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAuB;QAChC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAA0B;QAC9C,MAAM,aAAa,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEjC,MAAM,CAAC,QAAQ,GAAG;YAChB,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,aAAa,CAAC,IAAI,CAAC;YACrE,aAAa;SACd,CAAC;QAEF,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,MAAwB,EACxB,YAAqB;QAErB,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEjC,MAAM,CAAC,OAAO,GAAG;YACf,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CACtB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,CACxE;YACD,YAAY;SACb,CAAC;QAEF,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,qBAAqB,CACzB,UAAmC;QAEnC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/D,mBAAmB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEjC,MAAM,CAAC,eAAe,GAAG;YACvB,GAAG,MAAM,CAAC,eAAe,CAAC,MAAM,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,gBAAgB,CAAC,IAAI,CAC9C;YACD,gBAAgB;SACjB,CAAC;QACF,MAAM,CAAC,qBAAqB,GAAG,gBAAgB,CAAC,IAAI,CAAC;QAErD,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,IAAY;QACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEjC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YAC3E,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,sBAAsB,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,CAAC,qBAAqB,GAAG,IAAI,CAAC;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,QAA4B;QAE5B,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEjC,MAAM,CAAC,SAAS,GAAG;YACjB,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC;YAC/D,MAAM;SACP,CAAC;QAEF,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,IAAY;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACzE,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;CAAG;AAEnD,SAAS,mBAAmB,CAAC,IAAY;IACvC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,qBAAqB,CAC7B,4EAA4E,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,CACL,KAAK,YAAY,KAAK;QACtB,MAAM,IAAI,KAAK;QACd,KAA+B,CAAC,IAAI,KAAK,QAAQ,CACnD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { DatabaseEngine } from "../types.js";
|
|
2
|
+
/** A table the user can browse. `schema` is undefined for SQLite. */
|
|
3
|
+
export interface TableRef {
|
|
4
|
+
schema?: string;
|
|
5
|
+
name: string;
|
|
6
|
+
/** Stable key used by the API/UI, e.g. "public.users" or "users". */
|
|
7
|
+
qualifiedName: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ColumnInfo {
|
|
10
|
+
name: string;
|
|
11
|
+
dataType: string;
|
|
12
|
+
nullable: boolean;
|
|
13
|
+
primaryKey: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface RowSample {
|
|
16
|
+
columns: ColumnInfo[];
|
|
17
|
+
rows: Array<Record<string, unknown>>;
|
|
18
|
+
/** Number of rows returned (≤ requested limit). */
|
|
19
|
+
rowCount: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read-only access to a single database. Implementations enforce read-only at
|
|
23
|
+
* the connection/transaction level — there is no raw-SQL passthrough.
|
|
24
|
+
*/
|
|
25
|
+
export interface DbDriver {
|
|
26
|
+
readonly engine: DatabaseEngine;
|
|
27
|
+
/** Throws if the connection cannot be established. */
|
|
28
|
+
testConnection(): Promise<void>;
|
|
29
|
+
listTables(): Promise<TableRef[]>;
|
|
30
|
+
sampleRows(table: TableRef, limit: number): Promise<RowSample>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
/** Defence in depth: a catalog-sourced identifier must still be well-formed. */
|
|
34
|
+
export declare function assertSafeIdentifier(value: string): string;
|
|
35
|
+
/** Clamp a caller-supplied row limit into a sane range. */
|
|
36
|
+
export declare function clampLimit(limit: number | undefined, fallback?: number): number;
|
|
37
|
+
/**
|
|
38
|
+
* Drivers select `*` and return whatever the row shapes are. JSON.stringify
|
|
39
|
+
* can't serialize Buffers/BigInts/Dates cleanly, so normalize to display-safe
|
|
40
|
+
* primitives before they leave the core layer.
|
|
41
|
+
*/
|
|
42
|
+
export declare function normalizeCell(value: unknown): unknown;
|
|
43
|
+
export declare function normalizeRow(row: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Identifiers we accept after resolving against the live catalog. */
|
|
2
|
+
const SAFE_IDENTIFIER = /^[A-Za-z0-9_$]+$/;
|
|
3
|
+
/** Defence in depth: a catalog-sourced identifier must still be well-formed. */
|
|
4
|
+
export function assertSafeIdentifier(value) {
|
|
5
|
+
if (!SAFE_IDENTIFIER.test(value)) {
|
|
6
|
+
throw new Error(`Unsafe identifier: ${value}`);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
/** Clamp a caller-supplied row limit into a sane range. */
|
|
11
|
+
export function clampLimit(limit, fallback = 100) {
|
|
12
|
+
if (!Number.isFinite(limit) || limit === undefined)
|
|
13
|
+
return fallback;
|
|
14
|
+
return Math.min(Math.max(Math.trunc(limit), 1), 1000);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Drivers select `*` and return whatever the row shapes are. JSON.stringify
|
|
18
|
+
* can't serialize Buffers/BigInts/Dates cleanly, so normalize to display-safe
|
|
19
|
+
* primitives before they leave the core layer.
|
|
20
|
+
*/
|
|
21
|
+
export function normalizeCell(value) {
|
|
22
|
+
if (value === null || value === undefined)
|
|
23
|
+
return null;
|
|
24
|
+
if (typeof value === "bigint")
|
|
25
|
+
return value.toString();
|
|
26
|
+
if (value instanceof Date)
|
|
27
|
+
return value.toISOString();
|
|
28
|
+
if (Buffer.isBuffer(value))
|
|
29
|
+
return `\\x${value.toString("hex")}`;
|
|
30
|
+
if (typeof value === "object")
|
|
31
|
+
return value; // arrays / json columns
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
export function normalizeRow(row) {
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const [key, value] of Object.entries(row)) {
|
|
37
|
+
out[key] = normalizeCell(value);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/core/db/driver.ts"],"names":[],"mappings":"AAqCA,sEAAsE;AACtE,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAE3C,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CAAC,KAAyB,EAAE,QAAQ,GAAG,GAAG;IAClE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACpE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IACvD,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;IACtD,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,CAAC,wBAAwB;IACrE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,GAA4B;IAE5B,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DatabaseEngine } from "../types.js";
|
|
2
|
+
import { type DbDriver, type RowSample, type TableRef } from "./driver.js";
|
|
3
|
+
/**
|
|
4
|
+
* MySQL / MariaDB driver. Each operation runs in a `START TRANSACTION READ
|
|
5
|
+
* ONLY` block on a pooled connection, then rolls back.
|
|
6
|
+
*/
|
|
7
|
+
export declare class MysqlDriver implements DbDriver {
|
|
8
|
+
private readonly url;
|
|
9
|
+
readonly engine: DatabaseEngine;
|
|
10
|
+
private poolPromise;
|
|
11
|
+
constructor(url: string);
|
|
12
|
+
private pool;
|
|
13
|
+
private withReadOnly;
|
|
14
|
+
testConnection(): Promise<void>;
|
|
15
|
+
listTables(): Promise<TableRef[]>;
|
|
16
|
+
sampleRows(table: TableRef, limit: number): Promise<RowSample>;
|
|
17
|
+
private columnsFor;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { assertSafeIdentifier, clampLimit, normalizeRow, } from "./driver.js";
|
|
2
|
+
/**
|
|
3
|
+
* MySQL / MariaDB driver. Each operation runs in a `START TRANSACTION READ
|
|
4
|
+
* ONLY` block on a pooled connection, then rolls back.
|
|
5
|
+
*/
|
|
6
|
+
export class MysqlDriver {
|
|
7
|
+
url;
|
|
8
|
+
engine = "mysql";
|
|
9
|
+
poolPromise = null;
|
|
10
|
+
constructor(url) {
|
|
11
|
+
this.url = url;
|
|
12
|
+
}
|
|
13
|
+
async pool() {
|
|
14
|
+
if (!this.poolPromise) {
|
|
15
|
+
this.poolPromise = (async () => {
|
|
16
|
+
const mysql = await import("mysql2/promise");
|
|
17
|
+
return mysql.createPool({
|
|
18
|
+
uri: this.url,
|
|
19
|
+
connectionLimit: 4,
|
|
20
|
+
connectTimeout: 8000,
|
|
21
|
+
// Surface BIGINT/DECIMAL as strings so values survive JSON intact.
|
|
22
|
+
decimalNumbers: false,
|
|
23
|
+
supportBigNumbers: true,
|
|
24
|
+
bigNumberStrings: true,
|
|
25
|
+
});
|
|
26
|
+
})();
|
|
27
|
+
}
|
|
28
|
+
return this.poolPromise;
|
|
29
|
+
}
|
|
30
|
+
async withReadOnly(fn) {
|
|
31
|
+
const pool = await this.pool();
|
|
32
|
+
const conn = await pool.getConnection();
|
|
33
|
+
try {
|
|
34
|
+
await conn.query("START TRANSACTION READ ONLY");
|
|
35
|
+
const result = await fn(conn);
|
|
36
|
+
await conn.query("COMMIT");
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
await conn.query("ROLLBACK").catch(() => { });
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
conn.release();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async testConnection() {
|
|
48
|
+
await this.withReadOnly((conn) => conn.query("SELECT 1"));
|
|
49
|
+
}
|
|
50
|
+
async listTables() {
|
|
51
|
+
return this.withReadOnly(async (conn) => {
|
|
52
|
+
const [rows] = await conn.query(`SELECT table_name
|
|
53
|
+
FROM information_schema.tables
|
|
54
|
+
WHERE table_schema = DATABASE()
|
|
55
|
+
ORDER BY table_name`);
|
|
56
|
+
return rows.map((row) => {
|
|
57
|
+
const name = String(row.table_name ?? row.TABLE_NAME);
|
|
58
|
+
return { name, qualifiedName: name };
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async sampleRows(table, limit) {
|
|
63
|
+
const name = assertSafeIdentifier(table.name);
|
|
64
|
+
const max = clampLimit(limit);
|
|
65
|
+
return this.withReadOnly(async (conn) => {
|
|
66
|
+
const columns = await this.columnsFor(conn, name);
|
|
67
|
+
const [rows] = await conn.query(`SELECT * FROM \`${name}\` LIMIT ?`, [max]);
|
|
68
|
+
return {
|
|
69
|
+
columns,
|
|
70
|
+
rows: rows.map((row) => normalizeRow(row)),
|
|
71
|
+
rowCount: rows.length,
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async columnsFor(conn, name) {
|
|
76
|
+
const [rows] = await conn.query(`SELECT column_name, data_type, is_nullable, column_key
|
|
77
|
+
FROM information_schema.columns
|
|
78
|
+
WHERE table_schema = DATABASE() AND table_name = ?
|
|
79
|
+
ORDER BY ordinal_position`, [name]);
|
|
80
|
+
return rows.map((row) => ({
|
|
81
|
+
name: String(row.column_name ?? row.COLUMN_NAME),
|
|
82
|
+
dataType: String(row.data_type ?? row.DATA_TYPE),
|
|
83
|
+
nullable: String(row.is_nullable ?? row.IS_NULLABLE) === "YES",
|
|
84
|
+
primaryKey: String(row.column_key ?? row.COLUMN_KEY) === "PRI",
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
async close() {
|
|
88
|
+
if (this.poolPromise) {
|
|
89
|
+
const pool = await this.poolPromise;
|
|
90
|
+
await pool.end();
|
|
91
|
+
this.poolPromise = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=mysql-driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mysql-driver.js","sourceRoot":"","sources":["../../../src/core/db/mysql-driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DatabaseEngine } from "../types.js";
|
|
2
|
+
import { type DbDriver, type RowSample, type TableRef } from "./driver.js";
|
|
3
|
+
/**
|
|
4
|
+
* Postgres driver. Every statement runs inside a `READ ONLY` transaction, so
|
|
5
|
+
* even a mistaken write would be rejected by the server.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PostgresDriver implements DbDriver {
|
|
8
|
+
private readonly url;
|
|
9
|
+
readonly engine: DatabaseEngine;
|
|
10
|
+
private poolPromise;
|
|
11
|
+
constructor(url: string);
|
|
12
|
+
private pool;
|
|
13
|
+
private withReadOnly;
|
|
14
|
+
testConnection(): Promise<void>;
|
|
15
|
+
listTables(): Promise<TableRef[]>;
|
|
16
|
+
sampleRows(table: TableRef, limit: number): Promise<RowSample>;
|
|
17
|
+
private columnsFor;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { assertSafeIdentifier, clampLimit, normalizeRow, } from "./driver.js";
|
|
2
|
+
/**
|
|
3
|
+
* Postgres driver. Every statement runs inside a `READ ONLY` transaction, so
|
|
4
|
+
* even a mistaken write would be rejected by the server.
|
|
5
|
+
*/
|
|
6
|
+
export class PostgresDriver {
|
|
7
|
+
url;
|
|
8
|
+
engine = "postgres";
|
|
9
|
+
poolPromise = null;
|
|
10
|
+
constructor(url) {
|
|
11
|
+
this.url = url;
|
|
12
|
+
}
|
|
13
|
+
async pool() {
|
|
14
|
+
if (!this.poolPromise) {
|
|
15
|
+
this.poolPromise = (async () => {
|
|
16
|
+
const { default: pg } = await import("pg");
|
|
17
|
+
return new pg.Pool({
|
|
18
|
+
connectionString: this.url,
|
|
19
|
+
max: 4,
|
|
20
|
+
connectionTimeoutMillis: 8000,
|
|
21
|
+
// Belt-and-suspenders: every session defaults to read-only.
|
|
22
|
+
options: "-c default_transaction_read_only=on",
|
|
23
|
+
});
|
|
24
|
+
})();
|
|
25
|
+
}
|
|
26
|
+
return this.poolPromise;
|
|
27
|
+
}
|
|
28
|
+
async withReadOnly(fn) {
|
|
29
|
+
const pool = await this.pool();
|
|
30
|
+
const client = await pool.connect();
|
|
31
|
+
try {
|
|
32
|
+
await client.query("BEGIN TRANSACTION READ ONLY");
|
|
33
|
+
const result = await fn(client);
|
|
34
|
+
await client.query("COMMIT");
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
await client.query("ROLLBACK").catch(() => { });
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
client.release();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async testConnection() {
|
|
46
|
+
await this.withReadOnly((client) => client.query("SELECT 1"));
|
|
47
|
+
}
|
|
48
|
+
async listTables() {
|
|
49
|
+
return this.withReadOnly(async (client) => {
|
|
50
|
+
const { rows } = await client.query(`SELECT table_schema, table_name
|
|
51
|
+
FROM information_schema.tables
|
|
52
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
53
|
+
AND table_type IN ('BASE TABLE', 'VIEW')
|
|
54
|
+
ORDER BY table_schema, table_name`);
|
|
55
|
+
return rows.map((row) => ({
|
|
56
|
+
schema: row.table_schema,
|
|
57
|
+
name: row.table_name,
|
|
58
|
+
qualifiedName: `${row.table_schema}.${row.table_name}`,
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async sampleRows(table, limit) {
|
|
63
|
+
const schema = assertSafeIdentifier(table.schema ?? "public");
|
|
64
|
+
const name = assertSafeIdentifier(table.name);
|
|
65
|
+
const max = clampLimit(limit);
|
|
66
|
+
return this.withReadOnly(async (client) => {
|
|
67
|
+
const columns = await this.columnsFor(client, schema, name);
|
|
68
|
+
const { rows } = await client.query(`SELECT * FROM "${schema}"."${name}" LIMIT $1`, [max]);
|
|
69
|
+
return {
|
|
70
|
+
columns,
|
|
71
|
+
rows: rows.map((row) => normalizeRow(row)),
|
|
72
|
+
rowCount: rows.length,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async columnsFor(client, schema, name) {
|
|
77
|
+
const { rows } = await client.query(`SELECT c.column_name,
|
|
78
|
+
c.data_type,
|
|
79
|
+
c.is_nullable,
|
|
80
|
+
COALESCE(pk.is_primary, false) AS is_primary
|
|
81
|
+
FROM information_schema.columns c
|
|
82
|
+
LEFT JOIN (
|
|
83
|
+
SELECT kcu.column_name, true AS is_primary
|
|
84
|
+
FROM information_schema.table_constraints tc
|
|
85
|
+
JOIN information_schema.key_column_usage kcu
|
|
86
|
+
ON kcu.constraint_name = tc.constraint_name
|
|
87
|
+
AND kcu.table_schema = tc.table_schema
|
|
88
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
89
|
+
AND tc.table_schema = $1
|
|
90
|
+
AND tc.table_name = $2
|
|
91
|
+
) pk ON pk.column_name = c.column_name
|
|
92
|
+
WHERE c.table_schema = $1 AND c.table_name = $2
|
|
93
|
+
ORDER BY c.ordinal_position`, [schema, name]);
|
|
94
|
+
return rows.map((row) => ({
|
|
95
|
+
name: row.column_name,
|
|
96
|
+
dataType: row.data_type,
|
|
97
|
+
nullable: row.is_nullable === "YES",
|
|
98
|
+
primaryKey: row.is_primary,
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
async close() {
|
|
102
|
+
if (this.poolPromise) {
|
|
103
|
+
const pool = await this.poolPromise;
|
|
104
|
+
await pool.end();
|
|
105
|
+
this.poolPromise = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=postgres-driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-driver.js","sourceRoot":"","sources":["../../../src/core/db/postgres-driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DatabaseEngine } from "../types.js";
|
|
2
|
+
import { type DbDriver, type RowSample, type TableRef } from "./driver.js";
|
|
3
|
+
/**
|
|
4
|
+
* SQLite driver backed by Node's built-in `node:sqlite`. The file is opened
|
|
5
|
+
* read-only, so the connection itself cannot mutate the database.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SqliteDriver implements DbDriver {
|
|
8
|
+
private readonly file;
|
|
9
|
+
readonly engine: DatabaseEngine;
|
|
10
|
+
private dbPromise;
|
|
11
|
+
constructor(file: string);
|
|
12
|
+
private db;
|
|
13
|
+
testConnection(): Promise<void>;
|
|
14
|
+
listTables(): Promise<TableRef[]>;
|
|
15
|
+
sampleRows(table: TableRef, limit: number): Promise<RowSample>;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { assertSafeIdentifier, clampLimit, normalizeRow, } from "./driver.js";
|
|
2
|
+
/**
|
|
3
|
+
* SQLite driver backed by Node's built-in `node:sqlite`. The file is opened
|
|
4
|
+
* read-only, so the connection itself cannot mutate the database.
|
|
5
|
+
*/
|
|
6
|
+
export class SqliteDriver {
|
|
7
|
+
file;
|
|
8
|
+
engine = "sqlite";
|
|
9
|
+
dbPromise = null;
|
|
10
|
+
constructor(file) {
|
|
11
|
+
this.file = file;
|
|
12
|
+
}
|
|
13
|
+
async db() {
|
|
14
|
+
if (!this.dbPromise) {
|
|
15
|
+
this.dbPromise = (async () => {
|
|
16
|
+
let DatabaseSync;
|
|
17
|
+
try {
|
|
18
|
+
({ DatabaseSync } = await import("node:sqlite"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new Error(`SQLite browsing requires Node >=22.5 (uses the built-in node:sqlite module); you're on ${process.version}. Upgrade Node, or use a Postgres/MySQL connection instead.`);
|
|
22
|
+
}
|
|
23
|
+
return new DatabaseSync(this.file, { readOnly: true });
|
|
24
|
+
})();
|
|
25
|
+
}
|
|
26
|
+
return this.dbPromise;
|
|
27
|
+
}
|
|
28
|
+
async testConnection() {
|
|
29
|
+
const db = await this.db();
|
|
30
|
+
db.prepare("SELECT 1").get();
|
|
31
|
+
}
|
|
32
|
+
async listTables() {
|
|
33
|
+
const db = await this.db();
|
|
34
|
+
const rows = db
|
|
35
|
+
.prepare(`SELECT name FROM sqlite_master
|
|
36
|
+
WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
|
|
37
|
+
ORDER BY name`)
|
|
38
|
+
.all();
|
|
39
|
+
return rows.map((row) => ({ name: row.name, qualifiedName: row.name }));
|
|
40
|
+
}
|
|
41
|
+
async sampleRows(table, limit) {
|
|
42
|
+
const name = assertSafeIdentifier(table.name);
|
|
43
|
+
const max = clampLimit(limit);
|
|
44
|
+
const db = await this.db();
|
|
45
|
+
const columns = db.prepare(`PRAGMA table_info("${name}")`).all().map((col) => ({
|
|
46
|
+
name: col.name,
|
|
47
|
+
dataType: col.type || "",
|
|
48
|
+
nullable: col.notnull === 0,
|
|
49
|
+
primaryKey: col.pk > 0,
|
|
50
|
+
}));
|
|
51
|
+
const rows = db
|
|
52
|
+
.prepare(`SELECT * FROM "${name}" LIMIT ?`)
|
|
53
|
+
.all(max);
|
|
54
|
+
return {
|
|
55
|
+
columns,
|
|
56
|
+
rows: rows.map((row) => normalizeRow(row)),
|
|
57
|
+
rowCount: rows.length,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async close() {
|
|
61
|
+
if (this.dbPromise) {
|
|
62
|
+
const db = await this.dbPromise;
|
|
63
|
+
db.close();
|
|
64
|
+
this.dbPromise = null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=sqlite-driver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-driver.js","sourceRoot":"","sources":["../../../src/core/db/sqlite-driver.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,YAAY,GAKb,MAAM,aAAa,CAAC;AASrB;;;GAGG;AACH,MAAM,OAAO,YAAY;IAIM;IAHpB,MAAM,GAAmB,QAAQ,CAAC;IACnC,SAAS,GAAiC,IAAI,CAAC;IAEvD,YAA6B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAErC,KAAK,CAAC,EAAE;QACd,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,SAAS,GAAG,CAAC,KAAK,IAAI,EAAE;gBAC3B,IAAI,YAAuD,CAAC;gBAC5D,IAAI,CAAC;oBACH,CAAC,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;gBACnD,CAAC;gBAAC,MAAM,CAAC;oBACP,MAAM,IAAI,KAAK,CACb,0FAA0F,OAAO,CAAC,OAAO,6DAA6D,CACvK,CAAC;gBACJ,CAAC;gBACD,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC,CAAC,EAAE,CAAC;QACP,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,CAAC;QAC3B,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,EAAE;aACZ,OAAO,CACN;;wBAEgB,CACjB;aACA,GAAG,EAA6B,CAAC;QACpC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAe,EAAE,KAAa;QAC7C,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,OAAO,GACX,EAAE,CAAC,OAAO,CAAC,sBAAsB,IAAI,IAAI,CAAC,CAAC,GAAG,EAC/C,CAAC,GAAG,CAAa,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE;YACxB,QAAQ,EAAE,GAAG,CAAC,OAAO,KAAK,CAAC;YAC3B,UAAU,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC;SACvB,CAAC,CAAC,CAAC;QACJ,MAAM,IAAI,GAAG,EAAE;aACZ,OAAO,CAAC,kBAAkB,IAAI,WAAW,CAAC;aAC1C,GAAG,CAAC,GAAG,CAAmC,CAAC;QAC9C,OAAO;YACL,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;YAC1C,QAAQ,EAAE,IAAI,CAAC,MAAM;SACtB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC;YAChC,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ConfigStore } from "./config-store.js";
|
|
2
|
+
import type { DatabaseEngine } from "./types.js";
|
|
3
|
+
import type { RowSample, TableRef } from "./db/driver.js";
|
|
4
|
+
export interface MaskedConnection {
|
|
5
|
+
name: string;
|
|
6
|
+
engine: DatabaseEngine;
|
|
7
|
+
/** Connection URL with any password redacted (path left intact for SQLite). */
|
|
8
|
+
url: string;
|
|
9
|
+
}
|
|
10
|
+
/** A DB connection string discovered in a service's `.env` file. */
|
|
11
|
+
export interface DetectedConnection {
|
|
12
|
+
service: string;
|
|
13
|
+
key: string;
|
|
14
|
+
engine: DatabaseEngine;
|
|
15
|
+
url: string;
|
|
16
|
+
maskedUrl: string;
|
|
17
|
+
}
|
|
18
|
+
export interface DbPeekOptions {
|
|
19
|
+
configStore: ConfigStore;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* DB Peek: a read-only table browser. Owns its own bundled drivers (Postgres,
|
|
23
|
+
* MySQL, SQLite) — it never borrows the agent's MCP database tooling. Drivers
|
|
24
|
+
* are cached per connection URL and reused across requests.
|
|
25
|
+
*/
|
|
26
|
+
export declare class DbPeek {
|
|
27
|
+
private readonly configStore;
|
|
28
|
+
private readonly drivers;
|
|
29
|
+
constructor(options: DbPeekOptions);
|
|
30
|
+
listConnections(): Promise<MaskedConnection[]>;
|
|
31
|
+
listTables(name: string): Promise<TableRef[]>;
|
|
32
|
+
sampleRows(name: string, qualifiedName: string, limit: number): Promise<{
|
|
33
|
+
engine: DatabaseEngine;
|
|
34
|
+
table: TableRef;
|
|
35
|
+
} & RowSample>;
|
|
36
|
+
/** Test an unsaved connection without caching it. */
|
|
37
|
+
test(engine: DatabaseEngine, url: string): Promise<void>;
|
|
38
|
+
/** Scan registered services' `.env` files for usable connection strings. */
|
|
39
|
+
detectFromEnv(): Promise<DetectedConnection[]>;
|
|
40
|
+
closeAll(): Promise<void>;
|
|
41
|
+
private resolve;
|
|
42
|
+
private driverFor;
|
|
43
|
+
/** Only browse tables that actually exist — blocks identifier injection. */
|
|
44
|
+
private resolveTable;
|
|
45
|
+
}
|
|
46
|
+
/** Guess an engine from a connection string or file path; null if unrecognized. */
|
|
47
|
+
export declare function engineFromUrl(value: string): DatabaseEngine | null;
|
|
48
|
+
/** Redact the password from a URL; SQLite paths are returned unchanged. */
|
|
49
|
+
export declare function maskConnectionUrl(engine: DatabaseEngine, url: string): string;
|