@dbx-tools/appkit 0.3.44 → 0.4.1

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.
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Flexible address parser for Lakebase Postgres connection inputs.
3
+ *
4
+ * Accepts whatever shape a user is likely to paste into
5
+ * `LAKEBASE_ENDPOINT` (or the matching config field) and extracts
6
+ * every recognizable piece. Whatever it can't recover is left for the
7
+ * Lakebase resolver to discover.
8
+ *
9
+ * Recognized formats:
10
+ *
11
+ * - **Postgres URI** -
12
+ * `postgresql://user@host:port/db?sslmode=require` (also `postgres://`).
13
+ * Yields `user`, `host`, `port`, `database`, `sslMode`.
14
+ *
15
+ * - **Canonical endpoint resource path** -
16
+ * `projects/{p}/branches/{b}/endpoints/{e}` -
17
+ * yields `project`, `branch`, `endpointId`, and the original string as
18
+ * `endpoint` (already in lakebase's expected form).
19
+ *
20
+ * - **Database resource path** -
21
+ * `projects/{p}/branches/{b}/databases/{d}` -
22
+ * yields `project`, `branch`, and `databaseResourceId` (the UC
23
+ * resource leaf, not `PGDATABASE`; the resolver looks up the real
24
+ * Postgres name via REST).
25
+ *
26
+ * - **Branch resource path** -
27
+ * `projects/{p}/branches/{b}` - yields `project`, `branch`.
28
+ *
29
+ * - **Project resource path** -
30
+ * `projects/{p}` - yields `project`.
31
+ *
32
+ * - **Bare hostname** -
33
+ * `ep-steep-forest-e199v43w.database.eastus2.azuredatabricks.net` -
34
+ * yields `host` only; the resolver reverse-looks up the owning
35
+ * endpoint to recover the resource path.
36
+ *
37
+ * - **Bare project id** -
38
+ * `dbx-tools-demo` (1-63 chars, lowercase letters/digits/hyphens) -
39
+ * yields `project`.
40
+ *
41
+ * Returns an empty object for inputs it doesn't recognize.
42
+ *
43
+ * @module
44
+ */
45
+ /** Postgres TLS modes accepted by {@link SslMode}, in `PGSSLMODE` spelling. */
46
+ export declare const SSL_MODES: readonly ["require", "disable", "prefer"];
47
+ /** Postgres TLS mode passed through to `pg`. */
48
+ export type SslMode = (typeof SSL_MODES)[number];
49
+ /**
50
+ * Optional Lakebase Postgres connection fields shared by parsed addresses,
51
+ * resolver/env inputs, and resolved connections.
52
+ */
53
+ export interface LakebaseConnectionInputs {
54
+ /** Lakebase project id. Resolved from the workspace when unset. */
55
+ project?: string;
56
+ /** Branch id within the project. Defaults to the project's default branch. */
57
+ branch?: string;
58
+ /**
59
+ * Canonical endpoint resource path (`projects/.../endpoints/...`), from
60
+ * `LAKEBASE_ENDPOINT`. Defaults to the branch's read-write endpoint.
61
+ */
62
+ endpoint?: string;
63
+ /** Postgres database name (`PGDATABASE`). Defaults to `databricks_postgres`. */
64
+ database?: string;
65
+ /** Postgres hostname (`PGHOST`). Defaults to the resolved endpoint's host. */
66
+ host?: string;
67
+ /** Postgres port (`PGPORT`). Defaults to 5432. */
68
+ port?: number;
69
+ /** Postgres TLS mode (`PGSSLMODE`). Defaults to `require`. */
70
+ sslMode?: SslMode;
71
+ }
72
+ /** Pieces recovered from parsing a single address or resource-path input. */
73
+ export interface ParsedAddress extends LakebaseConnectionInputs {
74
+ /** Endpoint leaf id (last segment of an endpoint resource path). */
75
+ endpointId?: string;
76
+ /**
77
+ * Database resource id leaf from a `.../databases/{id}` path. Not the
78
+ * Postgres database name.
79
+ */
80
+ databaseResourceId?: string;
81
+ /** Postgres user (URI-decoded if encoded). */
82
+ user?: string;
83
+ }
84
+ /**
85
+ * Parse a Lakebase connection input into whatever pieces it carries.
86
+ * See module docstring for the supported formats. Returns `{}` for
87
+ * `undefined`, empty strings, and unrecognized inputs.
88
+ *
89
+ * @example
90
+ * import { pgaddress } from "@dbx-tools/appkit";
91
+ *
92
+ * pgaddress.parseAddress("projects/demo/branches/production/endpoints/ep-1");
93
+ * // { project: "demo", branch: "production", endpointId: "ep-1", endpoint: "projects/..." }
94
+ *
95
+ * pgaddress.parseAddress("postgresql://me@ep-1.database.azuredatabricks.net/app?sslmode=require");
96
+ * // { host: "ep-1.database.azuredatabricks.net", user: "me", database: "app", sslMode: "require" }
97
+ */
98
+ export declare function parseAddress(input: string | undefined | null): ParsedAddress;
99
+ /**
100
+ * Parse a Lakebase `projects/...` resource path. Returns `{}` when the
101
+ * input is not a resource path (so bare branch ids are not mistaken for
102
+ * project ids).
103
+ */
104
+ export declare function parseResourcePath(input: string | undefined | null): ParsedAddress;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Flexible address parser for Lakebase Postgres connection inputs.
3
+ *
4
+ * Accepts whatever shape a user is likely to paste into
5
+ * `LAKEBASE_ENDPOINT` (or the matching config field) and extracts
6
+ * every recognizable piece. Whatever it can't recover is left for the
7
+ * Lakebase resolver to discover.
8
+ *
9
+ * Recognized formats:
10
+ *
11
+ * - **Postgres URI** -
12
+ * `postgresql://user@host:port/db?sslmode=require` (also `postgres://`).
13
+ * Yields `user`, `host`, `port`, `database`, `sslMode`.
14
+ *
15
+ * - **Canonical endpoint resource path** -
16
+ * `projects/{p}/branches/{b}/endpoints/{e}` -
17
+ * yields `project`, `branch`, `endpointId`, and the original string as
18
+ * `endpoint` (already in lakebase's expected form).
19
+ *
20
+ * - **Database resource path** -
21
+ * `projects/{p}/branches/{b}/databases/{d}` -
22
+ * yields `project`, `branch`, and `databaseResourceId` (the UC
23
+ * resource leaf, not `PGDATABASE`; the resolver looks up the real
24
+ * Postgres name via REST).
25
+ *
26
+ * - **Branch resource path** -
27
+ * `projects/{p}/branches/{b}` - yields `project`, `branch`.
28
+ *
29
+ * - **Project resource path** -
30
+ * `projects/{p}` - yields `project`.
31
+ *
32
+ * - **Bare hostname** -
33
+ * `ep-steep-forest-e199v43w.database.eastus2.azuredatabricks.net` -
34
+ * yields `host` only; the resolver reverse-looks up the owning
35
+ * endpoint to recover the resource path.
36
+ *
37
+ * - **Bare project id** -
38
+ * `dbx-tools-demo` (1-63 chars, lowercase letters/digits/hyphens) -
39
+ * yields `project`.
40
+ *
41
+ * Returns an empty object for inputs it doesn't recognize.
42
+ *
43
+ * @module
44
+ */
45
+ /** Postgres TLS modes accepted by {@link SslMode}, in `PGSSLMODE` spelling. */
46
+ export const SSL_MODES = ["require", "disable", "prefer"];
47
+ const URL_SCHEME_RE = /^(postgres|postgresql):\/\//i;
48
+ const PROJECT_ID_RE = /^[a-z][a-z0-9-]{0,61}[a-z0-9]$|^[a-z]$/;
49
+ const HOSTNAME_HINT_RE = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)+$/i;
50
+ /**
51
+ * Parse a Lakebase connection input into whatever pieces it carries.
52
+ * See module docstring for the supported formats. Returns `{}` for
53
+ * `undefined`, empty strings, and unrecognized inputs.
54
+ *
55
+ * @example
56
+ * import { pgaddress } from "@dbx-tools/appkit";
57
+ *
58
+ * pgaddress.parseAddress("projects/demo/branches/production/endpoints/ep-1");
59
+ * // { project: "demo", branch: "production", endpointId: "ep-1", endpoint: "projects/..." }
60
+ *
61
+ * pgaddress.parseAddress("postgresql://me@ep-1.database.azuredatabricks.net/app?sslmode=require");
62
+ * // { host: "ep-1.database.azuredatabricks.net", user: "me", database: "app", sslMode: "require" }
63
+ */
64
+ export function parseAddress(input) {
65
+ if (!input)
66
+ return {};
67
+ const s = input.trim();
68
+ if (!s)
69
+ return {};
70
+ if (URL_SCHEME_RE.test(s))
71
+ return parseUri(s);
72
+ if (s.startsWith("projects/"))
73
+ return parseResourcePathSegments(s);
74
+ // Resource ids never contain dots; a dotted input must be a hostname.
75
+ if (HOSTNAME_HINT_RE.test(s) && s.includes("."))
76
+ return { host: s };
77
+ if (PROJECT_ID_RE.test(s))
78
+ return { project: s };
79
+ return {};
80
+ }
81
+ /**
82
+ * Parse a Lakebase `projects/...` resource path. Returns `{}` when the
83
+ * input is not a resource path (so bare branch ids are not mistaken for
84
+ * project ids).
85
+ */
86
+ export function parseResourcePath(input) {
87
+ if (!input)
88
+ return {};
89
+ const s = input.trim();
90
+ if (!s.startsWith("projects/"))
91
+ return {};
92
+ return parseResourcePathSegments(s);
93
+ }
94
+ function parseUri(s) {
95
+ let url;
96
+ try {
97
+ url = new URL(s);
98
+ }
99
+ catch {
100
+ return {};
101
+ }
102
+ const result = {};
103
+ if (url.hostname)
104
+ result.host = url.hostname;
105
+ if (url.port) {
106
+ const port = Number.parseInt(url.port, 10);
107
+ if (!Number.isNaN(port))
108
+ result.port = port;
109
+ }
110
+ if (url.username) {
111
+ try {
112
+ result.user = decodeURIComponent(url.username);
113
+ }
114
+ catch {
115
+ result.user = url.username;
116
+ }
117
+ }
118
+ const db = url.pathname.replace(/^\//, "");
119
+ if (db)
120
+ result.database = decodeURIComponent(db);
121
+ const sslmodeRaw = url.searchParams.get("sslmode") ?? url.searchParams.get("sslMode");
122
+ const sslmode = sslmodeRaw?.toLowerCase();
123
+ if (isSslMode(sslmode)) {
124
+ result.sslMode = sslmode;
125
+ }
126
+ return result;
127
+ }
128
+ function isSslMode(value) {
129
+ return SSL_MODES.some((mode) => mode === value);
130
+ }
131
+ function parseResourcePathSegments(s) {
132
+ const parts = s.split("/");
133
+ if (parts[0] !== "projects" || parts.length < 2) {
134
+ return {};
135
+ }
136
+ const project = parts[1];
137
+ if (!project) {
138
+ return {};
139
+ }
140
+ if (parts.length === 2) {
141
+ return { project };
142
+ }
143
+ if (parts.length === 4 && parts[2] === "branches" && parts[3]) {
144
+ return { project, branch: parts[3] };
145
+ }
146
+ if (parts.length === 6 &&
147
+ parts[2] === "branches" &&
148
+ parts[4] === "endpoints" &&
149
+ parts[3] &&
150
+ parts[5]) {
151
+ return {
152
+ project,
153
+ branch: parts[3],
154
+ endpointId: parts[5],
155
+ endpoint: s,
156
+ };
157
+ }
158
+ if (parts.length === 6 &&
159
+ parts[2] === "branches" &&
160
+ parts[4] === "databases" &&
161
+ parts[3] &&
162
+ parts[5]) {
163
+ return {
164
+ project,
165
+ branch: parts[3],
166
+ databaseResourceId: parts[5],
167
+ };
168
+ }
169
+ return {};
170
+ }
171
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGdhZGRyZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BnYWRkcmVzcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJDRztBQUVILCtFQUErRTtBQUMvRSxNQUFNLENBQUMsTUFBTSxTQUFTLEdBQUcsQ0FBQyxTQUFTLEVBQUUsU0FBUyxFQUFFLFFBQVEsQ0FBVSxDQUFDO0FBMENuRSxNQUFNLGFBQWEsR0FBRyw4QkFBOEIsQ0FBQztBQUNyRCxNQUFNLGFBQWEsR0FBRyx3Q0FBd0MsQ0FBQztBQUMvRCxNQUFNLGdCQUFnQixHQUFHLDhDQUE4QyxDQUFDO0FBRXhFOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLFVBQVUsWUFBWSxDQUFDLEtBQWdDO0lBQzNELElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFDdEIsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3ZCLElBQUksQ0FBQyxDQUFDO1FBQUUsT0FBTyxFQUFFLENBQUM7SUFFbEIsSUFBSSxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUFFLE9BQU8sUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzlDLElBQUksQ0FBQyxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUM7UUFBRSxPQUFPLHlCQUF5QixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ25FLHNFQUFzRTtJQUN0RSxJQUFJLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQztRQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDcEUsSUFBSSxhQUFhLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFD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YmxlIGFkZHJlc3MgcGFyc2VyIGZvciBMYWtlYmFzZSBQb3N0Z3JlcyBjb25uZWN0aW9uIGlucHV0cy5cbiAqXG4gKiBBY2NlcHRzIHdoYXRldmVyIHNoYXBlIGEgdXNlciBpcyBsaWtlbHkgdG8gcGFzdGUgaW50b1xuICogYExBS0VCQVNFX0VORFBPSU5UYCAob3IgdGhlIG1hdGNoaW5nIGNvbmZpZyBmaWVsZCkgYW5kIGV4dHJhY3RzXG4gKiBldmVyeSByZWNvZ25pemFibGUgcGllY2UuIFdoYXRldmVyIGl0IGNhbid0IHJlY292ZXIgaXMgbGVmdCBmb3IgdGhlXG4gKiBMYWtlYmFzZSByZXNvbHZlciB0byBkaXNjb3Zlci5cbiAqXG4gKiBSZWNvZ25pemVkIGZvcm1hdHM6XG4gKlxuICogLSAqKlBvc3RncmVzIFVSSSoqIC1cbiAqICAgYHBvc3RncmVzcWw6Ly91c2VyQGhvc3Q6cG9ydC9kYj9zc2xtb2RlPXJlcXVpcmVgIChhbHNvIGBwb3N0Z3JlczovL2ApLlxuICogICBZaWVsZHMgYHVzZXJgLCBgaG9zdGAsIGBwb3J0YCwgYGRhdGFiYXNlYCwgYHNzbE1vZGVgLlxuICpcbiAqIC0gKipDYW5vbmljYWwgZW5kcG9pbnQgcmVzb3VyY2UgcGF0aCoqIC1cbiAqICAgYHByb2plY3RzL3twfS9icmFuY2hlcy97Yn0vZW5kcG9pbnRzL3tlfWAgLVxuICogICB5aWVsZHMgYHByb2plY3RgLCBgYnJhbmNoYCwgYGVuZHBvaW50SWRgLCBhbmQgdGhlIG9yaWdpbmFsIHN0cmluZyBhc1xuICogICBgZW5kcG9pbnRgIChhbHJlYWR5IGluIGxha2ViYXNlJ3MgZXhwZWN0ZWQgZm9ybSkuXG4gKlxuICogLSAqKkRhdGFiYXNlIHJlc291cmNlIHBhdGgqKiAtXG4gKiAgIGBwcm9qZWN0cy97cH0vYnJhbmNoZXMve2J9L2RhdGFiYXNlcy97ZH1gIC1cbiAqICAgeWllbGRzIGBwcm9qZWN0YCwgYGJyYW5jaGAsIGFuZCBgZGF0YWJhc2VSZXNvdXJjZUlkYCAodGhlIFVDXG4gKiAgIHJlc291cmNlIGxlYWYsIG5vdCBgUEdEQVRBQkFTRWA7IHRoZSByZXNvbHZlciBsb29rcyB1cCB0aGUgcmVhbFxuICogICBQb3N0Z3JlcyBuYW1lIHZpYSBSRVNUKS5cbiAqXG4gKiAtICoqQnJhbmNoIHJlc291cmNlIHBhdGgqKiAtXG4gKiAgIGBwcm9qZWN0cy97cH0vYnJhbmNoZXMve2J9YCAtIHlpZWxkcyBgcHJvamVjdGAsIGBicmFuY2hgLlxuICpcbiAqIC0gKipQcm9qZWN0IHJlc291cmNlIHBhdGgqKiAtXG4gKiAgIGBwcm9qZWN0cy97cH1gIC0geWllbGRzIGBwcm9qZWN0YC5cbiAqXG4gKiAtICoqQmFyZSBob3N0bmFtZSoqIC1cbiAqICAgYGVwLXN0ZWVwLWZvcmVzdC1lMTk5djQzdy5kYXRhYmFzZS5lYXN0dXMyLmF6dXJlZGF0YWJyaWNrcy5uZXRgIC1cbiAqICAgeWllbGRzIGBob3N0YCBvbmx5OyB0aGUgcmVzb2x2ZXIgcmV2ZXJzZS1sb29rcyB1cCB0aGUgb3duaW5nXG4gKiAgIGVuZHBvaW50IHRvIHJlY292ZXIgdGhlIHJlc291cmNlIHBhdGguXG4gKlxuICogLSAqKkJhcmUgcHJvamVjdCBpZCoqIC1cbiAqICAgYGRieC10b29scy1kZW1vYCAoMS02MyBjaGFycywgbG93ZXJjYXNlIGxldHRlcnMvZGlnaXRzL2h5cGhlbnMpIC1cbiAqICAgeWllbGRzIGBwcm9qZWN0YC5cbiAqXG4gKiBSZXR1cm5zIGFuIGVtcHR5IG9iamVjdCBmb3IgaW5wdXRzIGl0IGRvZXNuJ3QgcmVjb2duaXplLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG4vKiogUG9zdGdyZXMgVExTIG1vZGVzIGFjY2VwdGVkIGJ5IHtAbGluayBTc2xNb2RlfSwgaW4gYFBHU1NMTU9ERWAgc3BlbGxpbmcuICovXG5leHBvcnQgY29uc3QgU1NMX01PREVTID0gW1wicmVxdWlyZVwiLCBcImRpc2FibGVcIiwgXCJwcmVmZXJcIl0gYXMgY29uc3Q7XG5cbi8qKiBQb3N0Z3JlcyBUTFMgbW9kZSBwYXNzZWQgdGhyb3VnaCB0byBgcGdgLiAqL1xuZXhwb3J0IHR5cGUgU3NsTW9kZSA9ICh0eXBlb2YgU1NMX01PREVTKVtudW1iZXJdO1xuXG4vKipcbiAqIE9wdGlvbmFsIExha2ViYXNlIFBvc3RncmVzIGNvbm5lY3Rpb24gZmllbGRzIHNoYXJlZCBieSBwYXJzZWQgYWRkcmVzc2VzLFxuICogcmVzb2x2ZXIvZW52IGlucHV0cywgYW5kIHJlc29sdmVkIGNvbm5lY3Rpb25zLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIExha2ViYXNlQ29ubmVjdGlvbklucHV0cyB7XG4gIC8qKiBMYWtlYmFzZSBwcm9qZWN0IGlkLiBSZXNvbHZlZCBmcm9tIHRoZSB3b3Jrc3BhY2Ugd2hlbiB1bnNldC4gKi9cbiAgcHJvamVjdD86IHN0cmluZztcbiAgLyoqIEJyYW5jaCBpZCB3aXRoaW4gdGhlIHByb2plY3QuIERlZmF1bHRzIHRvIHRoZSBwcm9qZWN0J3MgZGVmYXVsdCBicmFuY2guICovXG4gIGJyYW5jaD86IHN0cmluZztcbiAgLyoqXG4gICAqIENhbm9uaWNhbCBlbmRwb2ludCByZXNvdXJjZSBwYXRoIChgcHJvamVjdHMvLi4uL2VuZHBvaW50cy8uLi5gKSwgZnJvbVxuICAgKiBgTEFLRUJBU0VfRU5EUE9JTlRgLiBEZWZhdWx0cyB0byB0aGUgYnJhbmNoJ3MgcmVhZC13cml0ZSBlbmRwb2ludC5cbiAgICovXG4gIGVuZHBvaW50Pzogc3RyaW5nO1xuICAvKiogUG9zdGdyZXMgZGF0YWJhc2UgbmFtZSAoYFBHREFUQUJBU0VgKS4gRGVmYXVsdHMgdG8gYGRhdGFicmlja3NfcG9zdGdyZXNgLiAqL1xuICBkYXRhYmFzZT86IHN0cmluZztcbiAgLyoqIFBvc3RncmVzIGhvc3RuYW1lIChgUEdIT1NUYCkuIERlZmF1bHRzIHRvIHRoZSByZXNvbHZlZCBlbmRwb2ludCdzIGhvc3QuICovXG4gIGhvc3Q/OiBzdHJpbmc7XG4gIC8qKiBQb3N0Z3JlcyBwb3J0IChgUEdQT1JUYCkuIERlZmF1bHRzIHRvIDU0MzIuICovXG4gIHBvcnQ/OiBudW1iZXI7XG4gIC8qKiBQb3N0Z3JlcyBUTFMgbW9kZSAoYFBHU1NMTU9ERWApLiBEZWZhdWx0cyB0byBgcmVxdWlyZWAuICovXG4gIHNzbE1vZGU/OiBTc2xNb2RlO1xufVxuXG4vKiogUGllY2VzIHJlY292ZXJlZCBmcm9tIHBhcnNpbmcgYSBzaW5nbGUgYWRkcmVzcyBvciByZXNvdXJjZS1wYXRoIGlucHV0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBQYXJzZWRBZGRyZXNzIGV4dGVuZHMgTGFrZWJhc2VDb25uZWN0aW9uSW5wdXRzIHtcbiAgLyoqIEVuZHBvaW50IGxlYWYgaWQgKGxhc3Qgc2VnbWVudCBvZiBhbiBlbmRwb2ludCByZXNvdXJjZSBwYXRoKS4gKi9cbiAgZW5kcG9pbnRJZD86IHN0cmluZztcbiAgLyoqXG4gICAqIERhdGFiYXNlIHJlc291cmNlIGlkIGxlYWYgZnJvbSBhIGAuLi4vZGF0YWJhc2VzL3tpZH1gIHBhdGguIE5vdCB0aGVcbiAgICogUG9zdGdyZXMgZGF0YWJhc2UgbmFtZS5cbiAgICovXG4gIGRhdGFiYXNlUmVzb3VyY2VJZD86IHN0cmluZztcbiAgLyoqIFBvc3RncmVzIHVzZXIgKFVSSS1kZWNvZGVkIGlmIGVuY29kZWQpLiAqL1xuICB1c2VyPzogc3RyaW5nO1xufVxuXG5jb25zdCBVUkxfU0NIRU1FX1JFID0gL14ocG9zdGdyZXN8cG9zdGdyZXNxbCk6XFwvXFwvL2k7XG5jb25zdCBQUk9KRUNUX0lEX1JFID0gL15bYS16XVthLXowLTktXXswLDYxfVthLXowLTldJHxeW2Etel0kLztcbmNvbnN0IEhPU1ROQU1FX0hJTlRfUkUgPSAvXlthLXowLTldW2EtejAtOS1dKihcXC5bYS16MC05XVthLXowLTktXSopKyQvaTtcblxuLyoqXG4gKiBQYXJzZSBhIExha2ViYXNlIGNvbm5lY3Rpb24gaW5wdXQgaW50byB3aGF0ZXZlciBwaWVjZXMgaXQgY2Fycmllcy5cbiAqIFNlZSBtb2R1bGUgZG9jc3RyaW5nIGZvciB0aGUgc3VwcG9ydGVkIGZvcm1hdHMuIFJldHVybnMgYHt9YCBmb3JcbiAqIGB1bmRlZmluZWRgLCBlbXB0eSBzdHJpbmdzLCBhbmQgdW5yZWNvZ25pemVkIGlucHV0cy5cbiAqXG4gKiBAZXhhbXBsZVxuICogaW1wb3J0IHsgcGdhZGRyZXNzIH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG4gKlxuICogcGdhZGRyZXNzLnBhcnNlQWRkcmVzcyhcInByb2plY3RzL2RlbW8vYnJhbmNoZXMvcHJvZHVjdGlvbi9lbmRwb2ludHMvZXAtMVwiKTtcbiAqIC8vIHsgcHJvamVjdDogXCJkZW1vXCIsIGJyYW5jaDogXCJwcm9kdWN0aW9uXCIsIGVuZHBvaW50SWQ6IFwiZXAtMVwiLCBlbmRwb2ludDogXCJwcm9qZWN0cy8uLi5cIiB9XG4gKlxuICogcGdhZGRyZXNzLnBhcnNlQWRkcmVzcyhcInBvc3RncmVzcWw6Ly9tZUBlcC0xLmRhdGFiYXNlLmF6dXJlZGF0YWJyaWNrcy5uZXQvYXBwP3NzbG1vZGU9cmVxdWlyZVwiKTtcbiAqIC8vIHsgaG9zdDogXCJlcC0xLmRhdGFiYXNlLmF6dXJlZGF0YWJyaWNrcy5uZXRcIiwgdXNlcjogXCJtZVwiLCBkYXRhYmFzZTogXCJhcHBcIiwgc3NsTW9kZTogXCJyZXF1aXJlXCIgfVxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VBZGRyZXNzKGlucHV0OiBzdHJpbmcgfCB1bmRlZmluZWQgfCBudWxsKTogUGFyc2VkQWRkcmVzcyB7XG4gIGlmICghaW5wdXQpIHJldHVybiB7fTtcbiAgY29uc3QgcyA9IGlucHV0LnRyaW0oKTtcbiAgaWYgKCFzKSByZXR1cm4ge307XG5cbiAgaWYgKFVSTF9TQ0hFTUVfUkUudGVzdChzKSkgcmV0dXJuIHBhcnNlVXJpKHMpO1xuICBpZiAocy5zdGFydHNXaXRoKFwicHJvamVjdHMvXCIpKSByZXR1cm4gcGFyc2VSZXNvdXJjZVBhdGhTZWdtZW50cyhzKTtcbiAgLy8gUmVzb3VyY2UgaWRzIG5ldmVyIGNvbnRhaW4gZG90czsgYSBkb3R0ZWQgaW5wdXQgbXVzdCBiZSBhIGhvc3RuYW1lLlxuICBpZiAoSE9TVE5BTUVfSElOVF9SRS50ZXN0KHMpICYmIHMuaW5jbHVkZXMoXCIuXCIpKSByZXR1cm4geyBob3N0OiBzIH07XG4gIGlmIChQUk9KRUNUX0lEX1JFLnRlc3QocykpIHJldHVybiB7IHByb2plY3Q6IHMgfTtcbiAgcmV0dXJuIHt9O1xufVxuXG4vKipcbiAqIFBhcnNlIGEgTGFrZWJhc2UgYHByb2plY3RzLy4uLmAgcmVzb3VyY2UgcGF0aC4gUmV0dXJucyBge31gIHdoZW4gdGhlXG4gKiBpbnB1dCBpcyBub3QgYSByZXNvdXJjZSBwYXRoIChzbyBiYXJlIGJyYW5jaCBpZHMgYXJlIG5vdCBtaXN0YWtlbiBmb3JcbiAqIHByb2plY3QgaWRzKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUmVzb3VyY2VQYXRoKGlucHV0OiBzdHJpbmcgfCB1bmRlZmluZWQgfCBudWxsKTogUGFyc2VkQWRkcmVzcyB7XG4gIGlmICghaW5wdXQpIHJldHVybiB7fTtcbiAgY29uc3QgcyA9IGlucHV0LnRyaW0oKTtcbiAgaWYgKCFzLnN0YXJ0c1dpdGgoXCJwcm9qZWN0cy9cIikpIHJldHVybiB7fTtcbiAgcmV0dXJuIHBhcnNlUmVzb3VyY2VQYXRoU2VnbWVudHMocyk7XG59XG5cbmZ1bmN0aW9uIHBhcnNlVXJpKHM6IHN0cmluZyk6IFBhcnNlZEFkZHJlc3Mge1xuICBsZXQgdXJsOiBVUkw7XG4gIHRyeSB7XG4gICAgdXJsID0gbmV3IFVSTChzKTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG4gIGNvbnN0IHJlc3VsdDogUGFyc2VkQWRkcmVzcyA9IHt9O1xuICBpZiAodXJsLmhvc3RuYW1lKSByZXN1bHQuaG9zdCA9IHVybC5ob3N0bmFtZTtcbiAgaWYgKHVybC5wb3J0KSB7XG4gICAgY29uc3QgcG9ydCA9IE51bWJlci5wYXJzZUludCh1cmwucG9ydCwgMTApO1xuICAgIGlmICghTnVtYmVyLmlzTmFOKHBvcnQpKSByZXN1bHQucG9ydCA9IHBvcnQ7XG4gIH1cbiAgaWYgKHVybC51c2VybmFtZSkge1xuICAgIHRyeSB7XG4gICAgICByZXN1bHQudXNlciA9IGRlY29kZVVSSUNvbXBvbmVudCh1cmwudXNlcm5hbWUpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmVzdWx0LnVzZXIgPSB1cmwudXNlcm5hbWU7XG4gICAgfVxuICB9XG4gIGNvbnN0IGRiID0gdXJsLnBhdGhuYW1lLnJlcGxhY2UoL15cXC8vLCBcIlwiKTtcbiAgaWYgKGRiKSByZXN1bHQuZGF0YWJhc2UgPSBkZWNvZGVVUklDb21wb25lbnQoZGIpO1xuICBjb25zdCBzc2xtb2RlUmF3ID0gdXJsLnNlYXJjaFBhcmFtcy5nZXQoXCJzc2xtb2RlXCIpID8/IHVybC5zZWFyY2hQYXJhbXMuZ2V0KFwic3NsTW9kZVwiKTtcbiAgY29uc3Qgc3NsbW9kZSA9IHNzbG1vZGVSYXc/LnRvTG93ZXJDYXNlKCk7XG4gIGlmIChpc1NzbE1vZGUoc3NsbW9kZSkpIHtcbiAgICByZXN1bHQuc3NsTW9kZSA9IHNzbG1vZGU7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuZnVuY3Rpb24gaXNTc2xNb2RlKHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiB2YWx1ZSBpcyBTc2xNb2RlIHtcbiAgcmV0dXJuIFNTTF9NT0RFUy5zb21lKChtb2RlKSA9PiBtb2RlID09PSB2YWx1ZSk7XG59XG5cbmZ1bmN0aW9uIHBhcnNlUmVzb3VyY2VQYXRoU2VnbWVudHMoczogc3RyaW5nKTogUGFyc2VkQWRkcmVzcyB7XG4gIGNvbnN0IHBhcnRzID0gcy5zcGxpdChcIi9cIik7XG4gIGlmIChwYXJ0c1swXSAhPT0gXCJwcm9qZWN0c1wiIHx8IHBhcnRzLmxlbmd0aCA8IDIpIHtcbiAgICByZXR1cm4ge307XG4gIH1cblxuICBjb25zdCBwcm9qZWN0ID0gcGFydHNbMV07XG4gIGlmICghcHJvamVjdCkge1xuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIGlmIChwYXJ0cy5sZW5ndGggPT09IDIpIHtcbiAgICByZXR1cm4geyBwcm9qZWN0IH07XG4gIH1cblxuICBpZiAocGFydHMubGVuZ3RoID09PSA0ICYmIHBhcnRzWzJdID09PSBcImJyYW5jaGVzXCIgJiYgcGFydHNbM10pIHtcbiAgICByZXR1cm4geyBwcm9qZWN0LCBicmFuY2g6IHBhcnRzWzNdIH07XG4gIH1cblxuICBpZiAoXG4gICAgcGFydHMubGVuZ3RoID09PSA2ICYmXG4gICAgcGFydHNbMl0gPT09IFwiYnJhbmNoZXNcIiAmJlxuICAgIHBhcnRzWzRdID09PSBcImVuZHBvaW50c1wiICYmXG4gICAgcGFydHNbM10gJiZcbiAgICBwYXJ0c1s1XVxuICApIHtcbiAgICByZXR1cm4ge1xuICAgICAgcHJvamVjdCxcbiAgICAgIGJyYW5jaDogcGFydHNbM10sXG4gICAgICBlbmRwb2ludElkOiBwYXJ0c1s1XSxcbiAgICAgIGVuZHBvaW50OiBzLFxuICAgIH07XG4gIH1cblxuICBpZiAoXG4gICAgcGFydHMubGVuZ3RoID09PSA2ICYmXG4gICAgcGFydHNbMl0gPT09IFwiYnJhbmNoZXNcIiAmJlxuICAgIHBhcnRzWzRdID09PSBcImRhdGFiYXNlc1wiICYmXG4gICAgcGFydHNbM10gJiZcbiAgICBwYXJ0c1s1XVxuICApIHtcbiAgICByZXR1cm4ge1xuICAgICAgcHJvamVjdCxcbiAgICAgIGJyYW5jaDogcGFydHNbM10sXG4gICAgICBkYXRhYmFzZVJlc291cmNlSWQ6IHBhcnRzWzVdLFxuICAgIH07XG4gIH1cblxuICByZXR1cm4ge307XG59XG4iXX0=
@@ -0,0 +1,90 @@
1
+ /**
2
+ * AppKit plugin lookup: typed access to sibling plugins registered on the
3
+ * AppKit plugin context (`this.context` on any class that extends `Plugin`).
4
+ *
5
+ * Why these live here instead of in `@databricks/appkit`: AppKit exposes
6
+ * `this.context.getPlugins()`, which returns `ReadonlyMap<string, BasePlugin>`,
7
+ * but provides no typed lookup helper. Every caller ends up writing the same
8
+ * `as InstanceType<ReturnType<typeof someFactory>["plugin"]>` cast. These
9
+ * wrappers absorb that boilerplate.
10
+ *
11
+ * API shape: pass the plugin's factory (`lakebase`, `serving`, `genie`, or any
12
+ * `toPlugin(...)` result) directly. TypeScript infers both the instance type
13
+ * (so `.exports()` resolves) and the registered name (so the runtime lookup
14
+ * works) from that single value. No `<T>` annotation or string literal needed
15
+ * at the call site.
16
+ *
17
+ * Generic AppKit runtime (execution context, `WorkspaceClientLike`) lives in
18
+ * `./appkit`; this module is plugin-lookup only.
19
+ *
20
+ * @module
21
+ */
22
+ import { type NameLike } from "@dbx-tools/shared-core";
23
+ /**
24
+ * Minimal structural shape of `this.context`. We mirror only the method we
25
+ * touch instead of depending on AppKit's `PluginContext` type, which is not
26
+ * part of the package's `exports` map and therefore cannot be imported. Any
27
+ * compatible object (real `PluginContext`, mocks, tests) satisfies this shape.
28
+ */
29
+ export interface PluginContextLike {
30
+ getPlugins(): ReadonlyMap<string, unknown>;
31
+ }
32
+ type PluginData = {
33
+ plugin: abstract new (...args: never[]) => unknown;
34
+ name: string;
35
+ };
36
+ /**
37
+ * Structural shape of an AppKit plugin factory (the result of
38
+ * `toPlugin(SomePluginClass)`). Calling it returns a `PluginData` tuple whose
39
+ * `plugin` field is the *class constructor* and whose `name` field carries the
40
+ * registered plugin name as a literal string.
41
+ *
42
+ * Defined structurally so we don't pull `@databricks/appkit` into this as a
43
+ * type dependency for the bound. Any function returning the same shape (e.g.
44
+ * `lakebase`, `serving`, `genie`, or a user-defined `toPlugin(MyPlugin)`)
45
+ * satisfies it.
46
+ */
47
+ type PluginDataFactory = (...args: never[]) => PluginData;
48
+ /**
49
+ * Maps a plugin factory back to the *instance* type of its plugin class.
50
+ * Mirrors the inline pattern users would otherwise write:
51
+ * `InstanceType<ReturnType<typeof factory>["plugin"]>`.
52
+ */
53
+ type PluginInstanceOf<F extends PluginDataFactory> = InstanceType<ReturnType<F>["plugin"]>;
54
+ /**
55
+ * Returns the static `{ plugin, name }` descriptor for an AppKit plugin
56
+ * factory, caching per factory so repeated lookups do not allocate.
57
+ */
58
+ export declare function data<F extends PluginDataFactory, D extends ReturnType<F>>(factory: F): D;
59
+ /**
60
+ * Look up a sibling plugin instance from the AppKit plugin context, keyed off
61
+ * the factory's registered name and typed via its plugin class.
62
+ *
63
+ * Returns `undefined` when the context is missing or the plugin is not
64
+ * registered. For required siblings prefer {@link require}.
65
+ *
66
+ * @example
67
+ * import { lakebase } from "@databricks/appkit";
68
+ * import { plugin } from "@dbx-tools/appkit";
69
+ *
70
+ * const lake = plugin.instance(this.context, lakebase);
71
+ * // ^^ inferred as LakebasePlugin | undefined
72
+ * lake?.exports().pool;
73
+ */
74
+ export declare function instance<F extends PluginDataFactory>(ctx: PluginContextLike | undefined, factory: F): PluginInstanceOf<F> | undefined;
75
+ /**
76
+ * Like {@link instance} but throws when the plugin is not registered. Use for
77
+ * siblings whose absence is a wiring bug rather than a runtime condition (e.g.
78
+ * requiring `lakebase` when the caller has `storage` / `memory` enabled).
79
+ *
80
+ * `caller` is prepended to the error message so cross-plugin failures are easy
81
+ * to attribute in logs.
82
+ *
83
+ * @example
84
+ * import { lakebase } from "@databricks/appkit";
85
+ * import { plugin } from "@dbx-tools/appkit";
86
+ *
87
+ * const pool = plugin.require(this.context, lakebase, "mastra").exports().pool;
88
+ */
89
+ export declare function require<F extends PluginDataFactory>(ctx: PluginContextLike | undefined, factory: F, caller?: NameLike | string): PluginInstanceOf<F>;
90
+ export {};
@@ -0,0 +1,97 @@
1
+ /**
2
+ * AppKit plugin lookup: typed access to sibling plugins registered on the
3
+ * AppKit plugin context (`this.context` on any class that extends `Plugin`).
4
+ *
5
+ * Why these live here instead of in `@databricks/appkit`: AppKit exposes
6
+ * `this.context.getPlugins()`, which returns `ReadonlyMap<string, BasePlugin>`,
7
+ * but provides no typed lookup helper. Every caller ends up writing the same
8
+ * `as InstanceType<ReturnType<typeof someFactory>["plugin"]>` cast. These
9
+ * wrappers absorb that boilerplate.
10
+ *
11
+ * API shape: pass the plugin's factory (`lakebase`, `serving`, `genie`, or any
12
+ * `toPlugin(...)` result) directly. TypeScript infers both the instance type
13
+ * (so `.exports()` resolves) and the registered name (so the runtime lookup
14
+ * works) from that single value. No `<T>` annotation or string literal needed
15
+ * at the call site.
16
+ *
17
+ * Generic AppKit runtime (execution context, `WorkspaceClientLike`) lives in
18
+ * `./appkit`; this module is plugin-lookup only.
19
+ *
20
+ * @module
21
+ */
22
+ import { ConfigurationError } from "@databricks/appkit";
23
+ import { log } from "@dbx-tools/shared-core";
24
+ const logger = log.logger("plugin");
25
+ /**
26
+ * Registry name returned by `factory().name`, keyed by the factory function.
27
+ * Typical AppKit factories return stable metadata; caching avoids invoking
28
+ * `factory()` on every sibling lookup (which would allocate a fresh descriptor
29
+ * tuple each time).
30
+ */
31
+ const dataCache = new WeakMap();
32
+ /**
33
+ * Returns the static `{ plugin, name }` descriptor for an AppKit plugin
34
+ * factory, caching per factory so repeated lookups do not allocate.
35
+ */
36
+ export function data(factory) {
37
+ const cached = dataCache.get(factory);
38
+ // The cache is keyed by the erased `PluginDataFactory` bound, so `WeakMap`
39
+ // hands back the widened `PluginData`; only the caller's `F` knows the exact
40
+ // descriptor type.
41
+ if (cached !== undefined) {
42
+ return cached;
43
+ }
44
+ const result = factory();
45
+ dataCache.set(factory, result);
46
+ return result;
47
+ }
48
+ /**
49
+ * Look up a sibling plugin instance from the AppKit plugin context, keyed off
50
+ * the factory's registered name and typed via its plugin class.
51
+ *
52
+ * Returns `undefined` when the context is missing or the plugin is not
53
+ * registered. For required siblings prefer {@link require}.
54
+ *
55
+ * @example
56
+ * import { lakebase } from "@databricks/appkit";
57
+ * import { plugin } from "@dbx-tools/appkit";
58
+ *
59
+ * const lake = plugin.instance(this.context, lakebase);
60
+ * // ^^ inferred as LakebasePlugin | undefined
61
+ * lake?.exports().pool;
62
+ */
63
+ export function instance(ctx, factory) {
64
+ if (!ctx)
65
+ return undefined;
66
+ const name = data(factory).name;
67
+ // AppKit's registry is a `Map<string, unknown>`, so the instance type is only
68
+ // recoverable from the factory the caller passed.
69
+ return ctx.getPlugins().get(name);
70
+ }
71
+ /**
72
+ * Like {@link instance} but throws when the plugin is not registered. Use for
73
+ * siblings whose absence is a wiring bug rather than a runtime condition (e.g.
74
+ * requiring `lakebase` when the caller has `storage` / `memory` enabled).
75
+ *
76
+ * `caller` is prepended to the error message so cross-plugin failures are easy
77
+ * to attribute in logs.
78
+ *
79
+ * @example
80
+ * import { lakebase } from "@databricks/appkit";
81
+ * import { plugin } from "@dbx-tools/appkit";
82
+ *
83
+ * const pool = plugin.require(this.context, lakebase, "mastra").exports().pool;
84
+ */
85
+ export function require(ctx, factory, caller) {
86
+ const found = instance(ctx, factory);
87
+ if (found)
88
+ return found;
89
+ const prefix = typeof caller === "string" ? `${caller}: ` : caller?.name ? `${caller.name}: ` : "";
90
+ const registeredName = data(factory).name;
91
+ logger.debug("required plugin not registered", {
92
+ plugin: registeredName,
93
+ registered: [...(ctx?.getPlugins().keys() ?? [])],
94
+ });
95
+ throw ConfigurationError.resourceNotFound(`${prefix}plugin '${registeredName}'`, `Add ${registeredName}() to the plugins passed to createApp.`);
96
+ }
97
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFFSCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsR0FBRyxFQUFpQixNQUFNLHdCQUF3QixDQUFDO0FBRTVELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7QUFxQ3BDOzs7OztHQUtHO0FBQ0gsTUFBTSxTQUFTLEdBQUcsSUFBSSxPQUFPLEVBQWlDLENBQUM7QUFFL0Q7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLElBQUksQ0FBdUQsT0FBVTtJQUNuRixNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3RDLDJFQUEyRTtJQUMzRSw2RUFBNkU7SUFDN0UsbUJBQW1CO0lBQ25CLElBQUksTUFBTSxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3pCLE9BQU8sTUFBVyxDQUFDO0lBQ3JCLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxPQUFPLEVBQUUsQ0FBQztJQUN6QixTQUFTLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUMvQixPQUFPLE1BQVcsQ0FBQztBQUNyQixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLFVBQVUsUUFBUSxDQUN0QixHQUFrQyxFQUNsQyxPQUFVO0lBRVYsSUFBSSxDQUFDLEdBQUc7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUMzQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQ2hDLDhFQUE4RTtJQUM5RSxrREFBa0Q7SUFDbEQsT0FBTyxHQUFHLENBQUMsVUFBVSxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBb0MsQ0FBQztBQUN2RSxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sVUFBVSxPQUFPLENBQ3JCLEdBQWtDLEVBQ2xDLE9BQVUsRUFDVixNQUEwQjtJQUUxQixNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JDLElBQUksS0FBSztRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3hCLE1BQU0sTUFBTSxHQUNWLE9BQU8sTUFBTSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLElBQUksQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUN0RixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQzFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsZ0NBQWdDLEVBQUU7UUFDN0MsTUFBTSxFQUFFLGNBQWM7UUFDdEIsVUFBVSxFQUFFLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxVQUFVLEVBQUUsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztLQUNsRCxDQUFDLENBQUM7SUFDSCxNQUFNLGtCQUFrQixDQUFDLGdCQUFnQixDQUN2QyxHQUFHLE1BQU0sV0FBVyxjQUFjLEdBQUcsRUFDckMsT0FBTyxjQUFjLHdDQUF3QyxDQUM5RCxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQXBwS2l0IHBsdWdpbiBsb29rdXA6IHR5cGVkIGFjY2VzcyB0byBzaWJsaW5nIHBsdWdpbnMgcmVnaXN0ZXJlZCBvbiB0aGVcbiAqIEFwcEtpdCBwbHVnaW4gY29udGV4dCAoYHRoaXMuY29udGV4dGAgb24gYW55IGNsYXNzIHRoYXQgZXh0ZW5kcyBgUGx1Z2luYCkuXG4gKlxuICogV2h5IHRoZXNlIGxpdmUgaGVyZSBpbnN0ZWFkIG9mIGluIGBAZGF0YWJyaWNrcy9hcHBraXRgOiBBcHBLaXQgZXhwb3Nlc1xuICogYHRoaXMuY29udGV4dC5nZXRQbHVnaW5zKClgLCB3aGljaCByZXR1cm5zIGBSZWFkb25seU1hcDxzdHJpbmcsIEJhc2VQbHVnaW4+YCxcbiAqIGJ1dCBwcm92aWRlcyBubyB0eXBlZCBsb29rdXAgaGVscGVyLiBFdmVyeSBjYWxsZXIgZW5kcyB1cCB3cml0aW5nIHRoZSBzYW1lXG4gKiBgYXMgSW5zdGFuY2VUeXBlPFJldHVyblR5cGU8dHlwZW9mIHNvbWVGYWN0b3J5PltcInBsdWdpblwiXT5gIGNhc3QuIFRoZXNlXG4gKiB3cmFwcGVycyBhYnNvcmIgdGhhdCBib2lsZXJwbGF0ZS5cbiAqXG4gKiBBUEkgc2hhcGU6IHBhc3MgdGhlIHBsdWdpbidzIGZhY3RvcnkgKGBsYWtlYmFzZWAsIGBzZXJ2aW5nYCwgYGdlbmllYCwgb3IgYW55XG4gKiBgdG9QbHVnaW4oLi4uKWAgcmVzdWx0KSBkaXJlY3RseS4gVHlwZVNjcmlwdCBpbmZlcnMgYm90aCB0aGUgaW5zdGFuY2UgdHlwZVxuICogKHNvIGAuZXhwb3J0cygpYCByZXNvbHZlcykgYW5kIHRoZSByZWdpc3RlcmVkIG5hbWUgKHNvIHRoZSBydW50aW1lIGxvb2t1cFxuICogd29ya3MpIGZyb20gdGhhdCBzaW5nbGUgdmFsdWUuIE5vIGA8VD5gIGFubm90YXRpb24gb3Igc3RyaW5nIGxpdGVyYWwgbmVlZGVkXG4gKiBhdCB0aGUgY2FsbCBzaXRlLlxuICpcbiAqIEdlbmVyaWMgQXBwS2l0IHJ1bnRpbWUgKGV4ZWN1dGlvbiBjb250ZXh0LCBgV29ya3NwYWNlQ2xpZW50TGlrZWApIGxpdmVzIGluXG4gKiBgLi9hcHBraXRgOyB0aGlzIG1vZHVsZSBpcyBwbHVnaW4tbG9va3VwIG9ubHkuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IENvbmZpZ3VyYXRpb25FcnJvciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IGxvZywgdHlwZSBOYW1lTGlrZSB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJwbHVnaW5cIik7XG5cbi8qKlxuICogTWluaW1hbCBzdHJ1Y3R1cmFsIHNoYXBlIG9mIGB0aGlzLmNvbnRleHRgLiBXZSBtaXJyb3Igb25seSB0aGUgbWV0aG9kIHdlXG4gKiB0b3VjaCBpbnN0ZWFkIG9mIGRlcGVuZGluZyBvbiBBcHBLaXQncyBgUGx1Z2luQ29udGV4dGAgdHlwZSwgd2hpY2ggaXMgbm90XG4gKiBwYXJ0IG9mIHRoZSBwYWNrYWdlJ3MgYGV4cG9ydHNgIG1hcCBhbmQgdGhlcmVmb3JlIGNhbm5vdCBiZSBpbXBvcnRlZC4gQW55XG4gKiBjb21wYXRpYmxlIG9iamVjdCAocmVhbCBgUGx1Z2luQ29udGV4dGAsIG1vY2tzLCB0ZXN0cykgc2F0aXNmaWVzIHRoaXMgc2hhcGUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGx1Z2luQ29udGV4dExpa2Uge1xuICBnZXRQbHVnaW5zKCk6IFJlYWRvbmx5TWFwPHN0cmluZywgdW5rbm93bj47XG59XG5cbnR5cGUgUGx1Z2luRGF0YSA9IHtcbiAgcGx1Z2luOiBhYnN0cmFjdCBuZXcgKC4uLmFyZ3M6IG5ldmVyW10pID0+IHVua25vd247XG4gIG5hbWU6IHN0cmluZztcbn07XG5cbi8qKlxuICogU3RydWN0dXJhbCBzaGFwZSBvZiBhbiBBcHBLaXQgcGx1Z2luIGZhY3RvcnkgKHRoZSByZXN1bHQgb2ZcbiAqIGB0b1BsdWdpbihTb21lUGx1Z2luQ2xhc3MpYCkuIENhbGxpbmcgaXQgcmV0dXJucyBhIGBQbHVnaW5EYXRhYCB0dXBsZSB3aG9zZVxuICogYHBsdWdpbmAgZmllbGQgaXMgdGhlICpjbGFzcyBjb25zdHJ1Y3RvciogYW5kIHdob3NlIGBuYW1lYCBmaWVsZCBjYXJyaWVzIHRoZVxuICogcmVnaXN0ZXJlZCBwbHVnaW4gbmFtZSBhcyBhIGxpdGVyYWwgc3RyaW5nLlxuICpcbiAqIERlZmluZWQgc3RydWN0dXJhbGx5IHNvIHdlIGRvbid0IHB1bGwgYEBkYXRhYnJpY2tzL2FwcGtpdGAgaW50byB0aGlzIGFzIGFcbiAqIHR5cGUgZGVwZW5kZW5jeSBmb3IgdGhlIGJvdW5kLiBBbnkgZnVuY3Rpb24gcmV0dXJuaW5nIHRoZSBzYW1lIHNoYXBlIChlLmcuXG4gKiBgbGFrZWJhc2VgLCBgc2VydmluZ2AsIGBnZW5pZWAsIG9yIGEgdXNlci1kZWZpbmVkIGB0b1BsdWdpbihNeVBsdWdpbilgKVxuICogc2F0aXNmaWVzIGl0LlxuICovXG50eXBlIFBsdWdpbkRhdGFGYWN0b3J5ID0gKC4uLmFyZ3M6IG5ldmVyW10pID0+IFBsdWdpbkRhdGE7XG5cbi8qKlxuICogTWFwcyBhIHBsdWdpbiBmYWN0b3J5IGJhY2sgdG8gdGhlICppbnN0YW5jZSogdHlwZSBvZiBpdHMgcGx1Z2luIGNsYXNzLlxuICogTWlycm9ycyB0aGUgaW5saW5lIHBhdHRlcm4gdXNlcnMgd291bGQgb3RoZXJ3aXNlIHdyaXRlOlxuICogYEluc3RhbmNlVHlwZTxSZXR1cm5UeXBlPHR5cGVvZiBmYWN0b3J5PltcInBsdWdpblwiXT5gLlxuICovXG50eXBlIFBsdWdpbkluc3RhbmNlT2Y8RiBleHRlbmRzIFBsdWdpbkRhdGFGYWN0b3J5PiA9IEluc3RhbmNlVHlwZTxSZXR1cm5UeXBlPEY+W1wicGx1Z2luXCJdPjtcblxuLyoqXG4gKiBSZWdpc3RyeSBuYW1lIHJldHVybmVkIGJ5IGBmYWN0b3J5KCkubmFtZWAsIGtleWVkIGJ5IHRoZSBmYWN0b3J5IGZ1bmN0aW9uLlxuICogVHlwaWNhbCBBcHBLaXQgZmFjdG9yaWVzIHJldHVybiBzdGFibGUgbWV0YWRhdGE7IGNhY2hpbmcgYXZvaWRzIGludm9raW5nXG4gKiBgZmFjdG9yeSgpYCBvbiBldmVyeSBzaWJsaW5nIGxvb2t1cCAod2hpY2ggd291bGQgYWxsb2NhdGUgYSBmcmVzaCBkZXNjcmlwdG9yXG4gKiB0dXBsZSBlYWNoIHRpbWUpLlxuICovXG5jb25zdCBkYXRhQ2FjaGUgPSBuZXcgV2Vha01hcDxQbHVnaW5EYXRhRmFjdG9yeSwgUGx1Z2luRGF0YT4oKTtcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBzdGF0aWMgYHsgcGx1Z2luLCBuYW1lIH1gIGRlc2NyaXB0b3IgZm9yIGFuIEFwcEtpdCBwbHVnaW5cbiAqIGZhY3RvcnksIGNhY2hpbmcgcGVyIGZhY3Rvcnkgc28gcmVwZWF0ZWQgbG9va3VwcyBkbyBub3QgYWxsb2NhdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkYXRhPEYgZXh0ZW5kcyBQbHVnaW5EYXRhRmFjdG9yeSwgRCBleHRlbmRzIFJldHVyblR5cGU8Rj4+KGZhY3Rvcnk6IEYpOiBEIHtcbiAgY29uc3QgY2FjaGVkID0gZGF0YUNhY2hlLmdldChmYWN0b3J5KTtcbiAgLy8gVGhlIGNhY2hlIGlzIGtleWVkIGJ5IHRoZSBlcmFzZWQgYFBsdWdpbkRhdGFGYWN0b3J5YCBib3VuZCwgc28gYFdlYWtNYXBgXG4gIC8vIGhhbmRzIGJhY2sgdGhlIHdpZGVuZWQgYFBsdWdpbkRhdGFgOyBvbmx5IHRoZSBjYWxsZXIncyBgRmAga25vd3MgdGhlIGV4YWN0XG4gIC8vIGRlc2NyaXB0b3IgdHlwZS5cbiAgaWYgKGNhY2hlZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIGNhY2hlZCBhcyBEO1xuICB9XG4gIGNvbnN0IHJlc3VsdCA9IGZhY3RvcnkoKTtcbiAgZGF0YUNhY2hlLnNldChmYWN0b3J5LCByZXN1bHQpO1xuICByZXR1cm4gcmVzdWx0IGFzIEQ7XG59XG5cbi8qKlxuICogTG9vayB1cCBhIHNpYmxpbmcgcGx1Z2luIGluc3RhbmNlIGZyb20gdGhlIEFwcEtpdCBwbHVnaW4gY29udGV4dCwga2V5ZWQgb2ZmXG4gKiB0aGUgZmFjdG9yeSdzIHJlZ2lzdGVyZWQgbmFtZSBhbmQgdHlwZWQgdmlhIGl0cyBwbHVnaW4gY2xhc3MuXG4gKlxuICogUmV0dXJucyBgdW5kZWZpbmVkYCB3aGVuIHRoZSBjb250ZXh0IGlzIG1pc3Npbmcgb3IgdGhlIHBsdWdpbiBpcyBub3RcbiAqIHJlZ2lzdGVyZWQuIEZvciByZXF1aXJlZCBzaWJsaW5ncyBwcmVmZXIge0BsaW5rIHJlcXVpcmV9LlxuICpcbiAqIEBleGFtcGxlXG4gKiBpbXBvcnQgeyBsYWtlYmFzZSB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IHBsdWdpbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdFwiO1xuICpcbiAqIGNvbnN0IGxha2UgPSBwbHVnaW4uaW5zdGFuY2UodGhpcy5jb250ZXh0LCBsYWtlYmFzZSk7XG4gKiAvLyAgICBeXiBpbmZlcnJlZCBhcyBMYWtlYmFzZVBsdWdpbiB8IHVuZGVmaW5lZFxuICogbGFrZT8uZXhwb3J0cygpLnBvb2w7XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnN0YW5jZTxGIGV4dGVuZHMgUGx1Z2luRGF0YUZhY3Rvcnk+KFxuICBjdHg6IFBsdWdpbkNvbnRleHRMaWtlIHwgdW5kZWZpbmVkLFxuICBmYWN0b3J5OiBGLFxuKTogUGx1Z2luSW5zdGFuY2VPZjxGPiB8IHVuZGVmaW5lZCB7XG4gIGlmICghY3R4KSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCBuYW1lID0gZGF0YShmYWN0b3J5KS5uYW1lO1xuICAvLyBBcHBLaXQncyByZWdpc3RyeSBpcyBhIGBNYXA8c3RyaW5nLCB1bmtub3duPmAsIHNvIHRoZSBpbnN0YW5jZSB0eXBlIGlzIG9ubHlcbiAgLy8gcmVjb3ZlcmFibGUgZnJvbSB0aGUgZmFjdG9yeSB0aGUgY2FsbGVyIHBhc3NlZC5cbiAgcmV0dXJuIGN0eC5nZXRQbHVnaW5zKCkuZ2V0KG5hbWUpIGFzIFBsdWdpbkluc3RhbmNlT2Y8Rj4gfCB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogTGlrZSB7QGxpbmsgaW5zdGFuY2V9IGJ1dCB0aHJvd3Mgd2hlbiB0aGUgcGx1Z2luIGlzIG5vdCByZWdpc3RlcmVkLiBVc2UgZm9yXG4gKiBzaWJsaW5ncyB3aG9zZSBhYnNlbmNlIGlzIGEgd2lyaW5nIGJ1ZyByYXRoZXIgdGhhbiBhIHJ1bnRpbWUgY29uZGl0aW9uIChlLmcuXG4gKiByZXF1aXJpbmcgYGxha2ViYXNlYCB3aGVuIHRoZSBjYWxsZXIgaGFzIGBzdG9yYWdlYCAvIGBtZW1vcnlgIGVuYWJsZWQpLlxuICpcbiAqIGBjYWxsZXJgIGlzIHByZXBlbmRlZCB0byB0aGUgZXJyb3IgbWVzc2FnZSBzbyBjcm9zcy1wbHVnaW4gZmFpbHVyZXMgYXJlIGVhc3lcbiAqIHRvIGF0dHJpYnV0ZSBpbiBsb2dzLlxuICpcbiAqIEBleGFtcGxlXG4gKiBpbXBvcnQgeyBsYWtlYmFzZSB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IHBsdWdpbiB9IGZyb20gXCJAZGJ4LXRvb2xzL2FwcGtpdFwiO1xuICpcbiAqIGNvbnN0IHBvb2wgPSBwbHVnaW4ucmVxdWlyZSh0aGlzLmNvbnRleHQsIGxha2ViYXNlLCBcIm1hc3RyYVwiKS5leHBvcnRzKCkucG9vbDtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlcXVpcmU8RiBleHRlbmRzIFBsdWdpbkRhdGFGYWN0b3J5PihcbiAgY3R4OiBQbHVnaW5Db250ZXh0TGlrZSB8IHVuZGVmaW5lZCxcbiAgZmFjdG9yeTogRixcbiAgY2FsbGVyPzogTmFtZUxpa2UgfCBzdHJpbmcsXG4pOiBQbHVnaW5JbnN0YW5jZU9mPEY+IHtcbiAgY29uc3QgZm91bmQgPSBpbnN0YW5jZShjdHgsIGZhY3RvcnkpO1xuICBpZiAoZm91bmQpIHJldHVybiBmb3VuZDtcbiAgY29uc3QgcHJlZml4ID1cbiAgICB0eXBlb2YgY2FsbGVyID09PSBcInN0cmluZ1wiID8gYCR7Y2FsbGVyfTogYCA6IGNhbGxlcj8ubmFtZSA/IGAke2NhbGxlci5uYW1lfTogYCA6IFwiXCI7XG4gIGNvbnN0IHJlZ2lzdGVyZWROYW1lID0gZGF0YShmYWN0b3J5KS5uYW1lO1xuICBsb2dnZXIuZGVidWcoXCJyZXF1aXJlZCBwbHVnaW4gbm90IHJlZ2lzdGVyZWRcIiwge1xuICAgIHBsdWdpbjogcmVnaXN0ZXJlZE5hbWUsXG4gICAgcmVnaXN0ZXJlZDogWy4uLihjdHg/LmdldFBsdWdpbnMoKS5rZXlzKCkgPz8gW10pXSxcbiAgfSk7XG4gIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5yZXNvdXJjZU5vdEZvdW5kKFxuICAgIGAke3ByZWZpeH1wbHVnaW4gJyR7cmVnaXN0ZXJlZE5hbWV9J2AsXG4gICAgYEFkZCAke3JlZ2lzdGVyZWROYW1lfSgpIHRvIHRoZSBwbHVnaW5zIHBhc3NlZCB0byBjcmVhdGVBcHAuYCxcbiAgKTtcbn1cbiJdfQ==
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Lakebase cache-schema grant fix-up.
3
+ *
4
+ * AppKit's persistent cache (`CacheManager` -> `PersistentStorage`) uses a
5
+ * schema `appkit` and a table `appkit.appkit_cache_entries`. When the connecting
6
+ * Databricks identity lacks privileges on that schema, the cache migration
7
+ * throws, and because AppKit is typically run with `cache.strictPersistence:
8
+ * true`, the cache is silently switched to a disabled in-memory stub - so every
9
+ * persistent read (chart long-poll, history, etc.) misses and 404s.
10
+ *
11
+ * This grants the connecting role full rights on the cache schema, but only when
12
+ * the schema ALREADY EXISTS - it never creates the schema itself. Run from a
13
+ * LOCAL identity that owns the schema (or holds grant option on it); it is
14
+ * skipped inside a Databricks App, where the app SP cannot grant and its
15
+ * Postgres role does not exist until its first connection.
16
+ *
17
+ * Must run AFTER {@link applyLakebaseToEnv} has written the resolved connection
18
+ * to `process.env` (so `createLakebasePool` picks up host / database / endpoint)
19
+ * and BEFORE `createApp` initializes the cache.
20
+ *
21
+ * @module
22
+ */
23
+ import { log } from "@dbx-tools/shared-core";
24
+ /**
25
+ * Idempotent grants that make the (already-existing) AppKit cache schema fully
26
+ * usable by `role`. The `ALTER DEFAULT PRIVILEGES` lines cover the cache table
27
+ * whenever the schema owner creates it later.
28
+ *
29
+ * Throws a {@link ValidationError} when `role` is not a plausible Postgres role
30
+ * name, since the value lands in an identifier position.
31
+ */
32
+ export declare function cacheGrantStatements(role: string): readonly string[];
33
+ /**
34
+ * Grant `role` rights on the AppKit cache schema, but only when that schema
35
+ * already exists.
36
+ *
37
+ * No-ops inside a Databricks App env, when `role` is undefined, or when the
38
+ * schema is absent. Best-effort otherwise: any failure (e.g. the local identity
39
+ * doesn't own the schema) is logged and swallowed so it never blocks startup -
40
+ * a disabled cache is degraded, not fatal.
41
+ *
42
+ * @param role - Postgres role to grant to and connect as (the resolved
43
+ * workspace-client identity); skips when undefined.
44
+ * @param logger - Logger to report progress on. Defaults to this module's.
45
+ *
46
+ * @example
47
+ * import { provision } from "@dbx-tools/appkit";
48
+ *
49
+ * await provision.provisionCacheSchema("app-service-principal@databricks.com");
50
+ */
51
+ export declare function provisionCacheSchema(role: string | undefined, logger?: log.Logger): Promise<void>;