@databricks/appkit-ui 0.60.0 → 0.61.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.
- package/NOTICE.md +12 -10
- package/dist/cli/commands/doctor/bundle.js +3 -11
- package/dist/cli/commands/doctor/bundle.js.map +1 -1
- package/dist/cli/commands/doctor/databricks-client.js +18 -22
- package/dist/cli/commands/doctor/databricks-client.js.map +1 -1
- package/dist/cli/commands/doctor/resolve-targets.js +4 -6
- package/dist/cli/commands/doctor/resolve-targets.js.map +1 -1
- package/dist/cli/commands/registry/add.js +410 -0
- package/dist/cli/commands/registry/add.js.map +1 -0
- package/dist/cli/commands/registry/client.js +94 -0
- package/dist/cli/commands/registry/client.js.map +1 -0
- package/dist/cli/commands/registry/config-plan.js +110 -0
- package/dist/cli/commands/registry/config-plan.js.map +1 -0
- package/dist/cli/commands/registry/config-writer.js +176 -0
- package/dist/cli/commands/registry/config-writer.js.map +1 -0
- package/dist/cli/commands/registry/constants.js +62 -0
- package/dist/cli/commands/registry/constants.js.map +1 -0
- package/dist/cli/commands/registry/env-reconcile.js +116 -0
- package/dist/cli/commands/registry/env-reconcile.js.map +1 -0
- package/dist/cli/commands/registry/env-writer.js +198 -0
- package/dist/cli/commands/registry/env-writer.js.map +1 -0
- package/dist/cli/commands/registry/index.js +25 -0
- package/dist/cli/commands/registry/index.js.map +1 -0
- package/dist/cli/commands/registry/info.js +45 -0
- package/dist/cli/commands/registry/info.js.map +1 -0
- package/dist/cli/commands/registry/list.js +130 -0
- package/dist/cli/commands/registry/list.js.map +1 -0
- package/dist/cli/commands/registry/requirements.js +92 -0
- package/dist/cli/commands/registry/requirements.js.map +1 -0
- package/dist/cli/commands/registry/server-register.js +124 -0
- package/dist/cli/commands/registry/server-register.js.map +1 -0
- package/dist/cli/commands/registry/workspace-picker.js +353 -0
- package/dist/cli/commands/registry/workspace-picker.js.map +1 -0
- package/dist/cli/deploy-config.js +36 -0
- package/dist/cli/deploy-config.js.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/schemas/manifest.js +4 -4
- package/dist/schemas/manifest.js.map +1 -1
- package/dist/shared/src/index.d.ts +2 -1
- package/dist/shared/src/workspace-client/errors.d.ts +1 -0
- package/dist/shared/src/workspace-client/factory.d.ts +2 -0
- package/dist/shared/src/workspace-client/index.d.ts +4 -0
- package/dist/shared/src/workspace-client/legacy.d.ts +3 -0
- package/dist/shared/src/workspace-client/types.d.ts +2 -0
- package/dist/workspace-client/client.js +57 -0
- package/dist/workspace-client/client.js.map +1 -0
- package/dist/workspace-client/errors.d.ts +2 -0
- package/dist/workspace-client/errors.js +3 -0
- package/dist/workspace-client/factory.d.ts +19 -0
- package/dist/workspace-client/factory.d.ts.map +1 -0
- package/dist/workspace-client/factory.js +24 -0
- package/dist/workspace-client/factory.js.map +1 -0
- package/dist/workspace-client/index.d.ts +5 -0
- package/dist/workspace-client/index.js +5 -0
- package/dist/workspace-client/legacy.d.ts +34 -0
- package/dist/workspace-client/legacy.d.ts.map +1 -0
- package/dist/workspace-client/legacy.js +23 -0
- package/dist/workspace-client/legacy.js.map +1 -0
- package/dist/workspace-client/types.d.ts +49 -0
- package/dist/workspace-client/types.d.ts.map +1 -0
- package/docs/api/appkit/Function.createWorkspaceClient.md +4 -4
- package/docs/api/appkit/Interface.WorkspaceClientOptions.md +11 -0
- package/package.json +4 -2
- package/sbom.cdx.json +1 -1
package/NOTICE.md
CHANGED
|
@@ -9,19 +9,19 @@ This Software contains code from the following open source projects:
|
|
|
9
9
|
| [@ast-grep/napi](https://www.npmjs.com/package/@ast-grep/napi) | 0.37.0 | MIT | https://ast-grep.github.io |
|
|
10
10
|
| [@clack/prompts](https://www.npmjs.com/package/@clack/prompts) | 1.0.1 | MIT | https://github.com/bombshell-dev/clack/tree/main/packages/prompts#readme |
|
|
11
11
|
| [@opentelemetry/api](https://www.npmjs.com/package/@opentelemetry/api) | 1.9.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/api |
|
|
12
|
-
| [@opentelemetry/api-logs](https://www.npmjs.com/package/@opentelemetry/api-logs) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/api-logs |
|
|
12
|
+
| [@opentelemetry/api-logs](https://www.npmjs.com/package/@opentelemetry/api-logs) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/api-logs |
|
|
13
13
|
| [@opentelemetry/auto-instrumentations-node](https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-node) | 0.77.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/auto-instrumentations-node#readme |
|
|
14
|
-
| [@opentelemetry/exporter-logs-otlp-proto](https://www.npmjs.com/package/@opentelemetry/exporter-logs-otlp-proto) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-logs-otlp-proto |
|
|
15
|
-
| [@opentelemetry/exporter-metrics-otlp-proto](https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-proto) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-proto |
|
|
16
|
-
| [@opentelemetry/exporter-trace-otlp-proto](https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-proto) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-proto |
|
|
17
|
-
| [@opentelemetry/instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation |
|
|
14
|
+
| [@opentelemetry/exporter-logs-otlp-proto](https://www.npmjs.com/package/@opentelemetry/exporter-logs-otlp-proto) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-logs-otlp-proto |
|
|
15
|
+
| [@opentelemetry/exporter-metrics-otlp-proto](https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-proto) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-metrics-otlp-proto |
|
|
16
|
+
| [@opentelemetry/exporter-trace-otlp-proto](https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-proto) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/exporter-trace-otlp-proto |
|
|
17
|
+
| [@opentelemetry/instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation |
|
|
18
18
|
| [@opentelemetry/instrumentation-express](https://www.npmjs.com/package/@opentelemetry/instrumentation-express) | 0.67.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-express#readme |
|
|
19
19
|
| [@opentelemetry/instrumentation-http](https://www.npmjs.com/package/@opentelemetry/instrumentation-http) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-http |
|
|
20
|
-
| [@opentelemetry/resources](https://www.npmjs.com/package/@opentelemetry/resources) | 2.8.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-resources |
|
|
21
|
-
| [@opentelemetry/sdk-logs](https://www.npmjs.com/package/@opentelemetry/sdk-logs) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/sdk-logs |
|
|
22
|
-
| [@opentelemetry/sdk-metrics](https://www.npmjs.com/package/@opentelemetry/sdk-metrics) | 2.8.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics |
|
|
23
|
-
| [@opentelemetry/sdk-node](https://www.npmjs.com/package/@opentelemetry/sdk-node) | 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-node |
|
|
24
|
-
| [@opentelemetry/sdk-trace-base](https://www.npmjs.com/package/@opentelemetry/sdk-trace-base) | 2.8.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base |
|
|
20
|
+
| [@opentelemetry/resources](https://www.npmjs.com/package/@opentelemetry/resources) | 2.1.0, 2.8.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-resources |
|
|
21
|
+
| [@opentelemetry/sdk-logs](https://www.npmjs.com/package/@opentelemetry/sdk-logs) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/sdk-logs |
|
|
22
|
+
| [@opentelemetry/sdk-metrics](https://www.npmjs.com/package/@opentelemetry/sdk-metrics) | 2.1.0, 2.8.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/sdk-metrics |
|
|
23
|
+
| [@opentelemetry/sdk-node](https://www.npmjs.com/package/@opentelemetry/sdk-node) | 0.205.0, 0.219.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-node |
|
|
24
|
+
| [@opentelemetry/sdk-trace-base](https://www.npmjs.com/package/@opentelemetry/sdk-trace-base) | 2.1.0, 2.8.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base |
|
|
25
25
|
| [@opentelemetry/semantic-conventions](https://www.npmjs.com/package/@opentelemetry/semantic-conventions) | 1.38.0 | Apache-2.0 | https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions |
|
|
26
26
|
| [@radix-ui/react-accordion](https://www.npmjs.com/package/@radix-ui/react-accordion) | 1.2.12 | MIT | https://radix-ui.com/primitives |
|
|
27
27
|
| [@radix-ui/react-alert-dialog](https://www.npmjs.com/package/@radix-ui/react-alert-dialog) | 1.1.15 | MIT | https://radix-ui.com/primitives |
|
|
@@ -70,6 +70,7 @@ This Software contains code from the following open source projects:
|
|
|
70
70
|
| [lucide-react](https://www.npmjs.com/package/lucide-react) | 0.554.0 | ISC | https://lucide.dev |
|
|
71
71
|
| [magic-string](https://www.npmjs.com/package/magic-string) | 0.30.21 | MIT | https://github.com/Rich-Harris/magic-string#readme |
|
|
72
72
|
| [marked](https://www.npmjs.com/package/marked) | 16.4.2, 17.0.3 | MIT | https://marked.js.org |
|
|
73
|
+
| [mlflow-tracing](https://www.npmjs.com/package/mlflow-tracing) | 0.1.3 | Apache-2.0 | https://mlflow.org/ |
|
|
73
74
|
| [next-themes](https://www.npmjs.com/package/next-themes) | 0.4.6 | MIT | https://github.com/pacocoursey/next-themes#readme |
|
|
74
75
|
| [obug](https://www.npmjs.com/package/obug) | 2.1.1 | MIT | https://github.com/sxzz/obug#readme |
|
|
75
76
|
| [pg](https://www.npmjs.com/package/pg) | 8.18.0 | MIT | https://github.com/brianc/node-postgres |
|
|
@@ -82,5 +83,6 @@ This Software contains code from the following open source projects:
|
|
|
82
83
|
| [tailwind-merge](https://www.npmjs.com/package/tailwind-merge) | 3.4.0 | MIT | https://github.com/dcastil/tailwind-merge |
|
|
83
84
|
| [vaul](https://www.npmjs.com/package/vaul) | 1.1.2 | MIT | https://vaul.emilkowal.ski/ |
|
|
84
85
|
| [ws](https://www.npmjs.com/package/ws) | 7.5.10, 8.21.0 | MIT | https://github.com/websockets/ws |
|
|
86
|
+
| [yaml](https://www.npmjs.com/package/yaml) | 2.8.2 | ISC | https://eemeli.org/yaml/ |
|
|
85
87
|
| [zod](https://www.npmjs.com/package/zod) | 4.3.6 | MIT | https://zod.dev |
|
|
86
88
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { APP_YAML_FILE, DATABRICKS_YML_FILE, bindingTypeOf } from "../../deploy-config.js";
|
|
1
2
|
import { errorMessage } from "./utils.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
@@ -16,22 +17,13 @@ import yaml from "js-yaml";
|
|
|
16
17
|
*
|
|
17
18
|
* SDK-free; pure YAML parsing. Absent files degrade to empty results.
|
|
18
19
|
*/
|
|
19
|
-
const DEFAULT_BUNDLE_FILE = "databricks.yml";
|
|
20
|
-
const DEFAULT_APP_YAML_FILE = "app.yaml";
|
|
21
20
|
/** A `${resources.<type>.<key>.<field>}` reference — a bundle-created resource. */
|
|
22
21
|
const RESOURCES_REF = /\$\{resources\.([^.]+)\.([^.}]+)\.[^}]+\}/;
|
|
23
|
-
/** The typed sub-key of a binding is its single non-`name` object property. */
|
|
24
|
-
function bindingType(block) {
|
|
25
|
-
for (const [k, v] of Object.entries(block)) {
|
|
26
|
-
if (k === "name") continue;
|
|
27
|
-
if (v && typeof v === "object") return k;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
22
|
/** Classifies a binding by its typed sub-key and origin: scanning its field
|
|
31
23
|
* values for a `${resources.*}` reference (bundle-managed) vs anything else
|
|
32
24
|
* (external). */
|
|
33
25
|
function classifyBinding(block) {
|
|
34
|
-
const type =
|
|
26
|
+
const type = bindingTypeOf(block);
|
|
35
27
|
const typed = type ? block[type] : void 0;
|
|
36
28
|
if (typed && typeof typed === "object") for (const value of Object.values(typed)) {
|
|
37
29
|
if (typeof value !== "string") continue;
|
|
@@ -74,7 +66,7 @@ function readYaml(filePath) {
|
|
|
74
66
|
* empty-but-`present:false` result when no bundle is found, so callers can treat
|
|
75
67
|
* "no bundle" as "everything external" without special-casing.
|
|
76
68
|
*/
|
|
77
|
-
function readBundleInfo(cwd = process.cwd(), bundleFile =
|
|
69
|
+
function readBundleInfo(cwd = process.cwd(), bundleFile = DATABRICKS_YML_FILE, appYamlFile = APP_YAML_FILE) {
|
|
78
70
|
const bindings = /* @__PURE__ */ new Map();
|
|
79
71
|
const envToBinding = /* @__PURE__ */ new Map();
|
|
80
72
|
const declaredResources = /* @__PURE__ */ new Set();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","names":[],"sources":["../../../../src/cli/commands/doctor/bundle.ts"],"sourcesContent":["/**\n * Reads `databricks.yml` + `app.yaml` for two things doctor can't get from\n * `appkit.plugins.json`:\n *\n * 1. **Provenance** — whether each binding references an existing resource\n * (`${var.*}`/literal → `external`) or one this bundle creates\n * (`${resources.<type>.<key>.*}` → `bundle-managed`).\n * 2. **Wiring** — the binding `name` joins the three files: `app.yaml`'s\n * `valueFrom: <name>` must match a `databricks.yml` binding `name`.\n *\n * SDK-free; pure YAML parsing. Absent files degrade to empty results.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport yaml from \"js-yaml\";\nimport
|
|
1
|
+
{"version":3,"file":"bundle.js","names":[],"sources":["../../../../src/cli/commands/doctor/bundle.ts"],"sourcesContent":["/**\n * Reads `databricks.yml` + `app.yaml` for two things doctor can't get from\n * `appkit.plugins.json`:\n *\n * 1. **Provenance** — whether each binding references an existing resource\n * (`${var.*}`/literal → `external`) or one this bundle creates\n * (`${resources.<type>.<key>.*}` → `bundle-managed`).\n * 2. **Wiring** — the binding `name` joins the three files: `app.yaml`'s\n * `valueFrom: <name>` must match a `databricks.yml` binding `name`.\n *\n * SDK-free; pure YAML parsing. Absent files degrade to empty results.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport yaml from \"js-yaml\";\nimport {\n APP_YAML_FILE,\n bindingTypeOf,\n DATABRICKS_YML_FILE,\n} from \"../../deploy-config\";\nimport type { ResourceOrigin } from \"./types\";\nimport { errorMessage } from \"./utils\";\n\n/** A `${resources.<type>.<key>.<field>}` reference — a bundle-created resource. */\nconst RESOURCES_REF = /\\$\\{resources\\.([^.]+)\\.([^.}]+)\\.[^}]+\\}/;\n\n/** One app resource binding from `resources.apps.<app>.resources[]`. */\nexport interface BundleBinding {\n /** The binding name — the join key with `app.yaml` `valueFrom`. */\n name: string;\n /** The typed sub-key that names the kind (`sql_warehouse`, `genie_space`, …). */\n type?: string;\n origin: ResourceOrigin;\n /** For bundle-managed bindings: the `<type>.<key>` it references, if parseable. */\n ref?: { type: string; key: string };\n}\n\nexport interface BundleInfo {\n /** Bindings under every app, keyed by binding name. */\n bindings: Map<string, BundleBinding>;\n /** `app.yaml` env var name → binding name (from `valueFrom`). */\n envToBinding: Map<string, string>;\n /** Declared bundle resource keys, as `\"<type>.<key>\"`, for ref-integrity. */\n declaredResources: Set<string>;\n /** True when a `databricks.yml` was found and parsed. */\n present: boolean;\n}\n\ninterface AppResourceBlock {\n name?: string;\n [k: string]: unknown;\n}\ninterface BundleAppBlock {\n resources?: AppResourceBlock[];\n}\ninterface BundleDoc {\n resources?: {\n apps?: Record<string, BundleAppBlock>;\n [otherType: string]: Record<string, unknown> | undefined;\n };\n}\ninterface AppYamlDoc {\n env?: Array<{ name?: string; valueFrom?: string }>;\n}\n\n/** Classifies a binding by its typed sub-key and origin: scanning its field\n * values for a `${resources.*}` reference (bundle-managed) vs anything else\n * (external). */\nfunction classifyBinding(block: AppResourceBlock): {\n type?: string;\n origin: ResourceOrigin;\n ref?: { type: string; key: string };\n} {\n const type = bindingTypeOf(block);\n const typed = type ? block[type] : undefined;\n if (typed && typeof typed === \"object\") {\n for (const value of Object.values(typed as Record<string, unknown>)) {\n if (typeof value !== \"string\") continue;\n const m = value.match(RESOURCES_REF);\n if (m) {\n return {\n type,\n origin: \"bundle-managed\",\n ref: { type: m[1], key: m[2] },\n };\n }\n }\n }\n return { type, origin: \"external\" };\n}\n\n/**\n * Reads and parses a YAML file. Returns `null` when the file is absent (a\n * legitimate \"no bundle\" state); throws on invalid YAML, since silently\n * ignoring it would let doctor report a false all-clear.\n */\nfunction readYaml<T>(filePath: string): T | null {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n try {\n return (yaml.load(raw) ?? {}) as T;\n } catch (err) {\n throw new Error(\n `Failed to parse ${path.basename(filePath)}: ${errorMessage(err)}`,\n );\n }\n}\n\n/**\n * Parses `databricks.yml` (+ `app.yaml`) into {@link BundleInfo}. Returns an\n * empty-but-`present:false` result when no bundle is found, so callers can treat\n * \"no bundle\" as \"everything external\" without special-casing.\n */\nexport function readBundleInfo(\n cwd: string = process.cwd(),\n bundleFile: string = DATABRICKS_YML_FILE,\n appYamlFile: string = APP_YAML_FILE,\n): BundleInfo {\n const bindings = new Map<string, BundleBinding>();\n const envToBinding = new Map<string, string>();\n const declaredResources = new Set<string>();\n\n const doc = readYaml<BundleDoc>(path.resolve(cwd, bundleFile));\n if (!doc) {\n return { bindings, envToBinding, declaredResources, present: false };\n }\n\n for (const [type, group] of Object.entries(doc.resources ?? {})) {\n if (!group || typeof group !== \"object\") continue;\n for (const key of Object.keys(group))\n declaredResources.add(`${type}.${key}`);\n }\n\n for (const app of Object.values(doc.resources?.apps ?? {})) {\n for (const block of app.resources ?? []) {\n if (!block?.name) continue;\n const { type, origin, ref } = classifyBinding(block);\n bindings.set(block.name, {\n name: block.name,\n type,\n origin,\n ref,\n });\n }\n }\n\n const appYaml = readYaml<AppYamlDoc>(path.resolve(cwd, appYamlFile));\n for (const entry of appYaml?.env ?? []) {\n if (entry?.name && entry.valueFrom) {\n envToBinding.set(entry.name, entry.valueFrom);\n }\n }\n\n return { bindings, envToBinding, declaredResources, present: true };\n}\n\n/**\n * Resolves the origin for a target given its env vars, by walking\n * env var → binding name → binding origin. Returns undefined when the bundle\n * has nothing to say (so the target stays external by default).\n */\nexport function originForEnvVars(\n envVars: string[],\n info: BundleInfo,\n): ResourceOrigin | undefined {\n if (!info.present) return undefined;\n for (const env of envVars) {\n const bindingName = info.envToBinding.get(env);\n if (!bindingName) continue;\n const binding = info.bindings.get(bindingName);\n if (binding) return binding.origin;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,gBAAgB;;;;AA4CtB,SAAS,gBAAgB,OAIvB;CACA,MAAM,OAAO,cAAc,MAAM;CACjC,MAAM,QAAQ,OAAO,MAAM,QAAQ;AACnC,KAAI,SAAS,OAAO,UAAU,SAC5B,MAAK,MAAM,SAAS,OAAO,OAAO,MAAiC,EAAE;AACnE,MAAI,OAAO,UAAU,SAAU;EAC/B,MAAM,IAAI,MAAM,MAAM,cAAc;AACpC,MAAI,EACF,QAAO;GACL;GACA,QAAQ;GACR,KAAK;IAAE,MAAM,EAAE;IAAI,KAAK,EAAE;IAAI;GAC/B;;AAIP,QAAO;EAAE;EAAM,QAAQ;EAAY;;;;;;;AAQrC,SAAS,SAAY,UAA4B;CAC/C,IAAI;AACJ,KAAI;AACF,QAAM,GAAG,aAAa,UAAU,QAAQ;UACjC,KAAK;AACZ,MAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,QAAM;;AAER,KAAI;AACF,SAAQ,KAAK,KAAK,IAAI,IAAI,EAAE;UACrB,KAAK;AACZ,QAAM,IAAI,MACR,mBAAmB,KAAK,SAAS,SAAS,CAAC,IAAI,aAAa,IAAI,GACjE;;;;;;;;AASL,SAAgB,eACd,MAAc,QAAQ,KAAK,EAC3B,aAAqB,qBACrB,cAAsB,eACV;CACZ,MAAM,2BAAW,IAAI,KAA4B;CACjD,MAAM,+BAAe,IAAI,KAAqB;CAC9C,MAAM,oCAAoB,IAAI,KAAa;CAE3C,MAAM,MAAM,SAAoB,KAAK,QAAQ,KAAK,WAAW,CAAC;AAC9D,KAAI,CAAC,IACH,QAAO;EAAE;EAAU;EAAc;EAAmB,SAAS;EAAO;AAGtE,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,IAAI,aAAa,EAAE,CAAC,EAAE;AAC/D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,OAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CAClC,mBAAkB,IAAI,GAAG,KAAK,GAAG,MAAM;;AAG3C,MAAK,MAAM,OAAO,OAAO,OAAO,IAAI,WAAW,QAAQ,EAAE,CAAC,CACxD,MAAK,MAAM,SAAS,IAAI,aAAa,EAAE,EAAE;AACvC,MAAI,CAAC,OAAO,KAAM;EAClB,MAAM,EAAE,MAAM,QAAQ,QAAQ,gBAAgB,MAAM;AACpD,WAAS,IAAI,MAAM,MAAM;GACvB,MAAM,MAAM;GACZ;GACA;GACA;GACD,CAAC;;CAIN,MAAM,UAAU,SAAqB,KAAK,QAAQ,KAAK,YAAY,CAAC;AACpE,MAAK,MAAM,SAAS,SAAS,OAAO,EAAE,CACpC,KAAI,OAAO,QAAQ,MAAM,UACvB,cAAa,IAAI,MAAM,MAAM,MAAM,UAAU;AAIjD,QAAO;EAAE;EAAU;EAAc;EAAmB,SAAS;EAAM;;;;;;;AAQrE,SAAgB,iBACd,SACA,MAC4B;AAC5B,KAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,MAAK,MAAM,OAAO,SAAS;EACzB,MAAM,cAAc,KAAK,aAAa,IAAI,IAAI;AAC9C,MAAI,CAAC,YAAa;EAClB,MAAM,UAAU,KAAK,SAAS,IAAI,YAAY;AAC9C,MAAI,QAAS,QAAO,QAAQ"}
|
|
@@ -1,38 +1,34 @@
|
|
|
1
|
+
import { loadConfigFile } from "../../../workspace-client/legacy.js";
|
|
2
|
+
import { createWorkspaceClient } from "../../../workspace-client/factory.js";
|
|
3
|
+
|
|
1
4
|
//#region src/cli/commands/doctor/databricks-client.ts
|
|
2
5
|
/**
|
|
3
|
-
* The
|
|
4
|
-
* SDK
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* The seam where `appkit doctor` crosses into the Databricks SDK. It reaches
|
|
7
|
+
* the SDK through the shared `workspace-client` facade — the one sanctioned SDK
|
|
8
|
+
* import site — so no dynamic import or `noRestrictedImports` suppression is
|
|
9
|
+
* needed. (The Lakebase probe still dynamically imports `@databricks/appkit`
|
|
10
|
+
* below, since that's an optional peer `shared` deliberately doesn't depend on.)
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Retained for backward compatibility. Since `shared` now depends on the SDK
|
|
14
|
+
* (reached via the workspace-client facade), the SDK is always resolvable at
|
|
15
|
+
* runtime and this is no longer thrown; kept exported so existing callers and
|
|
16
|
+
* the `SDK_NOT_INSTALLED` diagnostic branch keep compiling.
|
|
13
17
|
*/
|
|
14
|
-
/** Raised when `@databricks/sdk-experimental` is not resolvable at runtime. */
|
|
15
18
|
var SdkNotInstalledError = class extends Error {
|
|
16
19
|
constructor() {
|
|
17
|
-
super("The 'doctor' command requires the Databricks SDK (a dependency of @databricks/appkit).
|
|
20
|
+
super("The 'doctor' command requires the Databricks SDK (a dependency of @databricks/appkit).");
|
|
18
21
|
this.name = "SdkNotInstalledError";
|
|
19
22
|
}
|
|
20
23
|
};
|
|
21
24
|
function isModuleNotFound(err) {
|
|
22
25
|
return err instanceof Error && (err.message.includes("Cannot find module") || err.message.includes("Cannot find package") || err.code === "ERR_MODULE_NOT_FOUND");
|
|
23
26
|
}
|
|
24
|
-
/** Constructs a
|
|
27
|
+
/** Constructs a workspace client via the SDK's unified-auth chain. An explicit
|
|
25
28
|
* `profile` is passed through `Config.profile` rather than mutating
|
|
26
29
|
* `process.env`, so it doesn't leak beyond this call. */
|
|
27
30
|
async function getServiceClient(profile) {
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
sdk = await import("@databricks/sdk-experimental");
|
|
31
|
-
} catch (err) {
|
|
32
|
-
if (isModuleNotFound(err)) throw new SdkNotInstalledError();
|
|
33
|
-
throw err;
|
|
34
|
-
}
|
|
35
|
-
return { client: new sdk.WorkspaceClient(profile ? { profile } : {}) };
|
|
31
|
+
return { client: createWorkspaceClient(profile ? { profile } : {}).toLegacyWorkspaceClient() };
|
|
36
32
|
}
|
|
37
33
|
/**
|
|
38
34
|
* The host a named profile declares in `~/.databrickscfg`, read offline. Doctor
|
|
@@ -43,7 +39,7 @@ async function getServiceClient(profile) {
|
|
|
43
39
|
*/
|
|
44
40
|
async function getProfileHost(profile) {
|
|
45
41
|
try {
|
|
46
|
-
const { iniFile } = await
|
|
42
|
+
const { iniFile } = await loadConfigFile(process.env.DATABRICKS_CONFIG_FILE);
|
|
47
43
|
const host = iniFile?.[profile]?.host;
|
|
48
44
|
return typeof host === "string" && host.length > 0 ? host : void 0;
|
|
49
45
|
} catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"databricks-client.js","names":[],"sources":["../../../../src/cli/commands/doctor/databricks-client.ts"],"sourcesContent":["/**\n * The
|
|
1
|
+
{"version":3,"file":"databricks-client.js","names":[],"sources":["../../../../src/cli/commands/doctor/databricks-client.ts"],"sourcesContent":["/**\n * The seam where `appkit doctor` crosses into the Databricks SDK. It reaches\n * the SDK through the shared `workspace-client` facade — the one sanctioned SDK\n * import site — so no dynamic import or `noRestrictedImports` suppression is\n * needed. (The Lakebase probe still dynamically imports `@databricks/appkit`\n * below, since that's an optional peer `shared` deliberately doesn't depend on.)\n */\n\nimport {\n createWorkspaceClient,\n loadConfigFile,\n} from \"../../../workspace-client\";\n\n/**\n * Retained for backward compatibility. Since `shared` now depends on the SDK\n * (reached via the workspace-client facade), the SDK is always resolvable at\n * runtime and this is no longer thrown; kept exported so existing callers and\n * the `SDK_NOT_INSTALLED` diagnostic branch keep compiling.\n */\nexport class SdkNotInstalledError extends Error {\n constructor() {\n super(\n \"The 'doctor' command requires the Databricks SDK (a dependency of @databricks/appkit).\",\n );\n this.name = \"SdkNotInstalledError\";\n }\n}\n\ninterface ServiceClientHandle {\n /** WorkspaceClient, typed as unknown to keep doctor's call sites SDK-agnostic. */\n client: unknown;\n}\n\nfunction isModuleNotFound(err: unknown): boolean {\n return (\n err instanceof Error &&\n (err.message.includes(\"Cannot find module\") ||\n err.message.includes(\"Cannot find package\") ||\n (err as NodeJS.ErrnoException).code === \"ERR_MODULE_NOT_FOUND\")\n );\n}\n\n/** Constructs a workspace client via the SDK's unified-auth chain. An explicit\n * `profile` is passed through `Config.profile` rather than mutating\n * `process.env`, so it doesn't leak beyond this call. */\nexport async function getServiceClient(\n profile?: string,\n): Promise<ServiceClientHandle> {\n const client = createWorkspaceClient(\n profile ? { profile } : {},\n ).toLegacyWorkspaceClient();\n return { client };\n}\n\n/**\n * The host a named profile declares in `~/.databrickscfg`, read offline. Doctor\n * uses it to detect a profile/host conflict, so it must not depend on the\n * resolved config (where `DATABRICKS_HOST` has already won). Returns undefined\n * for any unreadable file, missing profile, or hostless profile — a conflict we\n * can't prove isn't worth reporting.\n */\nexport async function getProfileHost(\n profile: string,\n): Promise<string | undefined> {\n try {\n const { iniFile } = await loadConfigFile(\n process.env.DATABRICKS_CONFIG_FILE,\n );\n const host = iniFile?.[profile]?.host;\n return typeof host === \"string\" && host.length > 0 ? host : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Raised when `@databricks/appkit` (needed for the Lakebase probe) is absent. */\nexport class AppkitNotInstalledError extends Error {\n constructor() {\n super(\n \"The Lakebase connection check requires @databricks/appkit. Please install it to run this check.\",\n );\n this.name = \"AppkitNotInstalledError\";\n }\n}\n\n/** Minimal `pg.Pool`-shaped handle. The caller owns it and must call `end()`. */\nexport interface LakebasePoolHandle {\n query: (sql: string) => Promise<unknown>;\n end: () => Promise<void>;\n}\n\n/** Builds a Lakebase pool via `createLakebasePool`. Connection settings\n * (`PGHOST`, `LAKEBASE_ENDPOINT`, …) come from the environment; we inject only\n * the resolved workspace client for OAuth token minting. */\nexport async function getLakebasePool(\n client: unknown,\n): Promise<LakebasePoolHandle> {\n // A variable specifier stops TS from statically resolving `@databricks/appkit`,\n // an optional peer that `shared` has no dependency on.\n const appkitPkg = \"@databricks/appkit\";\n let appkit: {\n createLakebasePool: (cfg: Record<string, unknown>) => unknown;\n };\n try {\n appkit = await import(appkitPkg);\n } catch (err) {\n if (isModuleNotFound(err)) {\n throw new AppkitNotInstalledError();\n }\n throw err;\n }\n\n // Silence Lakebase's own logger. It defaults to an error-only console logger\n // that dumps the raw SDK ApiError (stack + full response blob) to stderr on a\n // failed token fetch. doctor classifies and prints that failure itself, so the\n // library's dump is just noise on top of our clean one-line report.\n const pool = appkit.createLakebasePool({\n workspaceClient: client,\n logger: { error: false },\n });\n return pool as unknown as LakebasePoolHandle;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,IAAa,uBAAb,cAA0C,MAAM;CAC9C,cAAc;AACZ,QACE,yFACD;AACD,OAAK,OAAO;;;AAShB,SAAS,iBAAiB,KAAuB;AAC/C,QACE,eAAe,UACd,IAAI,QAAQ,SAAS,qBAAqB,IACzC,IAAI,QAAQ,SAAS,sBAAsB,IAC1C,IAA8B,SAAS;;;;;AAO9C,eAAsB,iBACpB,SAC8B;AAI9B,QAAO,EAAE,QAHM,sBACb,UAAU,EAAE,SAAS,GAAG,EAAE,CAC3B,CAAC,yBAAyB,EACV;;;;;;;;;AAUnB,eAAsB,eACpB,SAC6B;AAC7B,KAAI;EACF,MAAM,EAAE,YAAY,MAAM,eACxB,QAAQ,IAAI,uBACb;EACD,MAAM,OAAO,UAAU,UAAU;AACjC,SAAO,OAAO,SAAS,YAAY,KAAK,SAAS,IAAI,OAAO;SACtD;AACN;;;;AAKJ,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;AACZ,QACE,kGACD;AACD,OAAK,OAAO;;;;;;AAahB,eAAsB,gBACpB,QAC6B;CAG7B,MAAM,YAAY;CAClB,IAAI;AAGJ,KAAI;AACF,WAAS,MAAM,OAAO;UACf,KAAK;AACZ,MAAI,iBAAiB,IAAI,CACvB,OAAM,IAAI,yBAAyB;AAErC,QAAM;;AAWR,QAJa,OAAO,mBAAmB;EACrC,iBAAiB;EACjB,QAAQ,EAAE,OAAO,OAAO;EACzB,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { errorMessage } from "./utils.js";
|
|
2
|
+
import { computeOriginFromField } from "../../../schemas/manifest.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
|
|
@@ -10,13 +11,10 @@ import path from "node:path";
|
|
|
10
11
|
* `appkit plugin list` reads.
|
|
11
12
|
*/
|
|
12
13
|
const DEFAULT_MANIFEST_FILE = "appkit.plugins.json";
|
|
13
|
-
/** A field's env var is the developer's to supply only when
|
|
14
|
-
*
|
|
14
|
+
/** A field's env var is the developer's to supply only when its effective
|
|
15
|
+
* origin is `"user"` — a stamped `origin`, else derived from the field shape. */
|
|
15
16
|
function isUserSuppliedEnv(field) {
|
|
16
|
-
|
|
17
|
-
if (field.origin === "platform" || field.origin === "static") return false;
|
|
18
|
-
if (field.localOnly) return false;
|
|
19
|
-
return true;
|
|
17
|
+
return (field.origin ?? computeOriginFromField(field)) === "user";
|
|
20
18
|
}
|
|
21
19
|
/** Env vars the config layer should presence-check (i.e. user-supplied ones). */
|
|
22
20
|
function envVarsOf(resource) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-targets.js","names":[],"sources":["../../../../src/cli/commands/doctor/resolve-targets.ts"],"sourcesContent":["/**\n * Reads the app's resolved template manifest (`appkit.plugins.json`) and\n * flattens each plugin's declared resources into the flat {@link ResourceTarget}\n * shape the checks consume. Offline and SDK-free — parses the same file\n * `appkit plugin list` reads.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { ResourceTarget } from \"./types\";\nimport { errorMessage } from \"./utils\";\n\nexport const DEFAULT_MANIFEST_FILE = \"appkit.plugins.json\";\n\ninterface ManifestField {\n env?: string;\n /** Static default value baked into the manifest. */\n value?: string;\n origin?: \"user\" | \"platform\" | \"static\" | \"cli\";\n /** Only generated into the local .env; the platform injects it at deploy. */\n localOnly?: boolean;\n}\n\ninterface ManifestResource {\n type: string;\n resourceKey: string;\n alias?: string;\n permission: string;\n fields?: Record<string, ManifestField>;\n}\n\ninterface ManifestPlugin {\n resources?: {\n required?: ManifestResource[];\n optional?: ManifestResource[];\n };\n /**\n * True when the plugin is actually used by the app (imported and passed to\n * `createApp`). `plugin sync` discovers *every* plugin shipped by installed\n * packages but only marks the used ones; doctor checks only those, so an\n * unused built-in doesn't produce phantom \"missing env var\" errors.\n */\n requiredByTemplate?: boolean;\n}\n\ninterface TemplateManifest {\n plugins?: Record<string, ManifestPlugin>;\n}\n\n/** A field's env var is the developer's to supply only when
|
|
1
|
+
{"version":3,"file":"resolve-targets.js","names":[],"sources":["../../../../src/cli/commands/doctor/resolve-targets.ts"],"sourcesContent":["/**\n * Reads the app's resolved template manifest (`appkit.plugins.json`) and\n * flattens each plugin's declared resources into the flat {@link ResourceTarget}\n * shape the checks consume. Offline and SDK-free — parses the same file\n * `appkit plugin list` reads.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { computeOriginFromField } from \"../../../schemas/manifest\";\nimport type { ResourceTarget } from \"./types\";\nimport { errorMessage } from \"./utils\";\n\nexport const DEFAULT_MANIFEST_FILE = \"appkit.plugins.json\";\n\ninterface ManifestField {\n env?: string;\n /** Static default value baked into the manifest. */\n value?: string;\n origin?: \"user\" | \"platform\" | \"static\" | \"cli\";\n /** Resolver key (cli origin), e.g. \"postgres:host\". */\n resolve?: string;\n /** Only generated into the local .env; the platform injects it at deploy. */\n localOnly?: boolean;\n}\n\ninterface ManifestResource {\n type: string;\n resourceKey: string;\n alias?: string;\n permission: string;\n fields?: Record<string, ManifestField>;\n}\n\ninterface ManifestPlugin {\n resources?: {\n required?: ManifestResource[];\n optional?: ManifestResource[];\n };\n /**\n * True when the plugin is actually used by the app (imported and passed to\n * `createApp`). `plugin sync` discovers *every* plugin shipped by installed\n * packages but only marks the used ones; doctor checks only those, so an\n * unused built-in doesn't produce phantom \"missing env var\" errors.\n */\n requiredByTemplate?: boolean;\n}\n\ninterface TemplateManifest {\n plugins?: Record<string, ManifestPlugin>;\n}\n\n/** A field's env var is the developer's to supply only when its effective\n * origin is `\"user\"` — a stamped `origin`, else derived from the field shape. */\nfunction isUserSuppliedEnv(field: ManifestField): boolean {\n const origin = field.origin ?? computeOriginFromField(field);\n return origin === \"user\";\n}\n\n/** Env vars the config layer should presence-check (i.e. user-supplied ones). */\nfunction envVarsOf(resource: ManifestResource): string[] {\n const fields = resource.fields ?? {};\n const envs: string[] = [];\n for (const field of Object.values(fields)) {\n if (field?.env && isUserSuppliedEnv(field)) envs.push(field.env);\n }\n return envs;\n}\n\n/** Resolves each field's value keyed by manifest field name, preferring the\n * environment over a static `value` default. Unset/empty fields are omitted. */\nfunction fieldValuesOf(resource: ManifestResource): Record<string, string> {\n const fields = resource.fields ?? {};\n const values: Record<string, string> = {};\n for (const [fieldName, field] of Object.entries(fields)) {\n if (!field) continue;\n const envValue = field.env ? process.env[field.env] : undefined;\n const resolved =\n envValue !== undefined && envValue.trim().length > 0\n ? envValue\n : field.value;\n if (resolved !== undefined && resolved.trim().length > 0) {\n values[fieldName] = resolved;\n }\n }\n return values;\n}\n\nfunction toTarget(\n plugin: string,\n resource: ManifestResource,\n required: boolean,\n): ResourceTarget {\n return {\n type: resource.type,\n resourceKey: resource.resourceKey,\n alias: resource.alias ?? resource.resourceKey,\n plugin,\n requiredPermission: resource.permission,\n required,\n envVars: envVarsOf(resource),\n fieldValues: fieldValuesOf(resource),\n };\n}\n\n/** @throws if the file cannot be read or parsed. */\nexport function targetsFromManifestFile(\n manifestPath: string,\n): ResourceTarget[] {\n let raw: string;\n try {\n raw = fs.readFileSync(manifestPath, \"utf-8\");\n } catch (err) {\n throw new Error(\n `Failed to read manifest file ${manifestPath}: ${errorMessage(err)}`,\n );\n }\n\n let data: TemplateManifest;\n try {\n data = JSON.parse(raw) as TemplateManifest;\n } catch (err) {\n throw new Error(\n `Failed to parse manifest file ${manifestPath}: ${errorMessage(err)}`,\n );\n }\n\n // `plugin sync` catalogues every plugin the installed packages ship, marking\n // only those wired into `createApp` with `requiredByTemplate`. Check exactly\n // those, else doctor reports phantom \"missing env var\" errors for unimported\n // plugins.\n // KNOWN LIMITATION: sync strips `requiredByTemplate` for non-GA plugins, so a\n // used beta/experimental plugin is not checked here yet. See the README.\n const selected = Object.entries(data.plugins ?? {}).filter(\n ([, plugin]) => plugin.requiredByTemplate === true,\n );\n\n const targets: ResourceTarget[] = [];\n for (const [pluginName, plugin] of selected) {\n const resources = plugin.resources ?? {};\n for (const resource of resources.required ?? []) {\n targets.push(toTarget(pluginName, resource, true));\n }\n for (const resource of resources.optional ?? []) {\n targets.push(toTarget(pluginName, resource, false));\n }\n }\n return targets;\n}\n\n/** Returns an empty list if the manifest is absent — an app may legitimately\n * declare no resources. */\nexport function resolveTargetsFromCwd(\n cwd: string = process.cwd(),\n manifestFile: string = DEFAULT_MANIFEST_FILE,\n): ResourceTarget[] {\n const manifestPath = path.resolve(cwd, manifestFile);\n if (!fs.existsSync(manifestPath)) {\n return [];\n }\n return targetsFromManifestFile(manifestPath);\n}\n"],"mappings":";;;;;;;;;;;;AAaA,MAAa,wBAAwB;;;AAyCrC,SAAS,kBAAkB,OAA+B;AAExD,SADe,MAAM,UAAU,uBAAuB,MAAM,MAC1C;;;AAIpB,SAAS,UAAU,UAAsC;CACvD,MAAM,SAAS,SAAS,UAAU,EAAE;CACpC,MAAM,OAAiB,EAAE;AACzB,MAAK,MAAM,SAAS,OAAO,OAAO,OAAO,CACvC,KAAI,OAAO,OAAO,kBAAkB,MAAM,CAAE,MAAK,KAAK,MAAM,IAAI;AAElE,QAAO;;;;AAKT,SAAS,cAAc,UAAoD;CACzE,MAAM,SAAS,SAAS,UAAU,EAAE;CACpC,MAAM,SAAiC,EAAE;AACzC,MAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ,OAAO,EAAE;AACvD,MAAI,CAAC,MAAO;EACZ,MAAM,WAAW,MAAM,MAAM,QAAQ,IAAI,MAAM,OAAO;EACtD,MAAM,WACJ,aAAa,UAAa,SAAS,MAAM,CAAC,SAAS,IAC/C,WACA,MAAM;AACZ,MAAI,aAAa,UAAa,SAAS,MAAM,CAAC,SAAS,EACrD,QAAO,aAAa;;AAGxB,QAAO;;AAGT,SAAS,SACP,QACA,UACA,UACgB;AAChB,QAAO;EACL,MAAM,SAAS;EACf,aAAa,SAAS;EACtB,OAAO,SAAS,SAAS,SAAS;EAClC;EACA,oBAAoB,SAAS;EAC7B;EACA,SAAS,UAAU,SAAS;EAC5B,aAAa,cAAc,SAAS;EACrC;;;AAIH,SAAgB,wBACd,cACkB;CAClB,IAAI;AACJ,KAAI;AACF,QAAM,GAAG,aAAa,cAAc,QAAQ;UACrC,KAAK;AACZ,QAAM,IAAI,MACR,gCAAgC,aAAa,IAAI,aAAa,IAAI,GACnE;;CAGH,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;UACf,KAAK;AACZ,QAAM,IAAI,MACR,iCAAiC,aAAa,IAAI,aAAa,IAAI,GACpE;;CASH,MAAM,WAAW,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC,CAAC,QACjD,GAAG,YAAY,OAAO,uBAAuB,KAC/C;CAED,MAAM,UAA4B,EAAE;AACpC,MAAK,MAAM,CAAC,YAAY,WAAW,UAAU;EAC3C,MAAM,YAAY,OAAO,aAAa,EAAE;AACxC,OAAK,MAAM,YAAY,UAAU,YAAY,EAAE,CAC7C,SAAQ,KAAK,SAAS,YAAY,UAAU,KAAK,CAAC;AAEpD,OAAK,MAAM,YAAY,UAAU,YAAY,EAAE,CAC7C,SAAQ,KAAK,SAAS,YAAY,UAAU,MAAM,CAAC;;AAGvD,QAAO;;;;AAKT,SAAgB,sBACd,MAAc,QAAQ,KAAK,EAC3B,eAAuB,uBACL;CAClB,MAAM,eAAe,KAAK,QAAQ,KAAK,aAAa;AACpD,KAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,QAAO,EAAE;AAEX,QAAO,wBAAwB,aAAa"}
|