@@ -25,2 +25,6 @@ export interface ContextTargetEndpoint { | ||
| } | ||
| export interface ClientConfig { | ||
| endpoint: string; | ||
| headers: Record<string, string>; | ||
| } | ||
| export interface ConfigStore { | ||
@@ -43,3 +47,8 @@ loadSettings(): GlobalSettings; | ||
| hasValidCredentials(contextName: string): boolean; | ||
| setVar(key: string, value: string, contextName?: string): void; | ||
| getVar(key: string, contextName?: string): string | null; | ||
| deleteVar(key: string, contextName?: string): boolean; | ||
| listVars(contextName?: string): Record<string, string>; | ||
| getClientConfig(targetName: string, contextName?: string): ClientConfig; | ||
| } | ||
| export declare function createConfigStore(toolName: string, options?: ConfigStoreOptions): ConfigStore; |
+95
-4
@@ -40,3 +40,2 @@ "use strict"; | ||
| const index_1 = require("./index"); | ||
| const DEFAULT_SETTINGS = {}; | ||
| function readJson(filePath, fallback) { | ||
@@ -48,6 +47,6 @@ if (fs.existsSync(filePath)) { | ||
| catch { | ||
| return fallback; | ||
| return JSON.parse(JSON.stringify(fallback)); | ||
| } | ||
| } | ||
| return fallback; | ||
| return JSON.parse(JSON.stringify(fallback)); | ||
| } | ||
@@ -78,3 +77,3 @@ function writeJson(filePath, data, mode) { | ||
| function loadSettings() { | ||
| return readJson(settingsPath(), DEFAULT_SETTINGS); | ||
| return readJson(settingsPath(), {}); | ||
| } | ||
@@ -201,2 +200,89 @@ function saveSettings(settings) { | ||
| } | ||
| function varsPath(ctxName) { | ||
| const varsDir = (0, index_1.resolve)(dirs, 'config', 'vars'); | ||
| if (!fs.existsSync(varsDir)) { | ||
| fs.mkdirSync(varsDir, { recursive: true }); | ||
| } | ||
| return path.join(varsDir, `${ctxName}.json`); | ||
| } | ||
| function loadVars(ctxName) { | ||
| return readJson(varsPath(ctxName), {}); | ||
| } | ||
| function saveVars(ctxName, vars) { | ||
| writeJson(varsPath(ctxName), vars); | ||
| } | ||
| function resolveContextName(contextName) { | ||
| if (contextName) | ||
| return contextName; | ||
| const settings = loadSettings(); | ||
| return settings.currentContext || null; | ||
| } | ||
| function setVar(key, value, contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) { | ||
| throw new Error('No active context. Run "context create" or "context use" first.'); | ||
| } | ||
| const vars = loadVars(ctxName); | ||
| vars[key] = value; | ||
| saveVars(ctxName, vars); | ||
| } | ||
| function getVar(key, contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) | ||
| return null; | ||
| const vars = loadVars(ctxName); | ||
| return vars[key] ?? null; | ||
| } | ||
| function deleteVar(key, contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) | ||
| return false; | ||
| const vars = loadVars(ctxName); | ||
| if (key in vars) { | ||
| delete vars[key]; | ||
| saveVars(ctxName, vars); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function listVars(contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) | ||
| return {}; | ||
| return loadVars(ctxName); | ||
| } | ||
| function getClientConfig(targetName, contextName) { | ||
| const envPrefix = toolName.toUpperCase().replace(/[^A-Z0-9]/g, '_'); | ||
| const targetSuffix = targetName.toUpperCase().replace(/[^A-Z0-9]/g, '_'); | ||
| // Tier 1: Try appstash store | ||
| const ctx = contextName ? loadContext(contextName) : getCurrentContext(); | ||
| if (ctx) { | ||
| const endpoint = getTargetEndpoint(targetName, ctx.name); | ||
| const headers = {}; | ||
| if (hasValidCredentials(ctx.name)) { | ||
| const creds = getCredentials(ctx.name); | ||
| if (creds?.token) { | ||
| headers['Authorization'] = `Bearer ${creds.token}`; | ||
| } | ||
| } | ||
| if (endpoint) { | ||
| return { endpoint, headers }; | ||
| } | ||
| } | ||
| // Tier 2: Try env vars | ||
| const envToken = process.env[`${envPrefix}_TOKEN`]; | ||
| const envEndpoint = process.env[`${envPrefix}_${targetSuffix}_ENDPOINT`] || | ||
| process.env[`${envPrefix}_ENDPOINT`]; | ||
| if (envEndpoint) { | ||
| const headers = {}; | ||
| if (envToken) { | ||
| headers['Authorization'] = `Bearer ${envToken}`; | ||
| } | ||
| return { endpoint: envEndpoint, headers }; | ||
| } | ||
| // Tier 3: Throw with actionable error | ||
| throw new Error(`No configuration found for target "${targetName}". ` + | ||
| `Set up a context with "${toolName} context create" and authenticate with "${toolName} auth", ` + | ||
| `or set ${envPrefix}_${targetSuffix}_ENDPOINT and ${envPrefix}_TOKEN environment variables.`); | ||
| } | ||
| return { | ||
@@ -216,3 +302,8 @@ loadSettings, | ||
| hasValidCredentials, | ||
| setVar, | ||
| getVar, | ||
| deleteVar, | ||
| listVars, | ||
| getClientConfig, | ||
| }; | ||
| } |
@@ -25,2 +25,6 @@ export interface ContextTargetEndpoint { | ||
| } | ||
| export interface ClientConfig { | ||
| endpoint: string; | ||
| headers: Record<string, string>; | ||
| } | ||
| export interface ConfigStore { | ||
@@ -43,3 +47,8 @@ loadSettings(): GlobalSettings; | ||
| hasValidCredentials(contextName: string): boolean; | ||
| setVar(key: string, value: string, contextName?: string): void; | ||
| getVar(key: string, contextName?: string): string | null; | ||
| deleteVar(key: string, contextName?: string): boolean; | ||
| listVars(contextName?: string): Record<string, string>; | ||
| getClientConfig(targetName: string, contextName?: string): ClientConfig; | ||
| } | ||
| export declare function createConfigStore(toolName: string, options?: ConfigStoreOptions): ConfigStore; |
+95
-4
| import * as fs from 'fs'; | ||
| import * as path from 'path'; | ||
| import { appstash, resolve } from './index'; | ||
| const DEFAULT_SETTINGS = {}; | ||
| function readJson(filePath, fallback) { | ||
@@ -11,6 +10,6 @@ if (fs.existsSync(filePath)) { | ||
| catch { | ||
| return fallback; | ||
| return JSON.parse(JSON.stringify(fallback)); | ||
| } | ||
| } | ||
| return fallback; | ||
| return JSON.parse(JSON.stringify(fallback)); | ||
| } | ||
@@ -41,3 +40,3 @@ function writeJson(filePath, data, mode) { | ||
| function loadSettings() { | ||
| return readJson(settingsPath(), DEFAULT_SETTINGS); | ||
| return readJson(settingsPath(), {}); | ||
| } | ||
@@ -164,2 +163,89 @@ function saveSettings(settings) { | ||
| } | ||
| function varsPath(ctxName) { | ||
| const varsDir = resolve(dirs, 'config', 'vars'); | ||
| if (!fs.existsSync(varsDir)) { | ||
| fs.mkdirSync(varsDir, { recursive: true }); | ||
| } | ||
| return path.join(varsDir, `${ctxName}.json`); | ||
| } | ||
| function loadVars(ctxName) { | ||
| return readJson(varsPath(ctxName), {}); | ||
| } | ||
| function saveVars(ctxName, vars) { | ||
| writeJson(varsPath(ctxName), vars); | ||
| } | ||
| function resolveContextName(contextName) { | ||
| if (contextName) | ||
| return contextName; | ||
| const settings = loadSettings(); | ||
| return settings.currentContext || null; | ||
| } | ||
| function setVar(key, value, contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) { | ||
| throw new Error('No active context. Run "context create" or "context use" first.'); | ||
| } | ||
| const vars = loadVars(ctxName); | ||
| vars[key] = value; | ||
| saveVars(ctxName, vars); | ||
| } | ||
| function getVar(key, contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) | ||
| return null; | ||
| const vars = loadVars(ctxName); | ||
| return vars[key] ?? null; | ||
| } | ||
| function deleteVar(key, contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) | ||
| return false; | ||
| const vars = loadVars(ctxName); | ||
| if (key in vars) { | ||
| delete vars[key]; | ||
| saveVars(ctxName, vars); | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function listVars(contextName) { | ||
| const ctxName = resolveContextName(contextName); | ||
| if (!ctxName) | ||
| return {}; | ||
| return loadVars(ctxName); | ||
| } | ||
| function getClientConfig(targetName, contextName) { | ||
| const envPrefix = toolName.toUpperCase().replace(/[^A-Z0-9]/g, '_'); | ||
| const targetSuffix = targetName.toUpperCase().replace(/[^A-Z0-9]/g, '_'); | ||
| // Tier 1: Try appstash store | ||
| const ctx = contextName ? loadContext(contextName) : getCurrentContext(); | ||
| if (ctx) { | ||
| const endpoint = getTargetEndpoint(targetName, ctx.name); | ||
| const headers = {}; | ||
| if (hasValidCredentials(ctx.name)) { | ||
| const creds = getCredentials(ctx.name); | ||
| if (creds?.token) { | ||
| headers['Authorization'] = `Bearer ${creds.token}`; | ||
| } | ||
| } | ||
| if (endpoint) { | ||
| return { endpoint, headers }; | ||
| } | ||
| } | ||
| // Tier 2: Try env vars | ||
| const envToken = process.env[`${envPrefix}_TOKEN`]; | ||
| const envEndpoint = process.env[`${envPrefix}_${targetSuffix}_ENDPOINT`] || | ||
| process.env[`${envPrefix}_ENDPOINT`]; | ||
| if (envEndpoint) { | ||
| const headers = {}; | ||
| if (envToken) { | ||
| headers['Authorization'] = `Bearer ${envToken}`; | ||
| } | ||
| return { endpoint: envEndpoint, headers }; | ||
| } | ||
| // Tier 3: Throw with actionable error | ||
| throw new Error(`No configuration found for target "${targetName}". ` + | ||
| `Set up a context with "${toolName} context create" and authenticate with "${toolName} auth", ` + | ||
| `or set ${envPrefix}_${targetSuffix}_ENDPOINT and ${envPrefix}_TOKEN environment variables.`); | ||
| } | ||
| return { | ||
@@ -179,3 +265,8 @@ loadSettings, | ||
| hasValidCredentials, | ||
| setVar, | ||
| getVar, | ||
| deleteVar, | ||
| listVars, | ||
| getClientConfig, | ||
| }; | ||
| } |
+1
-1
@@ -101,2 +101,2 @@ /** | ||
| export { createConfigStore } from './config-store'; | ||
| export type { ConfigStore, ConfigStoreOptions, ContextConfig, ContextCredentials, ContextTargetEndpoint, Credentials, GlobalSettings, } from './config-store'; | ||
| export type { ClientConfig, ConfigStore, ConfigStoreOptions, ContextConfig, ContextCredentials, ContextTargetEndpoint, Credentials, GlobalSettings, } from './config-store'; |
+1
-1
@@ -101,2 +101,2 @@ /** | ||
| export { createConfigStore } from './config-store'; | ||
| export type { ConfigStore, ConfigStoreOptions, ContextConfig, ContextCredentials, ContextTargetEndpoint, Credentials, GlobalSettings, } from './config-store'; | ||
| export type { ClientConfig, ConfigStore, ConfigStoreOptions, ContextConfig, ContextCredentials, ContextTargetEndpoint, Credentials, GlobalSettings, } from './config-store'; |
+2
-2
| { | ||
| "name": "appstash", | ||
| "version": "0.5.0", | ||
| "version": "0.6.0", | ||
| "author": "Constructive <developers@constructive.io>", | ||
@@ -43,3 +43,3 @@ "description": "Simple, clean application directory resolution", | ||
| ], | ||
| "gitHead": "fd9721cb17389c4e2f9847169d5a42de2f9746dd" | ||
| "gitHead": "276d21250b952f110b9d8a509616ffd1cf5871e7" | ||
| } |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
53807
16.8%1326
17.76%18
50%