/** * Configuration types for the B2C SDK. * * This module defines the canonical configuration format and interfaces * for the configuration resolution system. * * @module config/types */ import type { AuthMethod, AuthStrategy } from '../auth/types.js'; import type { B2CInstance } from '../instance/index.js'; import type { SafetyLevel } from '../safety/safety-middleware.js'; import type { SafetyRule } from '../safety/types.js'; /** * A value that may be synchronous or a Promise. * * Used in the {@link ConfigSource} interface so that sources can return * results either synchronously or asynchronously. The resolver normalizes * both forms with `await`. */ export type MaybePromise = T | Promise; /** * Configured content library entry. When present in a {@link NormalizedConfig.libraries} * array, it identifies a library by ID and optionally marks it as site-private. * * The simpler `string` form (an ID alone) is equivalent to `{id, siteLibrary: false}`. */ export interface LibraryEntry { /** Library ID (or site ID for site-private libraries) */ id: string; /** True if this library is site-private (lookup uses the site-library API) */ siteLibrary?: boolean; } /** * Normalized B2C configuration with camelCase fields. * * This is the canonical intermediate format that all configuration sources * map to. It provides a consistent interface regardless of the source format * (dw.json uses kebab-case, env vars use SCREAMING_SNAKE_CASE, etc.). */ export interface NormalizedConfig { /** B2C instance hostname */ hostname?: string; /** Separate hostname for WebDAV operations (if different from main hostname) */ webdavHostname?: string; /** Code version for deployments */ codeVersion?: string; /** Username for Basic auth (WebDAV) */ username?: string; /** Password/access-key for Basic auth (WebDAV) */ password?: string; /** OAuth client ID */ clientId?: string; /** OAuth client secret */ clientSecret?: string; /** OAuth scopes */ scopes?: string[]; /** Allowed authentication methods in priority order */ authMethods?: AuthMethod[]; /** Account Manager hostname for OAuth (default: account.demandware.com) */ accountManagerHost?: string; /** Path to JWT certificate file (cert.pem) for JWT authentication */ jwtCertPath?: string; /** Path to JWT private key file (key.pem) for JWT authentication */ jwtKeyPath?: string; /** Optional passphrase for encrypted JWT private key */ jwtPassphrase?: string; /** SLAS client ID for shopper authentication */ slasClientId?: string; /** SLAS client secret for private shopper clients */ slasClientSecret?: string; /** B2C Commerce site/channel ID */ siteId?: string; /** SCAPI short code */ shortCode?: string; /** Tenant/Organization ID for SCAPI */ tenantId?: string; /** ODS API hostname */ sandboxApiHost?: string; /** Default ODS realm for sandbox operations */ realm?: string; /** MRT project slug */ mrtProject?: string; /** MRT environment name (e.g., staging, production) */ mrtEnvironment?: string; /** MRT API key */ mrtApiKey?: string; /** MRT API origin URL override */ mrtOrigin?: string; /** Whether to auto-start code upload/sync in IDE extensions */ autoUpload?: boolean; /** Cartridge names to include in deploy/watch operations */ cartridges?: string[]; /** Default content library ID for content export/list commands */ contentLibrary?: string; /** Catalog IDs for WebDAV browsing */ catalogs?: string[]; /** * Library IDs for WebDAV browsing and the Content Libraries tree. * * Accepts either a string array (all treated as shared libraries) or an * array of {@link LibraryEntry} objects that can mark individual libraries * as site-private. Both forms can be mixed in the same array. * * @example * ["RefArch", { id: "homepage", siteLibrary: true }] */ libraries?: (string | LibraryEntry)[]; /** * JSON dot-paths for asset extraction from component data during * content library parsing. Used by `content export` and the VS Code * content library browser. Defaults to `['image.path']` when unset. */ assetQuery?: string[]; /** Optional CIP analytics host override */ cipHost?: string; /** * Allowlist of documentation categories to expose from the `docs` search/read * surface (CLI `b2c docs`, MCP docs tools). When set, bounds the entire * available corpus to these categories; unset means no restriction (full * corpus). Category names are validated by `resolveEnabledCategories` in the * docs module (unknown names are dropped with a warning). * * Sourced from `docs-categories` (dw.json), `SFCC_DOCS_CATEGORIES` (env), or * `docsCategories` (package.json). A CLI `--topics` / MCP `--docs-topics` flag, * when provided, overrides this config value. * * @example ["script-api", "job-step", "help-admin", "tooling"] */ docsCategories?: string[]; /** Instance name (from multi-config supporting sources) */ instanceName?: string; /** Starting directory for config file search and project-relative operations */ projectDirectory?: string; /** @deprecated Use projectDirectory instead */ workingDirectory?: string; /** Path to PKCS12 certificate file for client mTLS (two-factor auth) */ certificate?: string; /** Passphrase for the certificate */ certificatePassphrase?: string; /** Whether to skip SSL/TLS certificate verification (self-signed certs) */ selfSigned?: boolean; /** Safety configuration for this instance */ safety?: { /** Safety level */ level?: SafetyLevel; /** When true, level-blocked operations require confirmation instead of hard-blocking */ confirm?: boolean; /** Ordered safety rules. First matching rule wins. */ rules?: SafetyRule[]; }; } /** * Warning codes for configuration resolution. */ export type ConfigWarningCode = 'HOSTNAME_MISMATCH' | 'DEPRECATED_FIELD' | 'MISSING_REQUIRED' | 'SOURCE_ERROR'; /** * A warning generated during configuration resolution. */ export interface ConfigWarning { /** Warning code for programmatic handling */ code: ConfigWarningCode; /** Human-readable warning message */ message: string; /** Additional details about the warning */ details?: Record; } /** * Information about a configuration source that contributed to resolution. */ export interface ConfigSourceInfo { /** Human-readable name of the source */ name: string; /** Location of the source (file path, keychain entry, URL, etc.) */ location?: string; /** All fields that this source provided values for */ fields: (keyof NormalizedConfig)[]; /** Fields that were not used because a higher priority source already provided them */ fieldsIgnored?: (keyof NormalizedConfig)[]; } /** * Result of configuration resolution. */ export interface ConfigResolutionResult { /** The resolved configuration */ config: NormalizedConfig; /** Warnings generated during resolution */ warnings: ConfigWarning[]; /** Information about which sources contributed to the config */ sources: ConfigSourceInfo[]; } /** * Options for configuration resolution. */ export interface ResolveConfigOptions { /** Named instance for supporting ConfigSources */ instance?: string; /** Explicit path to config file (defaults to auto-discover) */ configPath?: string; /** Starting directory for config file search */ projectDirectory?: string; /** @deprecated Use projectDirectory instead */ workingDirectory?: string; /** Whether to apply hostname mismatch protection (default: true) */ hostnameProtection?: boolean; /** Cloud origin for ~/.mobify lookup (MRT) */ cloudOrigin?: string; /** Path to custom MRT credentials file (overrides default ~/.mobify) */ credentialsFile?: string; /** Account Manager hostname for OAuth (passed to plugins for host-specific config) */ accountManagerHost?: string; /** * Custom sources to add BEFORE default sources (higher priority). * These sources can override values from dw.json and ~/.mobify. */ sourcesBefore?: ConfigSource[]; /** * Custom sources to add AFTER default sources (lower priority). * These sources fill in gaps left by dw.json and ~/.mobify. */ sourcesAfter?: ConfigSource[]; /** Replace default sources entirely (instead of appending) */ replaceDefaultSources?: boolean; } /** * Result of loading configuration from a source. */ export interface ConfigLoadResult { /** The loaded configuration */ config: NormalizedConfig; /** * Location of the source (for diagnostics). * May be a file path, keychain entry, URL, or other identifier. */ location?: string; } /** * A configuration source that can contribute config values. * * Implement this interface to create custom configuration sources. * Sources are called in order, and later sources can override earlier ones. * * @example * ```typescript * import type { ConfigSource, ConfigLoadResult, ResolveConfigOptions } from '@salesforce/b2c-tooling-sdk/config'; * * class MyCustomSource implements ConfigSource { * name = 'my-custom-source'; * * async load(options: ResolveConfigOptions): Promise { * // Load config from your custom source (sync return values also work) * return { config: { hostname: 'example.com' }, location: '/path/to/config' }; * } * } * ``` */ export interface ConfigSource { /** Human-readable name for diagnostics */ name: string; /** * Priority for source ordering. Lower numbers = higher priority. * * Recommended ranges: * - < 0: Before built-in sources (override defaults) * - 0: Built-in sources (DwJsonSource, MobifySource) * - 1-999: After built-in sources (fill gaps) * - 1000: Lowest priority (PackageJsonSource) * * @default 0 */ priority?: number; /** * Load configuration from this source. * * @param options - Resolution options * @returns Config and location from this source, or undefined if source not available. * May return synchronously or as a Promise. */ load(options: ResolveConfigOptions): MaybePromise; /** * List all instances from this source. * @param options - Resolution options * @returns Array of instance info objects. May return synchronously or as a Promise. */ listInstances?(options?: ResolveConfigOptions): MaybePromise; /** * Create a new instance in this source. * @param options - Creation options including name and config */ createInstance?(options: CreateInstanceOptions & ResolveConfigOptions): MaybePromise; /** * Remove an instance from this source. * @param name - Instance name to remove * @param options - Resolution options */ removeInstance?(name: string, options?: ResolveConfigOptions): MaybePromise; /** * Set an instance as active. * @param name - Instance name to set as active * @param options - Resolution options */ setActiveInstance?(name: string, options?: ResolveConfigOptions): MaybePromise; /** * Fields this source can securely store (e.g., ['password', 'clientSecret']). */ credentialFields?: (keyof NormalizedConfig)[]; /** * Store a credential value for an instance. * @param instanceName - Instance name * @param field - Config field to store * @param value - Value to store * @param options - Resolution options */ storeCredential?(instanceName: string, field: keyof NormalizedConfig, value: string, options?: ResolveConfigOptions): MaybePromise; /** * Remove a credential for an instance. * @param instanceName - Instance name * @param field - Config field to remove * @param options - Resolution options */ removeCredential?(instanceName: string, field: keyof NormalizedConfig, options?: ResolveConfigOptions): MaybePromise; } /** * Options for creating OAuth auth strategy. */ export interface CreateOAuthOptions { /** Allowed OAuth methods (default: ['client-credentials', 'implicit']) */ allowedMethods?: AuthMethod[]; /** Additional OAuth scopes to request beyond those in config */ scopes?: string[]; /** Override redirect URI for implicit OAuth flow (e.g., for port forwarding in remote environments) */ redirectUri?: string; /** Custom browser opener for implicit OAuth flow. Receives the authorization URL. */ openBrowser?: (url: string) => Promise; } /** * Information about a configured instance. */ export interface InstanceInfo { /** Instance name */ name: string; /** B2C instance hostname */ hostname?: string; /** Whether this instance is currently active */ active?: boolean; /** Source name for display */ source: string; /** Location (file path, etc.) */ location?: string; } /** * Options for creating an instance. */ export interface CreateInstanceOptions { /** Instance name */ name: string; /** Configuration values for the instance */ config: Partial; /** Whether to set as active instance */ setActive?: boolean; } /** * Result of configuration resolution with factory methods. * * Provides both raw configuration values and factory methods for creating * B2C SDK objects (B2CInstance, AuthStrategy, MrtClient) based on the * resolved configuration. Use the `has*` methods to check availability before * calling factory methods, which throw errors if required configuration is missing. * * @example * ```typescript * import { resolveConfig } from '@salesforce/b2c-tooling-sdk/config'; * * const config = resolveConfig({ * hostname: process.env.SFCC_SERVER, * clientId: process.env.SFCC_CLIENT_ID, * }); * * if (config.hasB2CInstanceConfig()) { * const instance = config.createB2CInstance(); * await instance.webdav.propfind('Cartridges'); * } * * if (config.hasMrtConfig()) { * const mrtAuth = config.createMrtAuth(); * } * ``` */ export interface ResolvedB2CConfig { /** Raw configuration values */ readonly values: NormalizedConfig; /** Warnings generated during resolution */ readonly warnings: ConfigWarning[]; /** Information about which sources contributed to the config */ readonly sources: ConfigSourceInfo[]; /** * Check if B2C instance configuration is available. * Requires: hostname */ hasB2CInstanceConfig(): boolean; /** * Check if MRT configuration is available. * Requires: mrtApiKey */ hasMrtConfig(): boolean; /** * Check if OAuth configuration is available. * Requires: clientId */ hasOAuthConfig(): boolean; /** * Check if Basic auth configuration is available. * Requires: username and password */ hasBasicAuthConfig(): boolean; /** * Creates a B2CInstance from the resolved configuration. * @param options - Options for implicit OAuth (redirectUri, openBrowser) * @throws Error if hostname is not configured */ createB2CInstance(options?: Pick): B2CInstance; /** * Creates a Basic auth strategy. * @throws Error if username or password is not configured */ createBasicAuth(): AuthStrategy; /** * Creates an OAuth auth strategy. * @param options - OAuth options (allowed methods) * @throws Error if clientId is not configured */ createOAuth(options?: CreateOAuthOptions): AuthStrategy; /** * Creates an MRT auth strategy (API key). * @throws Error if mrtApiKey is not configured */ createMrtAuth(): AuthStrategy; /** * Creates a WebDAV auth strategy. * Prefers Basic auth if available, falls back to OAuth. * @throws Error if neither Basic auth nor OAuth is configured */ createWebDavAuth(): AuthStrategy; }