@dbx-tools/cli-appkit-env 0.3.21

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 ADDED
@@ -0,0 +1,71 @@
1
+ # @dbx-tools/cli-appkit-env
2
+
3
+ CLI and formatting helpers for exporting AppKit auto-configuration results.
4
+
5
+ Use the `dbx-tools-appkit-env` bin when a shell or process manager needs the Lakebase /
6
+ AppKit environment that [`@dbx-tools/appkit`](../../node/appkit) would
7
+ resolve before `createApp()`.
8
+
9
+ Key features:
10
+
11
+ - Runs the same AppKit auto-configuration path used by
12
+ [`@dbx-tools/appkit`](../../node/appkit).
13
+ - Emits only variables that changed during auto-configuration.
14
+ - Supports shell `export`, JSON, and Windows `set` output formats.
15
+ - Provides importable env snapshot/diff/format helpers for tests and wrapper
16
+ CLIs.
17
+ - Keeps local shell setup aligned with deployed AppKit startup behavior.
18
+
19
+ ## Load Env Into A Shell
20
+
21
+ ```sh
22
+ eval "$(dbx-tools-appkit-env --quiet)"
23
+ ```
24
+
25
+ The command snapshots `process.env`, runs AppKit auto-config, diffs the result,
26
+ and prints only new or changed variables. On POSIX shells the default output is
27
+ `export KEY=value`.
28
+
29
+ The package installs two equivalent commands: `dbx-tools-appkit-env` and the
30
+ shorter `dbxt-appkit-env`. Neither matches the package name, so a one-off run
31
+ has to name the command explicitly:
32
+
33
+ ```sh
34
+ npx --package @dbx-tools/cli-appkit-env dbx-tools-appkit-env --quiet
35
+ ```
36
+
37
+ This is useful when another process must start after Lakebase discovery has
38
+ filled `PGHOST`, `PGDATABASE`, `PGUSER`, or related AppKit variables.
39
+
40
+ ## Inspect JSON Or Windows Output
41
+
42
+ ```sh
43
+ dbx-tools-appkit-env --format json
44
+ dbx-tools-appkit-env --format windows
45
+ ```
46
+
47
+ Use JSON for process managers and tests. Use Windows format for `cmd.exe`
48
+ `set KEY=value` lines.
49
+
50
+ ## Format Env Diffs Programmatically
51
+
52
+ ```ts
53
+ import { envExport } from "@dbx-tools/cli-appkit-env";
54
+
55
+ const before = envExport.snapshotEnv();
56
+ process.env.PGHOST = "ep-foo.database.azuredatabricks.net";
57
+ const diff = envExport.diffEnv(before);
58
+
59
+ console.log(envExport.formatEnvExport(diff, "export"));
60
+ ```
61
+
62
+ These helpers are useful in tests for auto-config behavior or in custom CLIs
63
+ that want the same output formats without invoking the bin.
64
+
65
+ ## Modules
66
+
67
+ - `envExport` - env snapshots, env diffs, default format detection,
68
+ `formatEnvExport()`, and `parseEnvExportFormat()`.
69
+
70
+ Auto-config itself lives in
71
+ [`@dbx-tools/appkit`](../../node/appkit).
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ // GENERATED by projen synth (cli tag) - DO NOT EDIT.
3
+ // Regenerated from ./dbx-tools-appkit-env.ts.
4
+ // Hand edits are overwritten on the next watch; this file is read-only.
5
+
6
+ // Resolved as a bare specifier so Node looks in THIS file's package rather than the
7
+ // caller's cwd - the only way a globally installed CLI finds its own tsx.
8
+ import { register } from "tsx/esm/api";
9
+
10
+ register();
11
+ await import(new URL("./dbx-tools-appkit-env.ts", import.meta.url).href);
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ /**
3
+ * `dbx-tools-appkit-env`: run AppKit auto-config and print the env vars it added or
4
+ * changed. Snapshots `process.env`, runs auto-config, diffs, and writes
5
+ * eval-able `export` / `set` lines (or JSON) to stdout - e.g.
6
+ * `eval "$(dbx-tools-appkit-env)"` to load a resolved Lakebase connection into your shell.
7
+ */
8
+
9
+ import { Command, CommanderError } from "commander";
10
+ import { log } from "@dbx-tools/shared-core";
11
+ import { createApp } from "@dbx-tools/appkit";
12
+ import {
13
+ defaultEnvExportFormat,
14
+ diffEnv,
15
+ formatEnvExport,
16
+ parseEnvExportFormat,
17
+ snapshotEnv,
18
+ } from "../src/env-export";
19
+
20
+ const logger = log.logger("appkit-env");
21
+
22
+ const program = new Command()
23
+ .name("dbx-tools-appkit-env")
24
+ .description("Run AppKit auto-config and print new/changed env vars.")
25
+ .option(
26
+ "-f, --format <format>",
27
+ "Output: export (POSIX shell), windows (cmd set), or json. Defaults by platform.",
28
+ )
29
+ .option("-q, --quiet", "Suppress auto-config log output (LOG_LEVEL=error)")
30
+ .action(async (opts: { format?: string; quiet?: boolean }) => {
31
+ if (opts.quiet) {
32
+ process.env.LOG_LEVEL = "error";
33
+ }
34
+
35
+ const format = opts.format ? parseEnvExportFormat(opts.format) : defaultEnvExportFormat();
36
+ logger.debug("Snapshotting env vars");
37
+ const before = snapshotEnv();
38
+ await createApp.autoConfigure({ autoConfigure: true });
39
+ const changes = diffEnv(before, snapshotEnv());
40
+
41
+ process.stdout.write(formatEnvExport(changes, format));
42
+ });
43
+
44
+ program.parseAsync(process.argv).catch((err: unknown) => {
45
+ if (err instanceof CommanderError) {
46
+ process.exit(err.exitCode);
47
+ }
48
+ process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
49
+ process.exit(1);
50
+ });
package/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+
5
+ export * as envExport from "./src/env-export";
6
+ export type { EnvExportFormat } from "./src/env-export";
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@dbx-tools/cli-appkit-env",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "git+https://github.com/reggie-db/dbx-tools.git",
6
+ "directory": "workspaces/cli/appkit-env"
7
+ },
8
+ "bin": {
9
+ "dbx-tools-appkit-env": "./bin/dbx-tools-appkit-env.mjs",
10
+ "dbxt-appkit-env": "./bin/dbx-tools-appkit-env.mjs"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^24.6.0",
14
+ "typescript": "^5.9.3"
15
+ },
16
+ "dependencies": {
17
+ "@clack/prompts": "^1.7.0",
18
+ "@databricks/appkit": "^0.43.0",
19
+ "commander": "^15.0.0",
20
+ "tsx": "^4.23.0",
21
+ "@dbx-tools/appkit": "0.3.21",
22
+ "@dbx-tools/shared-core": "0.3.21"
23
+ },
24
+ "main": "index.ts",
25
+ "license": "UNLICENSED",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "version": "0.3.21",
30
+ "types": "index.ts",
31
+ "type": "module",
32
+ "exports": {
33
+ ".": "./index.ts",
34
+ "./env-export": "./src/env-export.ts",
35
+ "./package.json": "./package.json"
36
+ },
37
+ "dbxToolsConfig": {
38
+ "tags": [
39
+ "cli"
40
+ ]
41
+ },
42
+ "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm exec projen\".",
43
+ "scripts": {
44
+ "build": "projen build",
45
+ "compile": "projen compile",
46
+ "default": "projen default",
47
+ "package": "projen package",
48
+ "post-compile": "projen post-compile",
49
+ "pre-compile": "projen pre-compile",
50
+ "test": "projen test",
51
+ "watch": "projen watch",
52
+ "projen": "projen"
53
+ }
54
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Snapshot `process.env`, diff after auto-config, and format deltas as
3
+ * eval-able shell `export` / Windows `set` lines or JSON.
4
+ *
5
+ * @module
6
+ */
7
+
8
+ export type EnvExportFormat = "export" | "windows" | "json";
9
+
10
+ /** Shallow copy of the current process environment. */
11
+ export function snapshotEnv(env: NodeJS.ProcessEnv = process.env): Record<string, string> {
12
+ const out: Record<string, string> = {};
13
+ for (const [key, value] of Object.entries(env)) {
14
+ if (value !== undefined) {
15
+ out[key] = value;
16
+ }
17
+ }
18
+ return out;
19
+ }
20
+
21
+ /**
22
+ * Keys whose values were added, changed, or cleared between snapshots. A key
23
+ * that went from a non-blank value to blank/removed is emitted with an empty
24
+ * value so it can be unset. Keys that were already blank/absent and stayed that
25
+ * way are omitted.
26
+ */
27
+ export function diffEnv(
28
+ before: Record<string, string>,
29
+ after: Record<string, string>,
30
+ ): Record<string, string> {
31
+ const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
32
+ const out: Record<string, string> = {};
33
+ for (const key of [...keys].sort()) {
34
+ // Treat a removed key as blank so a non-blank -> blank/removed transition is a delta.
35
+ const prev = before[key] ?? "";
36
+ const next = after[key] ?? "";
37
+ if (next !== prev) {
38
+ out[key] = next;
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+
44
+ export function defaultEnvExportFormat(
45
+ platform: NodeJS.Platform = process.platform,
46
+ ): EnvExportFormat {
47
+ return platform === "win32" ? "windows" : "export";
48
+ }
49
+
50
+ function escapeExportValue(value: string): string {
51
+ return value
52
+ .replace(/\\/g, "\\\\")
53
+ .replace(/"/g, '\\"')
54
+ .replace(/\$/g, "\\$")
55
+ .replace(/`/g, "\\`");
56
+ }
57
+
58
+ function escapeWindowsSetValue(value: string): string {
59
+ if (/[\s"&|<>^]/.test(value)) {
60
+ return `"${value.replace(/"/g, '""')}"`;
61
+ }
62
+ return value;
63
+ }
64
+
65
+ /**
66
+ * Format env entries for the requested output style. An empty map prints
67
+ * nothing (no trailing newline).
68
+ */
69
+ export function formatEnvExport(env: Record<string, string>, format: EnvExportFormat): string {
70
+ const entries = Object.entries(env).sort(([a], [b]) => a.localeCompare(b));
71
+ if (entries.length === 0) {
72
+ return "";
73
+ }
74
+
75
+ switch (format) {
76
+ case "json":
77
+ return `${JSON.stringify(Object.fromEntries(entries), null, 2)}\n`;
78
+ case "windows":
79
+ return (
80
+ entries.map(([key, value]) => `set ${key}=${escapeWindowsSetValue(value)}`).join("\n") +
81
+ "\n"
82
+ );
83
+ case "export":
84
+ return (
85
+ entries.map(([key, value]) => `export ${key}="${escapeExportValue(value)}"`).join("\n") +
86
+ "\n"
87
+ );
88
+ default:
89
+ throw new Error(`Unknown env export format: ${format satisfies never}`);
90
+ }
91
+ }
92
+
93
+ /** Normalize CLI format aliases to a supported export format. */
94
+ export function parseEnvExportFormat(value: string): EnvExportFormat {
95
+ const normalized = value.trim().toLowerCase();
96
+ if (
97
+ normalized === "export" ||
98
+ normalized === "shell" ||
99
+ normalized === "nix" ||
100
+ normalized === "bash"
101
+ ) {
102
+ return "export";
103
+ }
104
+ if (normalized === "windows" || normalized === "win" || normalized === "cmd") {
105
+ return "windows";
106
+ }
107
+ if (normalized === "json") {
108
+ return "json";
109
+ }
110
+ throw new Error(`Unknown format '${value}'. Use export, windows, or json.`);
111
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,43 @@
1
+ // ~~ Generated by projen. To modify, edit .projenrc.js and run "pnpm exec projen".
2
+ {
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "outDir": "lib",
6
+ "alwaysStrict": true,
7
+ "declaration": true,
8
+ "esModuleInterop": true,
9
+ "experimentalDecorators": true,
10
+ "inlineSourceMap": true,
11
+ "inlineSources": true,
12
+ "lib": [
13
+ "ES2022"
14
+ ],
15
+ "module": "ESNext",
16
+ "noEmitOnError": false,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noImplicitAny": true,
19
+ "noImplicitReturns": true,
20
+ "noImplicitThis": true,
21
+ "noUnusedLocals": true,
22
+ "noUnusedParameters": true,
23
+ "resolveJsonModule": true,
24
+ "strict": true,
25
+ "strictNullChecks": true,
26
+ "strictPropertyInitialization": true,
27
+ "stripInternal": true,
28
+ "target": "ES2022",
29
+ "types": [
30
+ "node"
31
+ ],
32
+ "moduleResolution": "bundler",
33
+ "skipLibCheck": true
34
+ },
35
+ "include": [
36
+ "src/**/*.ts",
37
+ "index.ts",
38
+ "bin/**/*.ts"
39
+ ],
40
+ "exclude": [
41
+ "node_modules"
42
+ ]
43
+ }