import type { ResolvedRudderClientOptions, RudderClientOptions, } from './RudderClientOptions.js'; import { RudderError, SDK_ERROR_INVALID_OPTIONS } from './RudderError.js'; import type { RudderContext } from '../core/context.js'; import { request, type RequestContext, type RequestOptions } from '../transport/request.js'; import { createDefaultTokenStore } from '../token/TokenStore.js'; import { AuthService } from '../auth/AuthService.js'; import { SyncEngine } from '../state/SyncEngine.js'; import { PlayerDomain } from '../domains/PlayerDomain.js'; import { CatalogDomain } from '../domains/CatalogDomain.js'; import { StorageDomain } from '../domains/StorageDomain.js'; import { ModulesDomain } from '../domains/ModulesDomain.js'; export const DEFAULT_BASE_URL = 'https://api.rudder.build'; type ManagedDomain = { invalidate(): void; reset(): void }; export class RudderClient { public readonly options: ResolvedRudderClientOptions; public readonly auth: AuthService; public readonly player: PlayerDomain; public readonly catalog: CatalogDomain; public readonly storage: StorageDomain; public readonly modules: ModulesDomain; private readonly syncEngine: SyncEngine; private readonly ctx: RudderContext; private readonly transportContext: RequestContext; private runtimeStarted = false; constructor(options: RudderClientOptions) { if (!options.projectKey) { throw new RudderError('RudderClient: projectKey is required', { code: SDK_ERROR_INVALID_OPTIONS, }); } this.options = { ...options, baseUrl: options.baseUrl || DEFAULT_BASE_URL, tokenStore: options.tokenStore ?? createDefaultTokenStore(), }; this.transportContext = { baseUrl: this.options.baseUrl, tokenStore: this.options.tokenStore, requestTimeoutMs: this.options.requestTimeoutMs, appVersion: this.options.appVersion, refreshState: {}, onAuthFailure: () => { this.auth.notifySessionExpired(); this.stopRuntimeAfterLogout(); }, }; this.ctx = { options: this.options, request: ( method: string, path: string, body?: unknown, options?: RequestOptions, ) => request(method, path, body, this.transportContext, options), }; const ctx = this.ctx; this.player = new PlayerDomain(ctx); this.catalog = new CatalogDomain(ctx); this.storage = new StorageDomain(ctx); this.modules = new ModulesDomain(ctx); this.syncEngine = new SyncEngine( ctx, [this.player, this.catalog, this.storage], this.options.syncIntervalMs, ); this.auth = new AuthService( ctx, () => this.startRuntimeAfterLogin(), () => this.stopRuntimeAfterLogout(), ); } dispose(): void { this.syncEngine.stop(); this.resetAll(); this.runtimeStarted = false; } async startRuntimeAfterLogin(): Promise { if (this.runtimeStarted) { this.invalidateAll(); } this.runtimeStarted = true; await Promise.all([this.player.load(), this.catalog.load()]); this.syncEngine.start(); } stopRuntimeAfterLogout(): void { this.runtimeStarted = false; this.syncEngine.stop(); this.resetAll(); } private get managedDomains(): ManagedDomain[] { return [this.player, this.catalog, this.storage]; } private invalidateAll(): void { for (const domain of this.managedDomains) domain.invalidate(); } private resetAll(): void { for (const domain of this.managedDomains) domain.reset(); } } export function createClient(options: RudderClientOptions): RudderClient { return new RudderClient(options); }