/** * Product identity — reads from a product.json file or falls back to env vars. * Eliminates the need for hardcoded product-config.ts files in every service. */ import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; export interface ProductIdentity { productId: string; displayName: string; licensePrefix: string; configDirName: string; envVarPrefix: string; bundleIdSuffix: string; packageName: string; } let _cached: ProductIdentity | null = null; /** * Load product identity from a JSON file or environment variables. * * @param jsonPath - Path to product.json (optional, tries common locations) * @returns Product identity object */ export function loadProductIdentity(jsonPath?: string): ProductIdentity { if (_cached) return _cached; // Try loading from file const paths = jsonPath ? [jsonPath] : [ resolve('shared/product.json'), resolve('../shared/product.json'), resolve('../../shared/product.json'), ]; for (const p of paths) { try { const raw = readFileSync(p, 'utf-8'); _cached = JSON.parse(raw) as ProductIdentity; return _cached; } catch { // Try next path } } // Fallback to env vars / defaults _cached = { productId: process.env.PRODUCT_ID || 'lysnrai', displayName: process.env.DISPLAY_NAME || 'LysnrAI', licensePrefix: process.env.LICENSE_PREFIX || 'LYSNR', configDirName: process.env.CONFIG_DIR_NAME || '.LysnrAI', envVarPrefix: process.env.ENV_VAR_PREFIX || 'LYSNR', bundleIdSuffix: process.env.BUNDLE_ID_SUFFIX || 'LysnrAI', packageName: process.env.PACKAGE_NAME || 'lysnrai', }; return _cached; } /** * Convenience: get just the product ID string. */ export function getProductId(): string { return loadProductIdentity().productId; } /** * Reset the cache (useful for testing). * @internal */ export function _resetProductIdentity(): void { _cached = null; }