{"version":3,"file":"auto-registry.mjs","sources":["../../../src/lib/feature/auto-registry.ts"],"sourcesContent":["/**\n * @file Automatic Feature Registry\n * @description Auto-discovers and registers features at build time using Vite's import.meta.glob\n */\n\nimport type { FeatureRegistryEntry } from './types';\nimport { registerFeature, getAllFeatures, clearFeatureRegistry } from './registry';\nimport { isDev } from '@/lib/core/config/env-helper';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Feature module shape expected by auto-registration\n */\nexport interface FeatureModule {\n  default?: FeatureRegistryEntry;\n  [key: string]: unknown;\n}\n\n/**\n * Feature discovery result\n */\nexport interface FeatureDiscoveryResult {\n  registered: string[];\n  failed: Array<{ path: string; error: string }>;\n  skipped: string[];\n  duration: number;\n}\n\n// ============================================================================\n// Feature Discovery\n// ============================================================================\n\n/**\n * Auto-register all features from the features directory\n * Uses Vite's import.meta.glob for build-time discovery\n */\nexport async function autoRegisterFeatures(): Promise<FeatureDiscoveryResult> {\n  const startTime = performance.now();\n  const result: FeatureDiscoveryResult = {\n    registered: [],\n    failed: [],\n    skipped: [],\n    duration: 0,\n  };\n\n  // Vite's glob import - resolved at build time\n  // This pattern discovers all feature.ts files in the features directory\n  const featureModules = import.meta.glob<FeatureModule>('/src/features/*/feature.ts', {\n    eager: false,\n  });\n\n  const entries = Object.entries(featureModules);\n\n  if (entries.length === 0) {\n    console.warn('[FeatureRegistry] No features found in /src/features/*/feature.ts');\n    result.duration = performance.now() - startTime;\n    return result;\n  }\n\n  // Load and register features in parallel\n  await Promise.all(\n    entries.map(async ([modulePath, importFn]) => {\n      try {\n        const module = await importFn();\n\n        // Look for default export (FeatureRegistryEntry)\n        const feature = module.default;\n\n        if (!feature) {\n          result.skipped.push(`${modulePath} (no default export)`);\n          return;\n        }\n\n        if (!feature.config?.metadata?.id) {\n          result.skipped.push(`${modulePath} (missing config.metadata.id)`);\n          return;\n        }\n\n        registerFeature(feature);\n        result.registered.push(feature.config.metadata.id);\n      } catch (error) {\n        result.failed.push({\n          path: modulePath,\n          error: error instanceof Error ? error.message : 'Unknown error',\n        });\n      }\n    })\n  );\n\n  result.duration = performance.now() - startTime;\n  return result;\n}\n\n/**\n * Synchronous feature registration from explicit imports\n * Use when you need deterministic registration order\n */\nexport function registerFeaturesSync(features: FeatureRegistryEntry[]): FeatureDiscoveryResult {\n  const startTime = performance.now();\n  const result: FeatureDiscoveryResult = {\n    registered: [],\n    failed: [],\n    skipped: [],\n    duration: 0,\n  };\n\n  for (const feature of features) {\n    try {\n      if (!feature.config?.metadata?.id) {\n        result.skipped.push('unknown (missing config.metadata.id)');\n        continue;\n      }\n\n      registerFeature(feature);\n      result.registered.push(feature.config.metadata.id);\n    } catch (error) {\n      result.failed.push({\n        path: feature.config?.metadata?.id || 'unknown',\n        error: error instanceof Error ? error.message : 'Unknown error',\n      });\n    }\n  }\n\n  result.duration = performance.now() - startTime;\n  return result;\n}\n\n// ============================================================================\n// Registry Utilities\n// ============================================================================\n\n/**\n * Get feature registry as a plain object (for serialization/debugging)\n */\nexport function getFeatureRegistrySnapshot(): Record<\n  string,\n  {\n    id: string;\n    name: string;\n    description?: string;\n    category?: string;\n    order?: number;\n    featureFlag?: string;\n  }\n> {\n  const features = getAllFeatures();\n  const snapshot: Record<\n    string,\n    {\n      id: string;\n      name: string;\n      description?: string;\n      category?: string;\n      order?: number;\n      featureFlag?: string;\n    }\n  > = {};\n\n  for (const feature of features) {\n    const { metadata, access } = feature.config;\n    snapshot[metadata.id] = {\n      id: metadata.id,\n      name: metadata.name,\n      ...(metadata.description !== undefined && { description: metadata.description }),\n      ...(metadata.category !== undefined && { category: metadata.category }),\n      ...(metadata.order !== undefined && { order: metadata.order }),\n      ...(access.featureFlag !== undefined && { featureFlag: access.featureFlag }),\n    };\n  }\n\n  return snapshot;\n}\n\n/**\n * Get features grouped by category\n */\nexport function getFeaturesByCategory(): Record<string, FeatureRegistryEntry[]> {\n  const features = getAllFeatures();\n  const byCategory: Record<string, FeatureRegistryEntry[]> = {};\n\n  for (const feature of features) {\n    const category = feature.config.metadata.category ?? 'general';\n    byCategory[category] ??= [];\n    byCategory[category].push(feature);\n  }\n\n  // Sort features within each category by order\n  for (const category of Object.keys(byCategory)) {\n    const categoryFeatures = byCategory[category];\n    if (categoryFeatures) {\n      categoryFeatures.sort((a, b) => {\n        const orderA = a.config.metadata.order ?? 999;\n        const orderB = b.config.metadata.order ?? 999;\n        return orderA - orderB;\n      });\n    }\n  }\n\n  return byCategory;\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialization state\n */\nlet initialized = false;\nlet initializationPromise: Promise<FeatureDiscoveryResult> | null = null;\n\n/**\n * Feature registry initialization hook\n * Safe to call multiple times - only initializes once\n */\nexport async function initializeFeatureRegistry(): Promise<FeatureDiscoveryResult> {\n  if (initialized) {\n    return { registered: [], failed: [], skipped: [], duration: 0 };\n  }\n\n  // Prevent race conditions with concurrent initialization\n  if (initializationPromise) {\n    return initializationPromise;\n  }\n\n  initializationPromise = autoRegisterFeatures().then((result) => {\n    initialized = true;\n\n    if (isDev()) {\n      console.info('[FeatureRegistry] Initialization complete:', {\n        registered: result.registered.length,\n        failed: result.failed.length,\n        skipped: result.skipped.length,\n        duration: `${result.duration.toFixed(2)}ms`,\n      });\n\n      if (result.failed.length > 0) {\n        console.warn('[FeatureRegistry] Failed to load:', result.failed);\n      }\n    }\n\n    return result;\n  });\n\n  return initializationPromise;\n}\n\n/**\n * Check if the feature registry has been initialized\n */\nexport function isFeatureRegistryInitialized(): boolean {\n  return initialized;\n}\n\n/**\n * Reset the feature registry (useful for testing)\n */\nexport function resetFeatureRegistry(): void {\n  clearFeatureRegistry();\n  initialized = false;\n  initializationPromise = null;\n}\n\n/**\n * Wait for feature registry to be ready\n * Useful when you need to ensure features are loaded before rendering\n */\nexport async function waitForFeatureRegistry(): Promise<void> {\n  if (initialized) return;\n  await initializeFeatureRegistry();\n}\n"],"names":["autoRegisterFeatures","startTime","result","entries","modulePath","importFn","feature","registerFeature","error","registerFeaturesSync","features","getFeatureRegistrySnapshot","getAllFeatures","snapshot","metadata","access","getFeaturesByCategory","byCategory","category","categoryFeatures","a","b","orderA","orderB","initialized","initializationPromise","initializeFeatureRegistry","isDev","isFeatureRegistryInitialized","resetFeatureRegistry","clearFeatureRegistry","waitForFeatureRegistry"],"mappings":"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"}