Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
129 lines
3.4 KiB
JavaScript
129 lines
3.4 KiB
JavaScript
/**
|
|
* Configuration Loader - Secure Configuration Loading Pattern
|
|
*
|
|
* This implements the expert-recommended blocking initialization pattern:
|
|
* 1. loadConfig() fetches from /api/config and stores it
|
|
* 2. getConfig() returns config or throws if not loaded
|
|
* 3. Application only initializes after loadConfig() completes
|
|
*
|
|
* This prevents race conditions where components try to access config
|
|
* before it's been fetched from the server.
|
|
*/
|
|
|
|
/**
|
|
* Module-scoped variable to hold the fetched server configuration.
|
|
* @type {Object|null}
|
|
*/
|
|
let serverConfig = null;
|
|
|
|
/**
|
|
* Fetches configuration from the server and stores it.
|
|
* This MUST be called before the application initializes any components
|
|
* that depend on the configuration.
|
|
*
|
|
* @async
|
|
* @returns {Promise<void>}
|
|
* @throws {Error} If the config endpoint is unreachable or returns an error
|
|
*/
|
|
export async function loadConfig() {
|
|
// Prevent double-loading
|
|
if (serverConfig) {
|
|
console.warn('[ConfigLoader] Configuration already loaded.');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/config');
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Server returned status ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
serverConfig = await response.json();
|
|
|
|
console.log('[ConfigLoader] Configuration loaded successfully', {
|
|
dssHost: serverConfig.dssHost,
|
|
dssPort: serverConfig.dssPort,
|
|
storybookPort: serverConfig.storybookPort,
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('[ConfigLoader] Failed to load configuration:', error);
|
|
// Re-throw to be caught by the bootstrap function
|
|
throw new Error(`Failed to load server configuration: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the entire configuration object.
|
|
* MUST ONLY be called after loadConfig() has completed successfully.
|
|
*
|
|
* @returns {Object} The server configuration
|
|
* @throws {Error} If called before loadConfig() has completed
|
|
*/
|
|
export function getConfig() {
|
|
if (!serverConfig) {
|
|
throw new Error('[ConfigLoader] getConfig() called before configuration was loaded. Did you forget to await loadConfig()?');
|
|
}
|
|
return serverConfig;
|
|
}
|
|
|
|
/**
|
|
* Convenience getter for just the DSS host.
|
|
* @returns {string} The DSS host
|
|
*/
|
|
export function getDssHost() {
|
|
const config = getConfig();
|
|
return config.dssHost;
|
|
}
|
|
|
|
/**
|
|
* Convenience getter for DSS port.
|
|
* @returns {string} The DSS port
|
|
*/
|
|
export function getDssPort() {
|
|
const config = getConfig();
|
|
return config.dssPort;
|
|
}
|
|
|
|
/**
|
|
* Convenience getter for Storybook port.
|
|
* @returns {number} The Storybook port (always 6006)
|
|
*/
|
|
export function getStorybookPort() {
|
|
const config = getConfig();
|
|
return config.storybookPort;
|
|
}
|
|
|
|
/**
|
|
* Builds the full Storybook URL from config.
|
|
* Points to Storybook running on port 6006 on the current host.
|
|
*
|
|
* @returns {string} The full Storybook URL (e.g., "http://dss.overbits.luz.uy:6006")
|
|
*/
|
|
export function getStorybookUrl() {
|
|
const dssHost = getDssHost();
|
|
const protocol = window.location.protocol; // "http:" or "https:"
|
|
// Point to Storybook on port 6006
|
|
return `${protocol}//${dssHost}:6006`;
|
|
}
|
|
|
|
/**
|
|
* TESTING ONLY: Reset the configuration state
|
|
* This allows tests to load different configurations
|
|
* @internal
|
|
*/
|
|
export function __resetForTesting() {
|
|
serverConfig = null;
|
|
}
|
|
|
|
export default {
|
|
loadConfig,
|
|
getConfig,
|
|
getDssHost,
|
|
getDssPort,
|
|
getStorybookPort,
|
|
getStorybookUrl,
|
|
__resetForTesting,
|
|
};
|