@forwardimpact/libconfig
Advanced tools
+1
-1
| { | ||
| "name": "@forwardimpact/libconfig", | ||
| "version": "0.1.75", | ||
| "version": "0.1.77", | ||
| "description": "Environment-aware application settings — services and CLIs load configuration without custom plumbing.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+44
-27
@@ -0,1 +1,2 @@ | ||
| import { execSync } from "node:child_process"; | ||
| import path from "node:path"; | ||
@@ -51,3 +52,2 @@ import { readFile } from "node:fs/promises"; | ||
| "GITHUB_TOKEN", | ||
| "LLM_TOKEN", | ||
| "MCP_TOKEN", | ||
@@ -65,2 +65,3 @@ ]); | ||
| #storageFn; | ||
| #execSync; | ||
@@ -74,2 +75,3 @@ /** | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} storageFn - Optional storage factory function that takes basePath and returns storage instance | ||
| * @param {(command: string, options?: object) => Buffer | string} execSyncFn - Optional child_process.execSync override (for testing) | ||
| */ | ||
@@ -82,5 +84,7 @@ constructor( | ||
| storageFn = createStorage, | ||
| execSyncFn = execSync, | ||
| ) { | ||
| this.#process = process; | ||
| this.#storageFn = storageFn; | ||
| this.#execSync = execSyncFn; | ||
@@ -146,32 +150,14 @@ this.name = name; | ||
| /** @returns {string} GitHub token (GH_TOKEN, or GITHUB_TOKEN as fallback) */ | ||
| /** @returns {string} GitHub token (GH_TOKEN → GITHUB_TOKEN → `gh auth token`) */ | ||
| ghToken() { | ||
| return this.#resolve(["GH_TOKEN", "GITHUB_TOKEN"]); | ||
| try { | ||
| return this.#resolve(["GH_TOKEN", "GITHUB_TOKEN"]); | ||
| } catch { | ||
| return this.#resolveGhCli(); | ||
| } | ||
| } | ||
| /** | ||
| * @deprecated Use {@link Config#anthropicToken} instead. LLM_TOKEN is being | ||
| * phased out in favor of ANTHROPIC_API_KEY for Anthropic-backed callers. | ||
| * This method remains for OpenAI-compatible embedding APIs (libvector). | ||
| * @returns {Promise<string>} LLM API token (async for caller compatibility) | ||
| */ | ||
| async llmToken() { | ||
| return this.#resolve(["LLM_TOKEN"]); | ||
| } | ||
| /** @returns {string} LLM API base URL with trailing slashes removed */ | ||
| llmBaseUrl() { | ||
| return this.#resolve(["LLM_BASE_URL"], stripTrailingSlashes); | ||
| } | ||
| /** | ||
| * Embedding API base URL. Uses EMBEDDING_BASE_URL if set, otherwise | ||
| * falls back to LLM_BASE_URL (OpenAI-compatible /embeddings endpoint). | ||
| * @returns {string} | ||
| */ | ||
| /** @returns {string} Embedding API base URL with trailing slashes removed */ | ||
| embeddingBaseUrl() { | ||
| return this.#resolve( | ||
| ["EMBEDDING_BASE_URL", "LLM_BASE_URL"], | ||
| stripTrailingSlashes, | ||
| ); | ||
| return this.#resolve(["EMBEDDING_BASE_URL"], stripTrailingSlashes); | ||
| } | ||
@@ -355,2 +341,33 @@ | ||
| /** | ||
| * Spawns `gh auth token` and caches the result under the GH_TOKEN key. | ||
| * @returns {string} | ||
| * @private | ||
| */ | ||
| #resolveGhCli() { | ||
| if (this.#cache.has("GH_TOKEN")) return this.#cache.get("GH_TOKEN"); | ||
| let token; | ||
| try { | ||
| token = this.#execSync("gh auth token", { | ||
| encoding: "utf8", | ||
| stdio: ["ignore", "pipe", "ignore"], | ||
| env: this.#process.env, | ||
| }).trim(); | ||
| } catch { | ||
| throw new Error( | ||
| "GH_TOKEN not found in environment and `gh auth token` failed", | ||
| ); | ||
| } | ||
| if (!token) { | ||
| throw new Error( | ||
| "GH_TOKEN not found in environment and `gh auth token` returned empty", | ||
| ); | ||
| } | ||
| this.#cache.set("GH_TOKEN", token); | ||
| return token; | ||
| } | ||
| /** | ||
| * Loads environment variables from a .env file in the working directory. | ||
@@ -357,0 +374,0 @@ * Credential keys (tokens, secrets) are loaded into a private override |
+29
-1
@@ -13,2 +13,3 @@ import { createStorage } from "@forwardimpact/libstorage"; | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} storageFn - Optional storage factory function that takes basePath and returns storage instance | ||
| * @param {(command: string, options?: object) => Buffer | string} [execSyncFn] - Optional child_process.execSync override (for testing) | ||
| * @returns {Promise<Config>} Initialized Config instance | ||
@@ -22,4 +23,12 @@ */ | ||
| storageFn = createStorage, | ||
| execSyncFn, | ||
| ) { | ||
| const instance = new Config(namespace, name, defaults, process, storageFn); | ||
| const instance = new Config( | ||
| namespace, | ||
| name, | ||
| defaults, | ||
| process, | ||
| storageFn, | ||
| execSyncFn, | ||
| ); | ||
| await instance.load(); | ||
@@ -87,2 +96,21 @@ return instance; | ||
| /** | ||
| * Creates and initializes a new product configuration instance asynchronously | ||
| * @param {string} name - Name of the product configuration | ||
| * @param {object} defaults - Default configuration values | ||
| * @param {object} process - Process environment access | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} storageFn - Optional storage factory function | ||
| * @returns {Promise<Config>} Initialized Config instance for product namespace | ||
| */ | ||
| export async function createProductConfig( | ||
| name, | ||
| defaults = {}, | ||
| process = global.process, | ||
| storageFn = createStorage, | ||
| ) { | ||
| const instance = new Config("product", name, defaults, process, storageFn); | ||
| await instance.load(); | ||
| return instance; | ||
| } | ||
| /** | ||
| * Creates and initializes a Config instance for init/supervision. | ||
@@ -89,0 +117,0 @@ * Used by rc.js to bootstrap services. |
31129
4.29%506
9.05%9
12.5%