@askdb/introspect 0.3.0-beta.2 → 0.3.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/kit/driver.d.ts +76 -0
- package/dist/kit/driver.d.ts.map +1 -0
- package/dist/kit/driver.js +156 -0
- package/dist/kit/driver.js.map +1 -0
- package/dist/kit/filters.d.ts +29 -0
- package/dist/kit/filters.d.ts.map +1 -0
- package/dist/kit/filters.js +58 -0
- package/dist/kit/filters.js.map +1 -0
- package/dist/kit/ids.d.ts +16 -0
- package/dist/kit/ids.d.ts.map +1 -0
- package/dist/kit/ids.js +20 -0
- package/dist/kit/ids.js.map +1 -0
- package/dist/kit/index.d.ts +18 -0
- package/dist/kit/index.d.ts.map +1 -0
- package/dist/kit/index.js +18 -0
- package/dist/kit/index.js.map +1 -0
- package/dist/kit/label.d.ts +44 -0
- package/dist/kit/label.d.ts.map +1 -0
- package/dist/kit/label.js +110 -0
- package/dist/kit/label.js.map +1 -0
- package/dist/kit/provider.d.ts +57 -0
- package/dist/kit/provider.d.ts.map +1 -0
- package/dist/kit/provider.js +50 -0
- package/dist/kit/provider.js.map +1 -0
- package/dist/kit/rows.d.ts +42 -0
- package/dist/kit/rows.d.ts.map +1 -0
- package/dist/kit/rows.js +86 -0
- package/dist/kit/rows.js.map +1 -0
- package/dist/registry.d.ts +188 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +156 -0
- package/dist/registry.js.map +1 -0
- package/dist/render/render.d.ts +27 -3
- package/dist/render/render.d.ts.map +1 -1
- package/dist/render/render.js +67 -11
- package/dist/render/render.js.map +1 -1
- package/dist/render/types.d.ts +10 -0
- package/dist/render/types.d.ts.map +1 -1
- package/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +14 -7
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection labels: the credential-free text shown for a configured database
|
|
3
|
+
* connection (Studio's `GET /api/introspect/status` `sourceLabel`).
|
|
4
|
+
*
|
|
5
|
+
* A label is built only from parts an engine's parser extracted cleanly — host,
|
|
6
|
+
* port, database, or a SQLite file path — and only when each part passes the
|
|
7
|
+
* allowlist below. No other substring of the input is ever copied. Input that
|
|
8
|
+
* does not parse cleanly, or a part that fails its allowlist, becomes
|
|
9
|
+
* `configured <engine> connection`. Nothing here masks: see
|
|
10
|
+
* docs/adrs/0011-connection-labels-from-parsed-parts.md for why.
|
|
11
|
+
*/
|
|
12
|
+
const HOST = /^(?:[A-Za-z0-9_](?:[A-Za-z0-9_.-]*[A-Za-z0-9_])?|\[[0-9A-Fa-f:.]+\])$/;
|
|
13
|
+
const PORT = /^[0-9]{1,5}$/;
|
|
14
|
+
const DATABASE = /^[A-Za-z0-9_$.-]+$/;
|
|
15
|
+
// A plain filesystem path: no control characters, and none of the characters
|
|
16
|
+
// that would make it a URI query/fragment, a `key=value` list, or userinfo.
|
|
17
|
+
const FILE = /^[^\u0000-\u001f\u007f?#;=@]+$/;
|
|
18
|
+
/**
|
|
19
|
+
* Build a connection label from parsed parts: `<engine>://host[:port][/database]`
|
|
20
|
+
* for network engines, the path itself for a file. Returns
|
|
21
|
+
* `configured <engine> connection` when `parts` is `undefined` (the input did
|
|
22
|
+
* not parse) or any part fails its allowlist. The shape is checked at runtime
|
|
23
|
+
* too, because a plain-JS adapter can return anything: a value that isn't a
|
|
24
|
+
* plain object (a string, an array, a `URL`), or a part that isn't a string,
|
|
25
|
+
* also gets the fallback instead of throwing.
|
|
26
|
+
*/
|
|
27
|
+
export function formatConnectionLabel(engine, parts) {
|
|
28
|
+
const fallback = `configured ${engine} connection`;
|
|
29
|
+
if (!isPlainObject(parts))
|
|
30
|
+
return fallback;
|
|
31
|
+
if ("file" in parts) {
|
|
32
|
+
const { file } = parts;
|
|
33
|
+
return typeof file === "string" && FILE.test(file) && !file.includes("://") ? file : fallback;
|
|
34
|
+
}
|
|
35
|
+
const raw = parts;
|
|
36
|
+
if (![raw.host, raw.port, raw.database].every((part) => part === undefined || typeof part === "string")) {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
const { host, port, database } = raw;
|
|
40
|
+
if (host === undefined && database === undefined)
|
|
41
|
+
return fallback;
|
|
42
|
+
if (host !== undefined && !HOST.test(host))
|
|
43
|
+
return fallback;
|
|
44
|
+
if (port !== undefined && (host === undefined || !PORT.test(port)))
|
|
45
|
+
return fallback;
|
|
46
|
+
if (database !== undefined && !DATABASE.test(database))
|
|
47
|
+
return fallback;
|
|
48
|
+
return `${engine}://${host ?? ""}${port === undefined ? "" : `:${port}`}${database === undefined ? "" : `/${database}`}`;
|
|
49
|
+
}
|
|
50
|
+
function isPlainObject(value) {
|
|
51
|
+
if (typeof value !== "object" || value === null)
|
|
52
|
+
return false;
|
|
53
|
+
const proto = Object.getPrototypeOf(value);
|
|
54
|
+
return proto === Object.prototype || proto === null;
|
|
55
|
+
}
|
|
56
|
+
// scheme://authority[/path][?query] — no fragment, and the whole input must match.
|
|
57
|
+
const URL_FORM = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^/?#]*)(\/[^?#]*)?(?:\?[^#]*)?$/;
|
|
58
|
+
/**
|
|
59
|
+
* Parse a standard `scheme://[user[:password]@]host[:port][/database][?query]`
|
|
60
|
+
* connection URL (Postgres, MySQL, SQL Server `mssql://`) into label parts.
|
|
61
|
+
* Userinfo and the query string are never returned. Returns `undefined` —
|
|
62
|
+
* so the label falls back — when:
|
|
63
|
+
*
|
|
64
|
+
* - the scheme is not one of `schemes` (so `jdbc:postgresql://…`, a quoted
|
|
65
|
+
* URL, and `postgres:/…` all fall back);
|
|
66
|
+
* - the input contains whitespace, a control character, or a `#`;
|
|
67
|
+
* - an `@` appears after the authority (a password containing `/`, `?` or `#`
|
|
68
|
+
* cut the authority short, so where it ends is unknown);
|
|
69
|
+
* - the host is not a single `host[:port]`, or the path has more than one segment.
|
|
70
|
+
*/
|
|
71
|
+
export function parseConnectionUrl(input, schemes) {
|
|
72
|
+
if (/[\s\u0000-\u001f\u007f]/.test(input))
|
|
73
|
+
return undefined;
|
|
74
|
+
const match = URL_FORM.exec(input);
|
|
75
|
+
if (!match)
|
|
76
|
+
return undefined;
|
|
77
|
+
const [, scheme = "", authority = "", path = ""] = match;
|
|
78
|
+
if (!schemes.includes(scheme.toLowerCase()))
|
|
79
|
+
return undefined;
|
|
80
|
+
const authorityEnd = scheme.length + "://".length + authority.length;
|
|
81
|
+
if (input.includes("@", authorityEnd))
|
|
82
|
+
return undefined;
|
|
83
|
+
const hostPort = splitHostPort(authority.slice(authority.lastIndexOf("@") + 1));
|
|
84
|
+
if (!hostPort)
|
|
85
|
+
return undefined;
|
|
86
|
+
const database = path.length > 1 ? path.slice(1) : undefined;
|
|
87
|
+
if (database?.includes("/"))
|
|
88
|
+
return undefined;
|
|
89
|
+
return {
|
|
90
|
+
...(hostPort.host === "" ? {} : { host: hostPort.host }),
|
|
91
|
+
...(hostPort.port === undefined ? {} : { port: hostPort.port }),
|
|
92
|
+
...(database === undefined ? {} : { database }),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function splitHostPort(value) {
|
|
96
|
+
if (value.startsWith("[")) {
|
|
97
|
+
const close = value.indexOf("]");
|
|
98
|
+
if (close === -1)
|
|
99
|
+
return undefined;
|
|
100
|
+
const rest = value.slice(close + 1);
|
|
101
|
+
if (rest === "")
|
|
102
|
+
return { host: value.slice(0, close + 1) };
|
|
103
|
+
return rest.startsWith(":") ? { host: value.slice(0, close + 1), port: rest.slice(1) } : undefined;
|
|
104
|
+
}
|
|
105
|
+
const pieces = value.split(":");
|
|
106
|
+
if (pieces.length > 2)
|
|
107
|
+
return undefined;
|
|
108
|
+
return pieces.length === 2 ? { host: pieces[0], port: pieces[1] } : { host: value };
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.js","sourceRoot":"","sources":["../../src/kit/label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,IAAI,GAAG,uEAAuE,CAAC;AACrF,MAAM,IAAI,GAAG,cAAc,CAAC;AAC5B,MAAM,QAAQ,GAAG,oBAAoB,CAAC;AACtC,6EAA6E;AAC7E,4EAA4E;AAC5E,MAAM,IAAI,GAAG,gCAAgC,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,KAAuC;IAEvC,MAAM,QAAQ,GAAG,cAAc,MAAM,aAAa,CAAC;IACnD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3C,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QACpB,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QACvB,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChG,CAAC;IACD,MAAM,GAAG,GAAG,KAA+D,CAAC;IAC5E,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;QACxG,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,GAA0D,CAAC;IAC5F,IAAI,IAAI,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAClE,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5D,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IACpF,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxE,OAAO,GAAG,MAAM,MAAM,IAAI,IAAI,EAAE,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,GACrE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,EAC5C,EAAE,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAY,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACpD,OAAO,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC;AACtD,CAAC;AAED,mFAAmF;AACnF,MAAM,QAAQ,GAAG,kEAAkE,CAAC;AAEpF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,OAA0B;IAE1B,IAAI,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,EAAE,SAAS,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC;IACzD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;IACrE,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;QAAE,OAAO,SAAS,CAAC;IACxD,MAAM,QAAQ,GAAG,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChF,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,IAAI,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,OAAO;QACL,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxD,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/D,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACpC,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrG,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ConnectorProviderAdapter, ConnectorProviderId, ConnectorRuntimeConfig } from "../registry.js";
|
|
2
|
+
import type { CatalogQueryRunner, Connector, IntrospectionFilters } from "../types.js";
|
|
3
|
+
import type { ConnectionLabelParts } from "./label.js";
|
|
4
|
+
/** Input shape of a live-only catalog connector: `{ mode: "live", runner, filters? }`. */
|
|
5
|
+
export type LiveCatalogInput = {
|
|
6
|
+
mode: "live";
|
|
7
|
+
runner: CatalogQueryRunner;
|
|
8
|
+
filters?: IntrospectionFilters;
|
|
9
|
+
};
|
|
10
|
+
export type LiveConnectorProviderSpec = {
|
|
11
|
+
provider: ConnectorProviderId;
|
|
12
|
+
/** Engine name used in error messages, e.g. `"MySQL"`. */
|
|
13
|
+
displayName: string;
|
|
14
|
+
/**
|
|
15
|
+
* Key in `runtime.introspection` (from `@askdb/config`) holding the configured
|
|
16
|
+
* connection, e.g. `"mysqlDatabaseUrl"`. An explicit `url` always wins.
|
|
17
|
+
*/
|
|
18
|
+
runtimeKey: string;
|
|
19
|
+
/** What `config.url` holds, for the missing-url error: `"a connection URL"`, `"a file path"`. */
|
|
20
|
+
connectionNoun: string;
|
|
21
|
+
/**
|
|
22
|
+
* Error when the caller passes an export bundle (`--from-export`), which a
|
|
23
|
+
* live-only engine can't read. Defaults to
|
|
24
|
+
* `--from-export is not supported for --engine <provider>.`
|
|
25
|
+
*/
|
|
26
|
+
fromExportUnsupported?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Error when neither an explicit URL nor a configured one exists: `cli` names
|
|
29
|
+
* `askdb introspect` flags, `config` names `askdb.config.ts` keys.
|
|
30
|
+
*/
|
|
31
|
+
missingConnection: {
|
|
32
|
+
cli: string;
|
|
33
|
+
config: string;
|
|
34
|
+
};
|
|
35
|
+
createConnector(): Connector<LiveCatalogInput>;
|
|
36
|
+
createRunner(url: string): CatalogQueryRunner;
|
|
37
|
+
/**
|
|
38
|
+
* The display-safe parts of a connection string (`{ host?, port?, database? }`
|
|
39
|
+
* or `{ file }`), parsed by the engine's own rules, or `undefined` when it
|
|
40
|
+
* doesn't parse cleanly. Becomes the adapter's `connectionLabelParts`; the
|
|
41
|
+
* registry builds the label from it (ADR 0011).
|
|
42
|
+
*/
|
|
43
|
+
connectionLabelParts(url: string): ConnectionLabelParts | undefined;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Build the `ConnectorProviderAdapter` for an engine that introspects a live
|
|
47
|
+
* database through a `CatalogQueryRunner` only (no export bundle, no schema
|
|
48
|
+
* file) — the shape shared by `@askdb/mysql`, `@askdb/sqlite`, and
|
|
49
|
+
* `@askdb/sqlserver`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function defineLiveConnectorProvider(spec: LiveConnectorProviderSpec): ConnectorProviderAdapter;
|
|
52
|
+
/**
|
|
53
|
+
* Read a non-empty string from `runtime.introspection[key]` — the shape
|
|
54
|
+
* `@askdb/config` resolves per-engine connection values into.
|
|
55
|
+
*/
|
|
56
|
+
export declare function runtimeIntrospectionString(runtime: ConnectorRuntimeConfig, key: string): string | undefined;
|
|
57
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/kit/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAC5G,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,iGAAiG;IACjG,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,iBAAiB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,eAAe,IAAI,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC/C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAAC;IAC9C;;;;;OAKG;IACH,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAAC;CACrE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,yBAAyB,GAAG,wBAAwB,CAkCrG;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,sBAAsB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3G"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the `ConnectorProviderAdapter` for an engine that introspects a live
|
|
3
|
+
* database through a `CatalogQueryRunner` only (no export bundle, no schema
|
|
4
|
+
* file) — the shape shared by `@askdb/mysql`, `@askdb/sqlite`, and
|
|
5
|
+
* `@askdb/sqlserver`.
|
|
6
|
+
*/
|
|
7
|
+
export function defineLiveConnectorProvider(spec) {
|
|
8
|
+
return {
|
|
9
|
+
provider: spec.provider,
|
|
10
|
+
createConnector(config) {
|
|
11
|
+
if (!config.url) {
|
|
12
|
+
throw new Error(`${spec.displayName} connector requires ${spec.connectionNoun} (config.url).`);
|
|
13
|
+
}
|
|
14
|
+
const input = {
|
|
15
|
+
mode: "live",
|
|
16
|
+
runner: spec.createRunner(config.url),
|
|
17
|
+
filters: config.filters,
|
|
18
|
+
};
|
|
19
|
+
return { mode: "live", input, connector: spec.createConnector() };
|
|
20
|
+
},
|
|
21
|
+
resolveConnection({ explicit = {}, runtime, surface }) {
|
|
22
|
+
const url = explicit.url ?? runtimeIntrospectionString(runtime, spec.runtimeKey);
|
|
23
|
+
if (!url) {
|
|
24
|
+
return {
|
|
25
|
+
ok: false,
|
|
26
|
+
error: surface === "cli" ? spec.missingConnection.cli : spec.missingConnection.config,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (explicit.fromExport) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
error: spec.fromExportUnsupported ?? `--from-export is not supported for --engine ${spec.provider}.`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, connection: { url } };
|
|
36
|
+
},
|
|
37
|
+
connectionLabelParts({ url }) {
|
|
38
|
+
return url === undefined ? undefined : spec.connectionLabelParts(url);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read a non-empty string from `runtime.introspection[key]` — the shape
|
|
44
|
+
* `@askdb/config` resolves per-engine connection values into.
|
|
45
|
+
*/
|
|
46
|
+
export function runtimeIntrospectionString(runtime, key) {
|
|
47
|
+
const value = runtime.introspection[key];
|
|
48
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/kit/provider.ts"],"names":[],"mappings":"AA4CA;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CAAC,IAA+B;IACzE,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,eAAe,CAAC,MAAM;YACpB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,uBAAuB,IAAI,CAAC,cAAc,gBAAgB,CAAC,CAAC;YACjG,CAAC;YACD,MAAM,KAAK,GAAqB;gBAC9B,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC;gBACrC,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC;YACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,eAAe,EAAwB,EAAE,CAAC;QAC1F,CAAC;QACD,iBAAiB,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;YACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,IAAI,0BAA0B,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACjF,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM;iBACtF,CAAC;YACJ,CAAC;YACD,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;gBACxB,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,IAAI,CAAC,qBAAqB,IAAI,+CAA+C,IAAI,CAAC,QAAQ,GAAG;iBACrG,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;QAC3C,CAAC;QACD,oBAAoB,CAAC,EAAE,GAAG,EAAE;YAC1B,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACxE,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAA+B,EAAE,GAAW;IACrF,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CatalogQueryResult, SqlForeignKeyAction } from "../types.js";
|
|
2
|
+
export type RowsToRecordsOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Columns the caller's SQL promises to return. When set, only these columns
|
|
5
|
+
* are copied into each record, and a result that lacks one of them throws —
|
|
6
|
+
* that signals a runner or bundle that does not match the query's contract.
|
|
7
|
+
* When omitted, every returned column is copied.
|
|
8
|
+
*/
|
|
9
|
+
expectedColumns?: readonly string[];
|
|
10
|
+
/** Prefix for the missing-column error, e.g. `"@askdb/postgres"`. */
|
|
11
|
+
source?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Turn a positional `CatalogQueryResult` into one record per row keyed by
|
|
15
|
+
* column name. A result with zero rows yields `[]` even when the runner
|
|
16
|
+
* omitted the column headers (some drivers / fake runners don't populate them
|
|
17
|
+
* on empty result sets).
|
|
18
|
+
*/
|
|
19
|
+
export declare function rowsToRecords<T>(result: CatalogQueryResult, options?: RowsToRecordsOptions): T[];
|
|
20
|
+
/** Group rows by key, preserving first-seen key order and row order within a group. */
|
|
21
|
+
export declare function groupBy<T, K>(rows: Iterable<T>, key: (row: T) => K): Map<K, T[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Group rows by key, order each group by `position` (e.g. a constraint's
|
|
24
|
+
* column ordinal), and build one named object per group; `build` may return
|
|
25
|
+
* `undefined` to drop a group. The result is sorted by name — the shape every
|
|
26
|
+
* multi-column constraint/index builder needs for deterministic output.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildOrderedGroups<T, K, R extends {
|
|
29
|
+
name: string;
|
|
30
|
+
}>(rows: Iterable<T>, key: (row: T) => K, position: (row: T) => number, build: (key: K, ordered: T[]) => R | undefined): R[];
|
|
31
|
+
/** Comparator: `localeCompare` on `.name`. */
|
|
32
|
+
export declare function byName<T extends {
|
|
33
|
+
name: string;
|
|
34
|
+
}>(a: T, b: T): number;
|
|
35
|
+
/** Deduplicate and `localeCompare`-sort. */
|
|
36
|
+
export declare function sortedUnique(values: Iterable<string>): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Map an SQL-standard referential action name (`CASCADE`, `SET NULL`, …;
|
|
39
|
+
* case-insensitive) to the Schema v2 action. Unknown or empty → `undefined`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mapFkAction(rule: string | null | undefined): SqlForeignKeyAction | undefined;
|
|
42
|
+
//# sourceMappingURL=rows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/kit/rows.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE3E,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAE,oBAAyB,GAAG,CAAC,EAAE,CAsBpG;AAED,uFAAuF;AACvF,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAShF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EACjE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EACjB,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAClB,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,EAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,SAAS,GAC7C,CAAC,EAAE,CAOL;AAED,8CAA8C;AAC9C,wBAAgB,MAAM,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,CAErE;AAED,4CAA4C;AAC5C,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAE/D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,mBAAmB,GAAG,SAAS,CAS5F"}
|
package/dist/kit/rows.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a positional `CatalogQueryResult` into one record per row keyed by
|
|
3
|
+
* column name. A result with zero rows yields `[]` even when the runner
|
|
4
|
+
* omitted the column headers (some drivers / fake runners don't populate them
|
|
5
|
+
* on empty result sets).
|
|
6
|
+
*/
|
|
7
|
+
export function rowsToRecords(result, options = {}) {
|
|
8
|
+
if (result.rows.length === 0)
|
|
9
|
+
return [];
|
|
10
|
+
const indexByName = new Map();
|
|
11
|
+
if (options.expectedColumns) {
|
|
12
|
+
for (const name of options.expectedColumns) {
|
|
13
|
+
const idx = result.columns.indexOf(name);
|
|
14
|
+
if (idx === -1) {
|
|
15
|
+
const prefix = options.source ? `${options.source}: ` : "";
|
|
16
|
+
throw new Error(`${prefix}result is missing column '${name}' (got [${result.columns.join(", ")}])`);
|
|
17
|
+
}
|
|
18
|
+
indexByName.set(name, idx);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
for (let i = 0; i < result.columns.length; i++)
|
|
23
|
+
indexByName.set(result.columns[i], i);
|
|
24
|
+
}
|
|
25
|
+
return result.rows.map((row) => {
|
|
26
|
+
const record = {};
|
|
27
|
+
for (const [name, idx] of indexByName)
|
|
28
|
+
record[name] = row[idx];
|
|
29
|
+
return record;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** Group rows by key, preserving first-seen key order and row order within a group. */
|
|
33
|
+
export function groupBy(rows, key) {
|
|
34
|
+
const out = new Map();
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
const k = key(row);
|
|
37
|
+
const list = out.get(k) ?? [];
|
|
38
|
+
list.push(row);
|
|
39
|
+
out.set(k, list);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Group rows by key, order each group by `position` (e.g. a constraint's
|
|
45
|
+
* column ordinal), and build one named object per group; `build` may return
|
|
46
|
+
* `undefined` to drop a group. The result is sorted by name — the shape every
|
|
47
|
+
* multi-column constraint/index builder needs for deterministic output.
|
|
48
|
+
*/
|
|
49
|
+
export function buildOrderedGroups(rows, key, position, build) {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const [k, list] of groupBy(rows, key)) {
|
|
52
|
+
const built = build(k, list.slice().sort((a, b) => position(a) - position(b)));
|
|
53
|
+
if (built !== undefined)
|
|
54
|
+
out.push(built);
|
|
55
|
+
}
|
|
56
|
+
return out.sort(byName);
|
|
57
|
+
}
|
|
58
|
+
/** Comparator: `localeCompare` on `.name`. */
|
|
59
|
+
export function byName(a, b) {
|
|
60
|
+
return a.name.localeCompare(b.name);
|
|
61
|
+
}
|
|
62
|
+
/** Deduplicate and `localeCompare`-sort. */
|
|
63
|
+
export function sortedUnique(values) {
|
|
64
|
+
return Array.from(new Set(values)).sort((a, b) => a.localeCompare(b));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Map an SQL-standard referential action name (`CASCADE`, `SET NULL`, …;
|
|
68
|
+
* case-insensitive) to the Schema v2 action. Unknown or empty → `undefined`.
|
|
69
|
+
*/
|
|
70
|
+
export function mapFkAction(rule) {
|
|
71
|
+
if (!rule)
|
|
72
|
+
return undefined;
|
|
73
|
+
const r = rule.toLowerCase();
|
|
74
|
+
if (r === "cascade")
|
|
75
|
+
return "cascade";
|
|
76
|
+
if (r === "restrict")
|
|
77
|
+
return "restrict";
|
|
78
|
+
if (r === "set null")
|
|
79
|
+
return "set null";
|
|
80
|
+
if (r === "set default")
|
|
81
|
+
return "set default";
|
|
82
|
+
if (r === "no action")
|
|
83
|
+
return "no action";
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=rows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rows.js","sourceRoot":"","sources":["../../src/kit/rows.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { type ConnectionLabelParts } from "./kit/label.js";
|
|
2
|
+
import type { Connector, IntrospectionFilters, SqlTemplateBundle } from "./types.js";
|
|
3
|
+
/** Provider ids of the first-party engine packages (`@askdb/<id>`). */
|
|
4
|
+
export declare const BUILT_IN_CONNECTOR_PROVIDERS: readonly ["postgres", "prisma", "mysql", "sqlite", "sqlserver"];
|
|
5
|
+
export type BuiltInConnectorProvider = (typeof BUILT_IN_CONNECTOR_PROVIDERS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* A connector provider id. Open: any string is valid, so a third-party engine
|
|
8
|
+
* package registers its own id without editing AskDB. The built-in ids stay in
|
|
9
|
+
* the type for editor autocompletion.
|
|
10
|
+
*/
|
|
11
|
+
export type ConnectorProviderId = BuiltInConnectorProvider | (string & {});
|
|
12
|
+
/**
|
|
13
|
+
* Unified config passed to a connector provider adapter. The registry converts
|
|
14
|
+
* this into the engine-specific connector + input pair consumed by `introspect()`.
|
|
15
|
+
*/
|
|
16
|
+
export type ConnectorConfig = {
|
|
17
|
+
provider: ConnectorProviderId;
|
|
18
|
+
/** Connection URL for live introspection (postgres, mysql, sqlserver) or file path (sqlite). */
|
|
19
|
+
url?: string;
|
|
20
|
+
/** Bundle directory path for from-export mode (postgres only). */
|
|
21
|
+
fromExport?: string;
|
|
22
|
+
/** Prisma schema file path or directory containing `.prisma` files (prisma only). */
|
|
23
|
+
schemaPath?: string;
|
|
24
|
+
filters?: IntrospectionFilters;
|
|
25
|
+
/** Schema ID embedded in the resulting `SqlSchema`. Defaults to `"introspected"`. */
|
|
26
|
+
schemaId?: string;
|
|
27
|
+
};
|
|
28
|
+
/** Connector + typed input pair returned by a provider adapter. */
|
|
29
|
+
export type ConnectorResult = {
|
|
30
|
+
connector: Connector<unknown>;
|
|
31
|
+
input: unknown;
|
|
32
|
+
/** Informational mode string (e.g. `"live"`, `"from-export"`, `"prisma-schema"`). */
|
|
33
|
+
mode: string;
|
|
34
|
+
};
|
|
35
|
+
/** Where a connection comes from: the three `ConnectorConfig` source fields. */
|
|
36
|
+
export type ConnectorConnection = Pick<ConnectorConfig, "url" | "fromExport" | "schemaPath">;
|
|
37
|
+
/**
|
|
38
|
+
* The AskDB runtime configuration an adapter resolves its connection from.
|
|
39
|
+
* Structurally typed so engine packages need not depend on `@askdb/config`;
|
|
40
|
+
* pass `getAskDbRuntimeConfig()` from `@askdb/config` as-is.
|
|
41
|
+
*/
|
|
42
|
+
export type ConnectorRuntimeConfig = {
|
|
43
|
+
/**
|
|
44
|
+
* Resolved introspection values (`getAskDbRuntimeConfig().introspection`),
|
|
45
|
+
* e.g. `postgresDatabaseUrl`, `sqliteFile`, `prismaSchemaPath` — config file
|
|
46
|
+
* first, then the engine's env var, as resolved by `@askdb/config`.
|
|
47
|
+
*/
|
|
48
|
+
readonly introspection: Readonly<Record<string, unknown>>;
|
|
49
|
+
/**
|
|
50
|
+
* The authoring-time config (`getAskDbRuntimeConfig().structured`). Engines
|
|
51
|
+
* without a first-class runtime field read their own block from
|
|
52
|
+
* `structured.introspection.providerConfig.<id>` here: `@askdb/config` passes
|
|
53
|
+
* it through unchanged, though its typed config doesn't declare third-party
|
|
54
|
+
* ids yet.
|
|
55
|
+
*/
|
|
56
|
+
readonly structured?: unknown;
|
|
57
|
+
};
|
|
58
|
+
export type ConnectorConnectionRequest = {
|
|
59
|
+
/** Values the caller passed explicitly (e.g. CLI flags). They take precedence over config. */
|
|
60
|
+
explicit?: ConnectorConnection;
|
|
61
|
+
/** AskDB runtime configuration to fall back to. */
|
|
62
|
+
runtime: ConnectorRuntimeConfig;
|
|
63
|
+
/**
|
|
64
|
+
* The surface asking, so errors can point at the right fix: `"cli"` names
|
|
65
|
+
* `askdb introspect` flags; anything else (e.g. `"studio"`) names config keys.
|
|
66
|
+
*/
|
|
67
|
+
surface?: "cli" | "studio" | (string & {});
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* What an adapter's `resolveConnection` returns: the connection, or an error.
|
|
71
|
+
* The error is shown to users as-is, so it must never echo the connection
|
|
72
|
+
* string. There is no label here: the registry builds it (ADR 0011).
|
|
73
|
+
*/
|
|
74
|
+
export type ConnectorConnectionResult = {
|
|
75
|
+
ok: true;
|
|
76
|
+
connection: ConnectorConnection;
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
error: string;
|
|
80
|
+
};
|
|
81
|
+
/** What `registry.resolveConnection` returns: the adapter's result plus the registry-built label. */
|
|
82
|
+
export type ConnectorConnectionResolution = {
|
|
83
|
+
ok: true;
|
|
84
|
+
connection: ConnectorConnection;
|
|
85
|
+
/**
|
|
86
|
+
* Credential-free description of the source, safe to show in a UI or log:
|
|
87
|
+
* `registry.connectionLabel(provider, connection)`.
|
|
88
|
+
*/
|
|
89
|
+
sourceLabel: string;
|
|
90
|
+
} | {
|
|
91
|
+
ok: false;
|
|
92
|
+
error: string;
|
|
93
|
+
};
|
|
94
|
+
export type ConnectorProviderAdapter = {
|
|
95
|
+
provider: ConnectorProviderId;
|
|
96
|
+
createConnector(config: ConnectorConfig): ConnectorResult;
|
|
97
|
+
/** Returns the engine's catalog SQL template bundle, if the engine supports it. */
|
|
98
|
+
getTemplates?(): SqlTemplateBundle;
|
|
99
|
+
/**
|
|
100
|
+
* Turn explicit values + AskDB runtime config into this engine's connection
|
|
101
|
+
* (URL, export bundle, or schema path), applying the engine's precedence and
|
|
102
|
+
* validation. Hosts (CLI, Studio) call this instead of switching on the engine.
|
|
103
|
+
* When omitted, the registry passes `explicit` through unchanged.
|
|
104
|
+
*/
|
|
105
|
+
resolveConnection?(request: ConnectorConnectionRequest): ConnectorConnectionResult;
|
|
106
|
+
/**
|
|
107
|
+
* The parts of `connection` that are safe to display (host, port, database,
|
|
108
|
+
* or a file path), parsed by the engine's own rules; `undefined` when the
|
|
109
|
+
* connection doesn't parse cleanly. The registry turns them into the label
|
|
110
|
+
* with `formatConnectionLabel`, so an adapter never supplies label text
|
|
111
|
+
* itself (ADR 0011).
|
|
112
|
+
*/
|
|
113
|
+
connectionLabelParts?(connection: ConnectorConnection): ConnectionLabelParts | undefined;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* A registered adapter that the registry imports on first use, for an engine
|
|
117
|
+
* package the host may not have installed (an optional peer):
|
|
118
|
+
*
|
|
119
|
+
* { provider: "prisma", load: async () => (await import("@askdb/prisma")).prismaConnectorProvider }
|
|
120
|
+
*
|
|
121
|
+
* `hasProvider()` and `providers()` answer from `provider` without loading.
|
|
122
|
+
* Call `registry.load(provider)` once the engine is selected; the other
|
|
123
|
+
* methods need the adapter and throw until it is loaded. `load()` errors reach
|
|
124
|
+
* the caller unchanged, so give a missing package its install hint in `load`
|
|
125
|
+
* (for example with `createOptionalDriverLoader` from `@askdb/introspect/kit`
|
|
126
|
+
* and `connectorPackageMissingMessage`).
|
|
127
|
+
*/
|
|
128
|
+
export type LazyConnectorProviderAdapter = {
|
|
129
|
+
provider: ConnectorProviderId;
|
|
130
|
+
load(): Promise<ConnectorProviderAdapter>;
|
|
131
|
+
};
|
|
132
|
+
/** An adapter, or a lazy entry that loads one on first use. */
|
|
133
|
+
export type ConnectorProviderEntry = ConnectorProviderAdapter | LazyConnectorProviderAdapter;
|
|
134
|
+
export type ConnectorProviderAdapters = readonly ConnectorProviderEntry[] | Readonly<Record<string, ConnectorProviderEntry | undefined>>;
|
|
135
|
+
export type ConnectorRegistry = {
|
|
136
|
+
/** True for a registered provider, lazy entries included (without loading them). */
|
|
137
|
+
hasProvider(provider: string): boolean;
|
|
138
|
+
/** Registered provider ids, in registration order, lazy entries included (without loading them). */
|
|
139
|
+
providers(): ConnectorProviderId[];
|
|
140
|
+
/**
|
|
141
|
+
* The provider's adapter, loading a lazy entry on the first call. Concurrent
|
|
142
|
+
* calls share one `load()`; a failed load is retried on the next call. Call it
|
|
143
|
+
* once the engine is selected, before the methods below. Rejects when the
|
|
144
|
+
* provider is not registered, with the entry's own `load()` error, or when the
|
|
145
|
+
* loaded adapter's `provider` differs from the entry's.
|
|
146
|
+
*/
|
|
147
|
+
load(provider: string): Promise<ConnectorProviderAdapter>;
|
|
148
|
+
createConnector(config: ConnectorConfig): ConnectorResult;
|
|
149
|
+
/**
|
|
150
|
+
* Returns the catalog SQL template bundle for the given provider, or `undefined`
|
|
151
|
+
* if the provider is not registered or does not support templates.
|
|
152
|
+
*/
|
|
153
|
+
getTemplates(provider: string): SqlTemplateBundle | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* Resolve a provider's connection via its adapter's `resolveConnection` hook
|
|
156
|
+
* (or pass `explicit` through when the adapter has none), with `sourceLabel`
|
|
157
|
+
* from `connectionLabel`. A blank explicit value (empty or whitespace-only)
|
|
158
|
+
* counts as absent, so the configured connection applies. Throws when the
|
|
159
|
+
* provider is not registered.
|
|
160
|
+
*/
|
|
161
|
+
resolveConnection(provider: string, request: ConnectorConnectionRequest): ConnectorConnectionResolution;
|
|
162
|
+
/**
|
|
163
|
+
* A credential-free label for a connection, safe to show in a UI or log:
|
|
164
|
+
* `formatConnectionLabel(provider, parts)` over the adapter's
|
|
165
|
+
* `connectionLabelParts`. A provider that is not registered, has no hook, or
|
|
166
|
+
* returns `undefined` gets `configured <provider> connection`.
|
|
167
|
+
*/
|
|
168
|
+
connectionLabel(provider: string, connection: ConnectorConnection): string;
|
|
169
|
+
};
|
|
170
|
+
export declare function createConnectorRegistry(adapters: ConnectorProviderAdapters): ConnectorRegistry;
|
|
171
|
+
export declare function connectorProviderMissingMessage(provider: string): string;
|
|
172
|
+
/**
|
|
173
|
+
* The install hint the first-party hosts (the `askdb` CLI and Studio) give for a
|
|
174
|
+
* registered engine whose package isn't installed: a lazy entry's `load()`
|
|
175
|
+
* throws it when the package resolves from nowhere (see
|
|
176
|
+
* `createOptionalDriverLoader` in `@askdb/introspect/kit`). `allowBuilds` names
|
|
177
|
+
* the dependencies whose install scripts pnpm must approve (pnpm 11 exits 1 on
|
|
178
|
+
* an unapproved one); with any, the hint gives an npm command and the pnpm 10.4+
|
|
179
|
+
* `--allow-build` one. It suggests a one-off `askdb` command, and the `askdb`
|
|
180
|
+
* binary registers only the first-party engines, so a third-party host writes
|
|
181
|
+
* its own message.
|
|
182
|
+
*/
|
|
183
|
+
export declare function connectorPackageMissingMessage(input: {
|
|
184
|
+
provider: string;
|
|
185
|
+
packageName: string;
|
|
186
|
+
allowBuilds?: readonly string[];
|
|
187
|
+
}): string;
|
|
188
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAErF,uEAAuE;AACvE,eAAO,MAAM,4BAA4B,iEAM/B,CAAC;AAEX,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC;AAErF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,gGAAgG;IAChG,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,gFAAgF;AAChF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC,CAAC;AAE7F;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,mDAAmD;IACnD,OAAO,EAAE,sBAAsB,CAAC;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;CAC5C,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,mBAAmB,CAAA;CAAE,GAC7C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,qGAAqG;AACrG,MAAM,MAAM,6BAA6B,GACrC;IACE,EAAE,EAAE,IAAI,CAAC;IACT,UAAU,EAAE,mBAAmB,CAAC;IAChC;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAAC;IAC1D,mFAAmF;IACnF,YAAY,CAAC,IAAI,iBAAiB,CAAC;IACnC;;;;;OAKG;IACH,iBAAiB,CAAC,CAAC,OAAO,EAAE,0BAA0B,GAAG,yBAAyB,CAAC;IACnF;;;;;;OAMG;IACH,oBAAoB,CAAC,CAAC,UAAU,EAAE,mBAAmB,GAAG,oBAAoB,GAAG,SAAS,CAAC;CAC1F,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC3C,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,sBAAsB,GAAG,wBAAwB,GAAG,4BAA4B,CAAC;AAE7F,MAAM,MAAM,yBAAyB,GACjC,SAAS,sBAAsB,EAAE,GACjC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,SAAS,CAAC,CAAC,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,oFAAoF;IACpF,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,oGAAoG;IACpG,SAAS,IAAI,mBAAmB,EAAE,CAAC;IACnC;;;;;;OAMG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC1D,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAAC;IAC1D;;;OAGG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAC9D;;;;;;OAMG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,0BAA0B,GAAG,6BAA6B,CAAC;IACxG;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAAC;CAC5E,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,iBAAiB,CA+E9F;AAiBD,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,GAAG,MAAM,CAYT"}
|