@forwardimpact/libconfig
Advanced tools
+3
-2
| { | ||
| "name": "@forwardimpact/libconfig", | ||
| "version": "0.1.82", | ||
| "version": "0.1.83", | ||
| "description": "Environment-aware application settings — services and CLIs load configuration without custom plumbing.", | ||
@@ -32,3 +32,4 @@ "keywords": [ | ||
| "@forwardimpact/libsecret": "^0.1.15", | ||
| "@forwardimpact/libstorage": "^0.1.53" | ||
| "@forwardimpact/libstorage": "^0.1.53", | ||
| "@forwardimpact/libutil": "^0.1.60" | ||
| }, | ||
@@ -35,0 +36,0 @@ "devDependencies": { |
+18
-10
| import path from "node:path"; | ||
| import { mkdir, readFile, writeFile } from "node:fs/promises"; | ||
| import { readEnvFile, updateEnvFile } from "@forwardimpact/libsecret"; | ||
| import { createDefaultRuntime } from "@forwardimpact/libutil/runtime"; | ||
@@ -23,3 +23,3 @@ import { mergeConfigFragment, mergeEnvEntries } from "./merge.js"; | ||
| * @param {object} params | ||
| * @param {string} [params.target] Absolute path; defaults to `process.cwd()`. | ||
| * @param {string} [params.target] Absolute path; defaults to `runtime.proc.cwd()`. | ||
| * @param {object} [params.fragment] Top-level keys are product-owned | ||
@@ -32,15 +32,23 @@ * namespaces; may be `{}` or omitted. | ||
| * top-level namespace names (single segment); `env` entries are bare keys. | ||
| * @param {{ runtime?: import("@forwardimpact/libutil/runtime").Runtime }} [params.deps] | ||
| * Injected collaborators. `runtime.fs` is used for all filesystem I/O; | ||
| * `runtime.proc.cwd()` resolves the default `target`. When omitted the | ||
| * production runtime is used (backward-compatible). | ||
| * @returns {Promise<void>} | ||
| */ | ||
| export async function bootstrapProject({ | ||
| target = process.cwd(), | ||
| target, | ||
| fragment = {}, | ||
| env = {}, | ||
| overwrites = {}, | ||
| deps, | ||
| } = {}) { | ||
| const configDir = path.join(target, "config"); | ||
| const { fs, proc } = deps?.runtime ?? createDefaultRuntime(); | ||
| const resolvedTarget = target ?? proc.cwd(); | ||
| const configDir = path.join(resolvedTarget, "config"); | ||
| const configPath = path.join(configDir, "config.json"); | ||
| const envPath = path.join(target, ".env"); | ||
| const envPath = path.join(resolvedTarget, ".env"); | ||
| const existingConfig = await readJsonOrEmpty(configPath); | ||
| const existingConfig = await readJsonOrEmpty(fs, configPath); | ||
| const existingEnv = await readEnvSubset(Object.keys(env), envPath); | ||
@@ -63,4 +71,4 @@ | ||
| await mkdir(configDir, { recursive: true }); | ||
| await writeFile(configPath, JSON.stringify(cfg.result, null, 2) + "\n"); | ||
| await fs.mkdir(configDir, { recursive: true }); | ||
| await fs.writeFile(configPath, JSON.stringify(cfg.result, null, 2) + "\n"); | ||
@@ -78,5 +86,5 @@ // Iterate the input `env`, not `ev.result`. Same-key-same-value writes are | ||
| async function readJsonOrEmpty(filePath) { | ||
| async function readJsonOrEmpty(fs, filePath) { | ||
| try { | ||
| const text = await readFile(filePath, "utf8"); | ||
| const text = await fs.readFile(filePath, "utf8"); | ||
| return JSON.parse(text); | ||
@@ -83,0 +91,0 @@ } catch (err) { |
+99
-44
@@ -1,7 +0,10 @@ | ||
| import { execSync } from "node:child_process"; | ||
| import path from "node:path"; | ||
| import { readFile } from "node:fs/promises"; | ||
| import { createStorage } from "@forwardimpact/libstorage"; | ||
| import { | ||
| createDefaultProc, | ||
| createDefaultRuntime, | ||
| } from "@forwardimpact/libutil/runtime"; | ||
| /** @typedef {import("@forwardimpact/libstorage").StorageInterface} StorageInterface */ | ||
| /** @typedef {import("@forwardimpact/libutil/runtime").Runtime} Runtime */ | ||
@@ -53,2 +56,35 @@ /** @param {string} url */ | ||
| /** | ||
| * Resolve a runtime collaborator from the new or legacy call shapes. | ||
| * | ||
| * New shape — callers pass `{ runtime }` as the third positional arg; the | ||
| * runtime bag carries `{ proc, fs, clock, subprocess }`. | ||
| * Legacy shape — callers pass a bare `process`-like object (with `.env` and | ||
| * `.cwd()`). This is mapped onto a minimal runtime for one deprecation cycle. | ||
| * Absent — falls back to `createDefaultRuntime()`. | ||
| * | ||
| * @param {object|undefined} runtimeOrProcess | ||
| * @returns {{ proc: object, fs: object, clock: object }} | ||
| */ | ||
| function resolveRuntime(runtimeOrProcess) { | ||
| if (!runtimeOrProcess) { | ||
| return createDefaultRuntime(); | ||
| } | ||
| // New shape: { runtime: <bag> } | ||
| if (runtimeOrProcess.runtime) { | ||
| return runtimeOrProcess.runtime; | ||
| } | ||
| // Legacy shape: bare process-like object ({ env, cwd }) | ||
| // Map onto a minimal runtime, preserving the original proc surface. | ||
| const legacyProc = | ||
| typeof runtimeOrProcess.cwd === "function" | ||
| ? runtimeOrProcess | ||
| : createDefaultProc({ source: runtimeOrProcess }); | ||
| const defaultRt = createDefaultRuntime(); | ||
| return { | ||
| ...defaultRt, | ||
| proc: legacyProc, | ||
| }; | ||
| } | ||
| /** | ||
| * Centralized configuration management class | ||
@@ -58,5 +94,5 @@ */ | ||
| // Keys containing secrets or tokens. Values from .env are loaded into | ||
| // a private map (#envOverrides) instead of process.env, so they never | ||
| // leak through child-process inheritance or process.env inspection. | ||
| // Getter methods read via #env(), which checks process.env first — | ||
| // a private map (#envOverrides) instead of proc.env, so they never | ||
| // leak through child-process inheritance or proc.env inspection. | ||
| // Getter methods read via #env(), which checks proc.env first — | ||
| // so shell-exported credentials still work; .env is the fallback. | ||
@@ -84,14 +120,26 @@ static #CREDENTIAL_KEYS = new Set([ | ||
| #storage = null; | ||
| #process; | ||
| #proc; | ||
| #fs; | ||
| #clock; | ||
| #storageFn; | ||
| #execSync; | ||
| #subprocess; | ||
| /** | ||
| * Creates a new Config instance | ||
| * @param {string} namespace - Namespace for the configuration (e.g., "service", "extension") | ||
| * @param {string} name - Name of the configuration (used for environment variable prefix) | ||
| * @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 that takes basePath and returns storage instance | ||
| * @param {(command: string, options?: object) => Buffer | string} execSyncFn - Optional child_process.execSync override (for testing) | ||
| * Creates a new Config instance. | ||
| * | ||
| * Preferred call shape (new): | ||
| * `new Config(namespace, name, defaults, { runtime }, storageFn)` | ||
| * where `runtime` is a runtime bag from `createDefaultRuntime()` or | ||
| * `createTestRuntime()`. | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `new Config(namespace, name, defaults, process, storageFn)` | ||
| * where `process` is a bare process-like object with `.env` and `.cwd()`. | ||
| * | ||
| * @param {string} namespace - Namespace for the configuration | ||
| * @param {string} name - Name of the configuration | ||
| * @param {object} [defaults] - Default configuration values | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper | ||
| * or legacy bare process object | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| */ | ||
@@ -102,9 +150,11 @@ constructor( | ||
| defaults = {}, | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| execSyncFn = execSync, | ||
| ) { | ||
| this.#process = process; | ||
| const rt = resolveRuntime(runtimeOrProcess); | ||
| this.#proc = rt.proc; | ||
| this.#fs = rt.fs; | ||
| this.#clock = rt.clock; | ||
| this.#storageFn = storageFn; | ||
| this.#execSync = execSyncFn; | ||
| this.#subprocess = rt.subprocess; | ||
@@ -121,6 +171,6 @@ this.name = name; | ||
| async load() { | ||
| this.#storage = this.#storageFn("config", null, this.#process); | ||
| this.#storage = this.#storageFn("config", null, this.#proc); | ||
| // 1. Load .env — credentials go to #envOverrides, everything else | ||
| // goes to process.env (so SERVICE_*_URL etc. are available below) | ||
| // goes to proc.env (so SERVICE_*_URL etc. are available below) | ||
| await this.#loadEnvFile(); | ||
@@ -145,3 +195,3 @@ // 2. Load config/config.json for file-based configuration | ||
| // 3. Environment overrides — SERVICE_{NAME}_{PARAM} env vars win over | ||
| // config file values. Shell process.env wins over .env #envOverrides. | ||
| // config file values. Shell proc.env wins over .env #envOverrides. | ||
| // Credential keys treat empty string as absent so a workflow ternary | ||
@@ -235,3 +285,3 @@ // emitting '' cannot clobber a .env-supplied value; non-credential | ||
| if (Date.now() >= oauth.expires_at - 5 * 60 * 1000) { | ||
| if (this.#clock.now() >= oauth.expires_at - 5 * 60 * 1000) { | ||
| try { | ||
@@ -329,3 +379,3 @@ const refreshed = await this.#refreshOAuthToken(oauth.refresh_token); | ||
| const tokenUrl = | ||
| this.#process.env.ANTHROPIC_OAUTH_TOKEN_URL || | ||
| this.#proc.env.ANTHROPIC_OAUTH_TOKEN_URL || | ||
| "https://auth.anthropic.com/oauth/token"; | ||
@@ -347,3 +397,3 @@ const res = await fetch(tokenUrl, { | ||
| refresh_token: body.refresh_token ?? refreshToken, | ||
| expires_at: Date.now() + (body.expires_in ?? 3600) * 1000, | ||
| expires_at: this.#clock.now() + (body.expires_in ?? 3600) * 1000, | ||
| }; | ||
@@ -353,5 +403,5 @@ } | ||
| /** | ||
| * Resolves an environment variable. Shell environment (process.env) | ||
| * Resolves an environment variable. Shell environment (proc.env) | ||
| * always wins; .env credential values in #envOverrides are the fallback. | ||
| * Non-credential .env keys are already on process.env (set by #loadEnvFile). | ||
| * Non-credential .env keys are already on proc.env (set by #loadEnvFile). | ||
| * @param {string} key - Environment variable name | ||
@@ -362,3 +412,3 @@ * @returns {string|undefined} | ||
| #env(key) { | ||
| return this.#process.env[key] ?? this.#envOverrides[key]; | ||
| return this.#proc.env[key] ?? this.#envOverrides[key]; | ||
| } | ||
@@ -377,3 +427,3 @@ | ||
| const isCredential = Config.#CREDENTIAL_KEYS.has(varName); | ||
| const shell = this.#process.env[varName]; | ||
| const shell = this.#proc.env[varName]; | ||
| const shellOk = isCredential | ||
@@ -396,3 +446,3 @@ ? shell !== undefined && shell !== "" | ||
| * @param {string[]} keys - Environment variable names in priority order | ||
| * @param {((v: string) => string)|null} [transform] - Optional value transform (e.g. strip slashes) | ||
| * @param {((v: string) => string)|null} [transform] - Optional value transform | ||
| * @returns {string} | ||
@@ -419,3 +469,6 @@ * @private | ||
| /** | ||
| * Spawns `gh auth token` and caches the result under the GH_TOKEN key. | ||
| * Shells out to `gh auth token` (synchronously, via the injected | ||
| * `runtime.subprocess.runSync`) and caches the result under the GH_TOKEN | ||
| * key. The sync surface is used because `ghToken()` is a synchronous | ||
| * accessor called across the codebase. | ||
| * @returns {string} | ||
@@ -429,7 +482,9 @@ * @private | ||
| try { | ||
| token = this.#execSync("gh auth token", { | ||
| encoding: "utf8", | ||
| stdio: ["ignore", "pipe", "ignore"], | ||
| env: this.#process.env, | ||
| }).trim(); | ||
| const result = this.#subprocess.runSync("gh", ["auth", "token"], { | ||
| env: this.#proc.env, | ||
| }); | ||
| if (result.exitCode !== 0) { | ||
| throw new Error(`gh auth token exited ${result.exitCode}`); | ||
| } | ||
| token = (result.stdout ?? "").trim(); | ||
| } catch { | ||
@@ -454,4 +509,4 @@ throw new Error( | ||
| * Credential keys (tokens, secrets) are loaded into a private override | ||
| * map so they never leak onto process.env. All other keys (SERVICE_*_URL, | ||
| * LLM_BASE_URL, etc.) are set directly on process.env when not already | ||
| * map so they never leak onto proc.env. All other keys (SERVICE_*_URL, | ||
| * LLM_BASE_URL, etc.) are set directly on proc.env when not already | ||
| * present. | ||
@@ -463,4 +518,4 @@ * @returns {Promise<void>} | ||
| try { | ||
| const envPath = path.join(this.#process.cwd(), ".env"); | ||
| const content = await readFile(envPath, "utf8"); | ||
| const envPath = path.join(this.#proc.cwd(), ".env"); | ||
| const content = await this.#fs.readFile(envPath, "utf8"); | ||
| for (const line of content.split("\n")) { | ||
@@ -480,14 +535,14 @@ const parsed = parseEnvLine(line); | ||
| // Credentials → private map. #env() reads them without | ||
| // exposing them on process.env. Shell env still wins at | ||
| // read time because #env() checks process.env first. | ||
| // exposing them on proc.env. Shell env still wins at | ||
| // read time because #env() checks proc.env first. | ||
| this.#envOverrides[key] = value; | ||
| } else { | ||
| // Non-credentials → process.env unconditionally. The .env file | ||
| // Non-credentials → proc.env unconditionally. The .env file | ||
| // is the persistent source of truth for SERVICE_* URLs and other | ||
| // runtime config. Supervised processes (svscan children) inherit | ||
| // their parent's process.env, so a stale inherited value is | ||
| // their parent's proc.env, so a stale inherited value is | ||
| // indistinguishable from an explicit shell export — always | ||
| // applying the .env value ensures that editing .env and | ||
| // restarting the service picks up the change. | ||
| this.#process.env[key] = value; | ||
| this.#proc.env[key] = value; | ||
| } | ||
@@ -494,0 +549,0 @@ } |
+109
-39
@@ -5,11 +5,19 @@ import { createStorage } from "@forwardimpact/libstorage"; | ||
| /** @typedef {import("@forwardimpact/libstorage").StorageInterface} StorageInterface */ | ||
| /** @typedef {import("@forwardimpact/libutil/runtime").Runtime} Runtime */ | ||
| /** | ||
| * Creates and initializes a new Config instance asynchronously | ||
| * @param {string} namespace - Namespace for the configuration (e.g., "service", "extension", "script") | ||
| * @param {string} name - Name of the configuration (used for environment variable prefix) | ||
| * @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 that takes basePath and returns storage instance | ||
| * @param {(command: string, options?: object) => Buffer | string} [execSyncFn] - Optional child_process.execSync override (for testing) | ||
| * Creates and initializes a new Config instance asynchronously. | ||
| * | ||
| * Preferred call shape (new): | ||
| * `createConfig(namespace, name, defaults, { runtime }, storageFn)` | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `createConfig(namespace, name, defaults, process, storageFn)` | ||
| * | ||
| * @param {string} namespace - Namespace for the configuration | ||
| * @param {string} name - Name of the configuration | ||
| * @param {object} [defaults] - Default configuration values | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper or | ||
| * legacy bare process object. Defaults to the production runtime when omitted. | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| * @returns {Promise<Config>} Initialized Config instance | ||
@@ -21,5 +29,4 @@ */ | ||
| defaults = {}, | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| execSyncFn, | ||
| ) { | ||
@@ -30,5 +37,4 @@ const instance = new Config( | ||
| defaults, | ||
| process, | ||
| runtimeOrProcess, | ||
| storageFn, | ||
| execSyncFn, | ||
| ); | ||
@@ -40,7 +46,15 @@ await instance.load(); | ||
| /** | ||
| * Creates and initializes a new service configuration instance asynchronously | ||
| * Creates and initializes a new service configuration instance asynchronously. | ||
| * | ||
| * Preferred call shape (new): | ||
| * `createServiceConfig(name, defaults, { runtime }, storageFn)` | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `createServiceConfig(name, defaults, process, storageFn)` | ||
| * | ||
| * @param {string} name - Name of the service 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 | ||
| * @param {object} [defaults] - Default configuration values | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper or | ||
| * legacy bare process object | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| * @returns {Promise<Config>} Initialized Config instance for service namespace | ||
@@ -51,6 +65,12 @@ */ | ||
| defaults = {}, | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| ) { | ||
| const instance = new Config("service", name, defaults, process, storageFn); | ||
| const instance = new Config( | ||
| "service", | ||
| name, | ||
| defaults, | ||
| runtimeOrProcess, | ||
| storageFn, | ||
| ); | ||
| await instance.load(); | ||
@@ -61,7 +81,15 @@ return instance; | ||
| /** | ||
| * Creates and initializes a new extension configuration instance asynchronously | ||
| * Creates and initializes a new extension configuration instance asynchronously. | ||
| * | ||
| * Preferred call shape (new): | ||
| * `createExtensionConfig(name, defaults, { runtime }, storageFn)` | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `createExtensionConfig(name, defaults, process, storageFn)` | ||
| * | ||
| * @param {string} name - Name of the extension 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 | ||
| * @param {object} [defaults] - Default configuration values | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper or | ||
| * legacy bare process object | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| * @returns {Promise<Config>} Initialized Config instance for extension namespace | ||
@@ -72,6 +100,12 @@ */ | ||
| defaults = {}, | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| ) { | ||
| const instance = new Config("extension", name, defaults, process, storageFn); | ||
| const instance = new Config( | ||
| "extension", | ||
| name, | ||
| defaults, | ||
| runtimeOrProcess, | ||
| storageFn, | ||
| ); | ||
| await instance.load(); | ||
@@ -82,7 +116,15 @@ return instance; | ||
| /** | ||
| * Creates and initializes a new script configuration instance asynchronously | ||
| * Creates and initializes a new script configuration instance asynchronously. | ||
| * | ||
| * Preferred call shape (new): | ||
| * `createScriptConfig(name, defaults, { runtime }, storageFn)` | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `createScriptConfig(name, defaults, process, storageFn)` | ||
| * | ||
| * @param {string} name - Name of the script 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 | ||
| * @param {object} [defaults] - Default configuration values | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper or | ||
| * legacy bare process object | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| * @returns {Promise<Config>} Initialized Config instance for script namespace | ||
@@ -93,6 +135,12 @@ */ | ||
| defaults = {}, | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| ) { | ||
| const instance = new Config("script", name, defaults, process, storageFn); | ||
| const instance = new Config( | ||
| "script", | ||
| name, | ||
| defaults, | ||
| runtimeOrProcess, | ||
| storageFn, | ||
| ); | ||
| await instance.load(); | ||
@@ -103,7 +151,15 @@ return instance; | ||
| /** | ||
| * Creates and initializes a new product configuration instance asynchronously | ||
| * Creates and initializes a new product configuration instance asynchronously. | ||
| * | ||
| * Preferred call shape (new): | ||
| * `createProductConfig(name, defaults, { runtime }, storageFn)` | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `createProductConfig(name, defaults, process, storageFn)` | ||
| * | ||
| * @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 | ||
| * @param {object} [defaults] - Default configuration values | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper or | ||
| * legacy bare process object | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| * @returns {Promise<Config>} Initialized Config instance for product namespace | ||
@@ -114,6 +170,12 @@ */ | ||
| defaults = {}, | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| ) { | ||
| const instance = new Config("product", name, defaults, process, storageFn); | ||
| const instance = new Config( | ||
| "product", | ||
| name, | ||
| defaults, | ||
| runtimeOrProcess, | ||
| storageFn, | ||
| ); | ||
| await instance.load(); | ||
@@ -126,11 +188,19 @@ return instance; | ||
| * Used by rc.js to bootstrap services. | ||
| * @param {object} process - Process environment access | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} storageFn - Optional storage factory function | ||
| * | ||
| * Preferred call shape (new): | ||
| * `createInitConfig({ runtime }, storageFn)` | ||
| * | ||
| * Legacy call shape (one-cycle deprecation alias — still supported): | ||
| * `createInitConfig(process, storageFn)` | ||
| * | ||
| * @param {{ runtime: Runtime }|object} [runtimeOrProcess] - Runtime bag wrapper or | ||
| * legacy bare process object | ||
| * @param {(bucket: string, type?: string, process?: object) => StorageInterface} [storageFn] | ||
| * @returns {Promise<Config>} Initialized Config instance with init() and rootDir() | ||
| */ | ||
| export async function createInitConfig( | ||
| process = global.process, | ||
| runtimeOrProcess = undefined, | ||
| storageFn = createStorage, | ||
| ) { | ||
| const instance = new Config("init", "rc", {}, process, storageFn); | ||
| const instance = new Config("init", "rc", {}, runtimeOrProcess, storageFn); | ||
| await instance.load(); | ||
@@ -137,0 +207,0 @@ return instance; |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
49454
8.44%938
16.23%1
-83.33%3
50%