sfcc-dev-mcp
Version:
MCP server for Salesforce B2C Commerce Cloud development assistance including logs, debugging, and development tools
172 lines • 6.71 kB
JavaScript
/**
* Configuration factory for SFCC MCP Server
*
* Centralized configuration management with validation and defaults.
* This factory creates SFCCConfig objects from various sources while
* leveraging secure file loading from the config module.
*/
import { existsSync } from 'fs';
import { resolve } from 'path';
import { loadSecureDwJson } from './dw-json-loader.js';
export class ConfigurationFactory {
/**
* Create configuration from various sources with proper validation
*/
static create(options) {
let config;
// Load from dw.json if path provided
if (options.dwJsonPath) {
const dwConfig = this.loadFromDwJson(options.dwJsonPath);
config = this.mapDwJsonToConfig(dwConfig);
}
else {
// Create from provided options
config = {
hostname: options.hostname ?? '',
username: options.username,
password: options.password,
clientId: options.clientId,
clientSecret: options.clientSecret,
siteId: options.siteId,
};
}
// Override with any provided options (command-line args take precedence)
if (options.hostname) {
config.hostname = options.hostname;
}
if (options.username) {
config.username = options.username;
}
if (options.password) {
config.password = options.password;
}
if (options.clientId) {
config.clientId = options.clientId;
}
if (options.clientSecret) {
config.clientSecret = options.clientSecret;
}
if (options.siteId) {
config.siteId = options.siteId;
}
this.validate(config);
return config;
}
/**
* Load configuration from dw.json file using secure file loading
*
* @param dwJsonPath - Path to the dw.json file
* @returns Parsed dw.json configuration
* @throws Error if file cannot be loaded or is invalid
*/
static loadFromDwJson(dwJsonPath) {
const resolvedPath = resolve(dwJsonPath);
if (!existsSync(resolvedPath)) {
throw new Error(`dw.json file not found at: ${resolvedPath}`);
}
// Use the secure loading function from dw-json-loader.ts
// This ensures all security validations are applied consistently
return loadSecureDwJson(dwJsonPath);
}
/**
* Map dw.json structure to SFCCConfig
*
* Transforms the dw.json format (with kebab-case properties) to the
* internal SFCCConfig format (with camelCase properties).
*
* @param dwConfig - The parsed dw.json configuration
* @returns Mapped SFCCConfig object
*/
static mapDwJsonToConfig(dwConfig) {
const config = {
hostname: dwConfig.hostname,
username: dwConfig.username,
password: dwConfig.password,
};
// Map OAuth credentials if present
if (dwConfig['client-id'] && dwConfig['client-secret']) {
config.clientId = dwConfig['client-id'];
config.clientSecret = dwConfig['client-secret'];
}
// Map site ID if present
if (dwConfig['site-id']) {
config.siteId = dwConfig['site-id'];
}
return config;
}
/**
* Validate configuration for different operating modes
*
* This validation supports both documentation-only mode (no credentials required)
* and full mode (credentials required for API access).
*
* @param config - The configuration to validate
* @throws Error if configuration is invalid for any supported mode
*/
static validate(config) {
const hasBasicAuth = config.username && config.password;
const hasOAuth = config.clientId && config.clientSecret;
const hasHostname = config.hostname && config.hostname.trim() !== '';
// Allow local mode if no credentials or hostname are provided
if (!hasBasicAuth && !hasOAuth && !hasHostname) {
// Local mode - only class documentation available
return;
}
// If hostname is provided, require credentials
if (hasHostname && !hasBasicAuth && !hasOAuth) {
throw new Error('When hostname is provided, either username/password or OAuth credentials (clientId/clientSecret) must be provided');
}
// Additional hostname validation if provided
if (hasHostname) {
const trimmedHostname = config.hostname.trim();
if (!trimmedHostname.match(/^[a-zA-Z0-9.-]+(?::[0-9]+)?$/)) {
throw new Error('Invalid hostname format in configuration');
}
}
}
/**
* Check if configuration supports specific features
*
* This method analyzes the provided configuration to determine what
* capabilities are available based on the credentials and hostname provided.
*
* @param config - The configuration to analyze
* @returns Object describing available capabilities
*/
static getCapabilities(config) {
// WebDAV/Logs can work with either basic auth OR OAuth credentials
const hasWebDAVCredentials = !!(config.username && config.password) ||
!!(config.clientId && config.clientSecret);
// OCAPI specifically requires OAuth credentials
const hasOAuthCredentials = !!(config.clientId && config.clientSecret);
// Local mode when no hostname or credentials are provided
const hasHostname = !!(config.hostname && config.hostname.trim() !== '');
const isLocalMode = !hasHostname && !hasWebDAVCredentials;
return {
canAccessLogs: hasWebDAVCredentials && hasHostname,
canAccessOCAPI: hasOAuthCredentials && hasHostname,
canAccessWebDAV: hasWebDAVCredentials && hasHostname,
canGenerateCartridges: true, // Always available since it's a local file operation
isLocalMode,
};
}
/**
* Create a configuration for local development mode
*
* This creates a minimal configuration that only provides access to
* documentation and best practices without requiring any SFCC credentials.
*
* @returns Configuration for local/documentation-only mode
*/
static createLocalMode() {
return {
hostname: '',
username: undefined,
password: undefined,
clientId: undefined,
clientSecret: undefined,
siteId: undefined,
};
}
}
//# sourceMappingURL=configuration-factory.js.map