/** * CoreInitializer - Unified initialization for @plyaz/core * * Provides a single entry point to initialize all core services with * environment-aware configuration. Works across all JavaScript runtimes. * * @example Backend with full config * ```typescript * import { Core } from '@plyaz/core'; * * await Core.initialize({ * envPath: '.env', * db: { adapter: 'drizzle' }, * api: { * env: 'production', * baseURL: 'https://api.example.com', * }, * }); * * // Access services * const db = Core.db; * const api = Core.api; * ``` * * @example Frontend (Nuxt/Next.js) * ```typescript * import { Core } from '@plyaz/core'; * * await Core.initialize({ * skipDb: true, * api: { baseURL: 'https://api.example.com' }, * }); * * const api = Core.api; * ``` * * @example NestJS with DI * ```typescript * // app.module.ts * import { CoreModule } from '@plyaz/core/adapters'; * import { FeatureFlagModule } from '@plyaz/core/backend/featureFlags'; * * @Module({ * imports: [ * CoreModule.forRoot({ envPath: '.env', db: { adapter: 'drizzle' } }), * FeatureFlagModule.forRoot({ provider: 'database' }), * ], * }) * export class AppModule {} * ``` */ import { ApiClientService } from '../services/ApiClientService'; import { CoreEventManager } from '../events/CoreEventManager'; import { StreamServer } from '../events/streaming'; import type { CoreRuntimeEnvironment, CoreRuntimeContext, CoreEnvVars, CoreAppContext, CoreApiInitOptions, CoreServiceRuntime, CoreInitOptionsBase as BaseCoreInitOptions, CoreServicesResultBase, CoreServiceEntry, CoreDomainServiceInstance, CoreFeatureFlagInitConfig, CoreErrorHandlerInitConfig, CoreObservabilityConfig, CoreDbServiceConfig, CoreStorageConfig, CoreNotificationConfig, StreamEndpointEntry, CoreDbServiceInstance, CoreCacheServiceInstance, CoreStorageServiceInstance, CoreNotificationServiceInstance } from '@plyaz/types/core'; import type { ObservabilityAdapter } from '@plyaz/types/observability'; import type { FeatureFlagStoreSlice } from '@plyaz/types/features'; import type { ErrorStoreActions } from '@plyaz/types/errors'; import { type StoreKey } from '@plyaz/store'; import type { RootStoreSlice, RootStoreHook } from '@plyaz/types/store'; import type { StoreApi } from 'zustand/vanilla'; /** * Core initialization options with specific types for this package */ export interface CoreInitOptions extends Omit, CoreApiInitOptions, CoreStorageConfig, CoreNotificationConfig>, 'observability' | 'skipObservability'> { /** Observability configuration */ observability?: CoreObservabilityConfig; /** Skip observability initialization */ skipObservability?: boolean; /** * Global error handler configuration. * Auto-initializes by default to catch all uncaught errors. * Set `enabled: false` to disable if you want to use your own error handling. * * @example * ```typescript * await Core.initialize({ * errorHandler: { * enabled: true, * logToConsole: true, * maxErrors: 200, * filter: (error) => error instanceof MyError, * }, * }); * ``` */ errorHandler?: CoreErrorHandlerInitConfig; /** Feature flags configuration */ featureFlags?: CoreFeatureFlagInitConfig; /** * Domain services to auto-initialize (frontend pattern). * Uses ServiceRegistry for automatic initialization. * * @example * ```typescript * await Core.initialize({ * services: [ * { service: FeatureFlagDomainService, config: { enabled: true } }, * { service: ExampleDomainService, config: { enabled: true, useRealApi: true } }, * ], * }); * * // Access via ServiceRegistry * const flags = ServiceRegistry.get('featureFlags'); * ``` */ services?: CoreServiceEntry[]; } /** * Core initialization result with specific types for this package */ export type CoreServicesResult = CoreServicesResultBase; /** * Function type for getting core domain services. * Used by entry points to inject runtime-appropriate services. * * @internal This is injected by entry points, not used directly. */ export type GetCoreDomainServicesFn = (isFrontend: boolean) => Promise; /** * Set the function to get core domain services. * Called by entry-backend.ts to inject the backend-aware service loader. * * @internal Used by entry points only. */ export declare function setGetCoreDomainServices(fn: GetCoreDomainServicesFn): void; /** * Function type for getting core stream endpoints. * Used by entry points to inject backend stream endpoints. * * @internal This is injected by entry points, not used directly. */ export type GetCoreStreamEndpointsFn = () => Promise; /** * Set the function to get core stream endpoints. * Called by entry-backend.ts to inject the backend stream endpoint loader. * * @internal Used by entry points only. */ export declare function setGetCoreStreamEndpoints(fn: GetCoreStreamEndpointsFn): void; export declare class Core { private static get initialized(); private static set initialized(value); private static get _initPromise(); private static set _initPromise(value); private static get _coreServices(); private static set _coreServices(value); /** Get or create the Core logger */ private static get logger(); /** Configure logger transport (call before first log) */ private static configureLogger; /** Get the configured logger transport */ static get loggerTransport(): 'pino' | 'console'; /** * Log a message during initialization. * Uses PackageLogger, respects verbose flag. */ private static log; /** * Log a debug message during initialization. */ private static logDebug; /** * Observability configuration */ private static _observabilityConfig; /** * Global error handler instance */ private static _errorHandler; /** * Error handler configuration */ private static _errorConfig; /** * HTTP error handler/middleware based on runtime. * Auto-created during initialization. */ private static _httpErrorHandler; /** * Event listener cleanup functions */ private static _eventCleanupFns; /** * Feature flag configuration */ private static _flagConfig; /** Full init options (from global state for configureNestApp and other runtime config) */ private static get _initOptions(); private static set _initOptions(value); /** * Root store instance (contains errors, feature flags, and all other slices) * Initialized during Core.initialize() - frontend uses React hook store, backend uses vanilla store * All store access should go through Core.rootStore - no separate stores needed */ private static _rootStore; /** * Injected store hook for frontend (set via setRootStoreHook before initialize). * This is used instead of directly importing from @plyaz/store to prevent * duplicate store instances when bundlers create multiple module copies. */ private static _injectedStoreHook; /** * Set the root store hook for frontend use. * Must be called before Core.initialize() when running in browser. * PlyazProvider calls this automatically with the store prop. * * @param store - The useRootStore hook from @plyaz/store */ static setRootStoreHook(store: RootStoreHook): void; /** * Setup environment and context */ private static setupEnvironment; /** * Initialize a service with error handling */ private static initService; /** Initialize error handler if enabled */ private static initErrorHandlerIfEnabled; /** Initialize API client if not skipped */ private static initApiIfEnabled; /** Initialize cache service if not skipped (backend-only due to ioredis dependency) */ private static initCacheIfEnabled; /** Initialize observability if not skipped */ private static initObservabilityIfEnabled; /** Initialize storage service if not skipped (backend-only) */ private static initStorageIfEnabled; /** Initialize notifications service if not skipped (backend-only) */ private static initNotificationsIfEnabled; /** * StreamServer instance (created during streaming initialization) */ private static _streamServer; /** * Get the stream server instance (if initialized) */ static get streamServer(): InstanceType | null; /** * Initialize streaming service (SSE/WebSocket real-time events) * * Sets up StreamServer and StreamRegistry with domain endpoints. * Streaming is backend-only for server-sent events. * * @param options - Core init options with streaming config */ private static initStreamingIfConfigured; /** * Initialize domain services * * Loads core domain services using the injected loader (if available), * then merges with any user-provided services. * User-provided service configs override core defaults. * * Note: Frontend entries don't inject a loader, so they only use user-provided services. */ private static initDomainServicesIfConfigured; /** * Initialize all core services */ static initialize(options?: CoreInitOptions): Promise; /** * Internal initialization logic - called only once */ private static performInitialization; /** * Get initialized domain service keys * Use Core.getService(key) or ServiceRegistry.get(key) to access individual services. * * @example * ```typescript * const keys = Core.serviceKeys; * const exampleService = Core.getService('example'); * ``` */ static get serviceKeys(): string[]; /** * All stores - unified access to ALL slices from single root store * All stores (global + domain) are slices of ONE Zustand store instance. * * **Auto-generated from STORE_KEYS** - No manual registration needed! * * **Benefits:** * - ✅ Access without hooks (store.getState()) * - ✅ Cross-slice subscriptions work automatically * - ✅ Single source of truth * - ✅ Automatically scales with new stores * * @example * const errorStore = Core.stores.error; // Global slice * const flagsStore = Core.stores.featureFlags; // Global slice * const exampleStore = Core.stores.example; // Domain slice */ static get stores(): Record; /** * Get a specific store slice by key (for ServiceRegistry injection). * Returns the actual slice from the namespaced root store. * * @param key - Store key (type-safe: 'example' | 'errors' | 'featureFlags') * @returns Specific store slice or undefined * * @example * ```typescript * const exampleSlice = Core.getDomainStore('example'); // ✅ Returns ExampleFrontendStoreSlice * const errorSlice = Core.getDomainStore('errors'); // ✅ Returns ErrorStoreSlice * const invalid = Core.getDomainStore('invalid'); // ❌ TypeScript error! * ``` */ static getDomainStore(key: K): RootStoreSlice[K] | undefined; static getDomainStore(): T | undefined; /** * Event manager for subscribing to domain events * @example Core.events.on('example:created', handler) */ static get events(): typeof CoreEventManager; /** * Get database service instance * @throws Error if not initialized */ static get db(): CoreDbServiceInstance; /** * Get API client service */ static get api(): typeof ApiClientService; /** * Get cache service instance (backend-only) * @throws Error if not initialized */ static get cache(): CoreCacheServiceInstance; /** * Get observability adapter instance * @throws Error if not initialized * * @example * ```typescript * // Record a metric * await Core.observability.recordMetric({ * type: 'counter', * name: 'api.requests', * value: 1, * tags: { endpoint: '/users' }, * }); * * // Start a span * const span = Core.observability.startSpan({ name: 'processOrder' }); * try { * // ... do work * span.setStatus('ok'); * } catch (e) { * span.recordException(e); * span.setStatus('error'); * } finally { * span.end(); * } * ``` */ static get observability(): ObservabilityAdapter; /** * Check if observability is initialized */ static get isObservabilityInitialized(): boolean; /** * Get observability configuration */ static get observabilityConfig(): CoreObservabilityConfig; /** * Get storage service instance (backend-only) * @throws Error if not initialized * * @example * ```typescript * const storage = Core.storage.getStorage(); * await storage.uploadFile({ file, filename: 'doc.pdf' }); * ``` */ static get storage(): CoreStorageServiceInstance; /** * Check if storage is initialized */ static get isStorageInitialized(): boolean; /** * Get notifications service instance (backend-only) * @throws Error if not initialized * * @example * ```typescript * const notifications = Core.notifications.getNotifications(); * await notifications.sendEmail({ to: 'user@example.com', templateId: 'welcome' }); * ``` */ static get notifications(): CoreNotificationServiceInstance; /** * Check if notifications is initialized */ static get isNotificationsInitialized(): boolean; /** * Get root store (contains all slices: errors, feature flags, etc.) * Use this to access the full store state and all slice actions globally. * * @example * ```typescript * // Access error actions * const state = Core.rootStore.getState(); * state.addError({ ... }); * * // Access flags * console.log(state.flags); * * // Subscribe to store changes * Core.rootStore.subscribe((state) => { * console.log('Errors:', state.errors); * console.log('Flags:', state.flags); * }); * ``` */ static get rootStore(): StoreApi; /** * Get global error store actions. * Provides access to the error store for querying and managing errors. * * @example * ```typescript * // Get all errors * const allErrors = useErrorStore.getState().errors; * * // Clear all errors * Core.errors.clearErrors(); * * // Dismiss an error * Core.errors.dismissError(errorId); * ``` */ static get errors(): ErrorStoreActions; /** * Get feature flags store * Provides synchronous access to feature flag values from the store. * * @example * ```typescript * // Check if a flag is enabled * const isEnabled = Core.flags.isEnabled('NEW_FEATURE'); * * // Get a flag value * const provider = Core.flags.getValue('PAYMENT_PROVIDER'); * * // Refresh flags from source * await Core.flags.refresh(); * ``` */ static get flags(): FeatureFlagStoreSlice; /** * Check if error handler is initialized */ static get isErrorHandlerInitialized(): boolean; /** * Get the HTTP error handler/middleware based on runtime. * Returns different types based on detected runtime: * * - **Express**: Error handler middleware function * ```typescript * app.use(Core.httpErrorHandler); * ``` * * - **NestJS**: Exception filter class * ```typescript * app.useGlobalFilters(new (Core.httpErrorHandler)()); * ``` * * - **Next.js**: Object with App Router and Pages Router handlers * ```typescript * // App Router * export const GET = Core.httpErrorHandler.withErrorHandler(handler); * // Pages Router * export default Core.httpErrorHandler.createNextApiErrorHandler()(handler); * ``` * * - **Node.js/Bun/Deno**: Object with handleError and createMiddleware * ```typescript * Core.httpErrorHandler.handleError(error, req, res); * ``` * * - **Browser**: null (no HTTP handler needed) * * @returns The HTTP error handler for the current runtime */ static get httpErrorHandler(): unknown; /** * Check if feature flags are initialized */ static get isFlagsInitialized(): boolean; /** * Get error handler configuration */ static get errorConfig(): CoreErrorHandlerInitConfig; /** * Get feature flag configuration */ static get flagConfig(): CoreFeatureFlagInitConfig; /** * Get loaded environment variables */ static get env(): CoreEnvVars; /** * Get detected runtime environment */ static get runtime(): CoreRuntimeEnvironment; /** * Get current app context */ static get appContext(): CoreAppContext; /** * Check if Core is initialized */ static get isInitialized(): boolean; /** * Check if running on a backend runtime */ static get isBackend(): boolean; /** * Check if running on a frontend runtime */ static get isFrontend(): boolean; /** * Get runtime context (backend, frontend, or universal) * Universal means the runtime can be either (SSR frameworks like Next.js, Nuxt, Edge) */ static getRuntimeContext(): CoreRuntimeContext; /** * Assert that the current runtime matches the expected runtime(s) * Throws an error if the runtime doesn't match * * @param expected - Expected runtime(s) to check against * @param serviceName - Name of the service for error message * @throws CorePackageError if runtime doesn't match * * @example * ```typescript * // Assert backend-only * Core.assertRuntime('backend', 'DatabaseService'); * * // Assert multiple runtimes * Core.assertRuntime(['backend', 'universal'], 'CacheService'); * ``` */ static assertRuntime(expected: CoreServiceRuntime | readonly CoreServiceRuntime[], serviceName: string): void; /** * Check if a service runtime is compatible with current runtime * Returns true/false instead of throwing * * @param expected - Expected runtime(s) to check against */ static isRuntimeCompatible(expected: CoreServiceRuntime | readonly CoreServiceRuntime[]): boolean; /** * Get initialization options (for internal use by framework adapters) * @internal */ static get initOptions(): CoreInitOptions; /** * Get a registered domain service by key. * Convenience method that wraps ServiceRegistry.get(). * * @param key - Service key (e.g., 'featureFlags', 'example') * @returns The service instance * * @example * ```typescript * const flags = Core.getService('featureFlags'); * const isEnabled = await flags.isEnabled('my-flag'); * ``` */ static getService(key: string): T; /** * Get a registered domain service by key with async initialization if needed. * Convenience method that wraps ServiceRegistry.getAsync(). * * @param key - Service key * @returns Promise resolving to the service instance */ static getServiceAsync(key: string): Promise; /** * Check if a domain service is registered. */ static hasService(key: string): boolean; /** * Reset Core (useful for testing) */ static reset(): Promise; /** * Determines whether to skip DbService initialization. * * DbService is backend-only and should never be initialized on frontend runtimes. * This method: * - Returns true (skip) if skipDb was explicitly set to true * - Returns true (skip) if running on frontend, with a warning * - Warns if someone explicitly tried to init DbService on frontend (skipDb: false) * * @param skipDb - User-provided skipDb option * @param verbose - Enable verbose logging * @returns true if DbService should be skipped */ private static shouldSkipDbService; /** * Initialize database service * Config validation is handled by DbService */ private static initializeDb; /** * Initialize API client service * Config validation is handled by ApiClientService */ private static initializeApi; /** * Initialize cache service (backend-only) * Config validation is handled by CacheService */ private static initializeCache; /** * Initialize observability service based on config. * Always includes LoggerAdapter as failover for console output. */ private static initializeObservability; /** * Initialize global error handler */ /** Create and initialize root store (includes error store + all other slices) */ private static initializeRootStore; /** * Serialize a PackageErrorLike to SerializedError format. * Used by BaseError.setEventEmitter() to convert errors for the store. */ private static serializePackageError; /** Get error store actions from root store */ private static getErrorStoreActions; /** Build global error handler config */ private static buildErrorHandlerConfig; private static initializeErrorHandler; /** * Log serialized errors with full details. */ private static logErrors; /** * Setup SYSTEM.ERROR event subscription for error store updates. * Backend: Also logs errors with full details. * Frontend: Only updates store (logging handled by store subscription). */ private static setupErrorEventSubscription; /** * Setup error store subscription for frontend logging. * Logs new errors when they're added to the store. * Only active for non-backend runtimes (browser, nextjs, nuxt, edge). */ private static setupErrorStoreSubscription; /** * Create HTTP error handler based on detected runtime. * Stores the handler in Core._httpErrorHandler for access via Core.httpErrorHandler */ private static createHttpErrorHandler; /** * Subscribe to CoreEventManager error events * Forwards entity, API, validation, database, and auth errors to the global error handler. * * NOTE: SYSTEM.ERROR is NOT subscribed here - it's handled in initializeErrorHandler() * via the BaseError.setEventEmitter() + addErrors() pattern to avoid duplicate subscriptions. * * For non-BaseError errors, domain-specific events (ENTITY.ERROR, API.REQUEST_ERROR, etc.) * are captured here. BaseError instances are skipped (they already auto-emit via SYSTEM.ERROR). * * Database errors (DATABASE.ERROR) are only subscribed on backend runtimes since * DbService is backend-only (skipDb: true on frontend). */ private static subscribeToErrorEvents; /** Handle fetch flags error and return empty flags */ private static handleFetchFlagsError; /** Create fetch flags function */ private static createFetchFlagsFn; /** * Initialize feature flags slice within root store */ private static initializeFeatureFlags; } export { ApiClientService }; //# sourceMappingURL=CoreInitializer.d.ts.map