{"version":3,"file":"EmbraceDynamicConfigManager.cjs","names":["diag","getConfigURL","DEFAULT_CONFIG","LOCAL_STORAGE_REMOTE_CONFIG_KEY"],"sources":["../../../src/managers/EmbraceConfigManager/EmbraceDynamicConfigManager.ts"],"sourcesContent":["import type { DiagLogger } from '@opentelemetry/api';\nimport { diag } from '@opentelemetry/api';\nimport type {\n  DynamicConfigManager,\n  DynamicSDKConfig,\n} from '../../sdk/index.ts';\nimport type { NamespacedStorage } from '../../utils/index.ts';\nimport {\n  DEFAULT_CONFIG,\n  LOCAL_STORAGE_REMOTE_CONFIG_KEY,\n} from './constants.ts';\nimport type {\n  EmbraceDynamicConfigManagerArgs,\n  RemoteConfig,\n  StoredRemoteConfig,\n} from './types.ts';\nimport { getConfigURL } from './utils.ts';\n\nconst parseRemoteConfig = (remoteConfig: RemoteConfig): DynamicSDKConfig => {\n  const parsed: DynamicSDKConfig = {\n    samplingPct: remoteConfig.threshold,\n  };\n\n  if (remoteConfig.network_span_forwarding !== undefined) {\n    parsed.networkSpansForwardingThreshold =\n      remoteConfig.network_span_forwarding.pct_enabled;\n  }\n\n  if (remoteConfig.user_session?.max_duration_seconds !== undefined) {\n    parsed.userSessionMaxDurationSeconds =\n      remoteConfig.user_session.max_duration_seconds;\n  }\n\n  if (remoteConfig.user_session?.inactivity_timeout_seconds !== undefined) {\n    parsed.userSessionInactivityTimeoutSeconds =\n      remoteConfig.user_session.inactivity_timeout_seconds;\n  }\n\n  if (\n    remoteConfig.user_session?.web_foreground_inactivity_timeout_seconds !==\n    undefined\n  ) {\n    parsed.userSessionForegroundInactivityTimeoutSeconds =\n      remoteConfig.user_session.web_foreground_inactivity_timeout_seconds;\n  }\n\n  return parsed;\n};\n\nexport class EmbraceDynamicConfigManager implements DynamicConfigManager {\n  // Set to null if appID is not provided, in that case only rely on local config\n  private readonly _remoteConfigURL: string | null = null;\n  private readonly _diag: DiagLogger;\n  private readonly _storage: NamespacedStorage;\n  private readonly _baseConfig: DynamicSDKConfig;\n\n  private _sdkConfig: DynamicSDKConfig;\n  private _etag: string | null = null;\n\n  public constructor({\n    appID,\n    appVersion,\n    deviceId,\n    diag: diagParam = diag.createComponentLogger({\n      namespace: 'embrace-config-manager',\n    }),\n    storage,\n    // Allow users to provide a default config\n    defaultConfig = {},\n    embraceConfigURL,\n  }: EmbraceDynamicConfigManagerArgs) {\n    if (appID && appVersion && deviceId) {\n      this._remoteConfigURL = getConfigURL(\n        appID,\n        {\n          appVersion,\n          deviceId,\n          // TODO: Replace with actual OS version once we start capturing it\n          osVersion: '1',\n        },\n        embraceConfigURL,\n      );\n    }\n\n    this._diag = diagParam;\n    this._storage = storage;\n\n    const storedRemoteConfig = this._getRemoteConfigFromStorage();\n\n    if (storedRemoteConfig) {\n      this._etag = storedRemoteConfig.etag;\n    }\n\n    // Default config merged with any user-provided defaults; user-provided values take precedence.\n    // Kept around so refreshRemoteConfig can re-merge against it instead of dropping defaults.\n    this._baseConfig = {\n      ...DEFAULT_CONFIG,\n      ...defaultConfig,\n    };\n\n    this._sdkConfig = {\n      ...this._baseConfig,\n      // Stored remote config values will override both defaults and user-provided defaults\n      ...(storedRemoteConfig\n        ? parseRemoteConfig(storedRemoteConfig.config)\n        : {}),\n    };\n  }\n\n  public setConfig(config: Partial<DynamicSDKConfig>): void {\n    this._sdkConfig = {\n      ...this._sdkConfig,\n      ...config,\n    };\n  }\n\n  public getConfig(): DynamicSDKConfig {\n    return this._sdkConfig;\n  }\n\n  // No-op if not sending data to embrace\n  public async refreshRemoteConfig(): Promise<void> {\n    if (!this._remoteConfigURL) {\n      return;\n    }\n\n    try {\n      const remoteConfigResponse = await this._fetchRemoteConfig(\n        this._remoteConfigURL,\n      );\n\n      if (!remoteConfigResponse) {\n        this._diag.debug('No changes in remote config, skipping update');\n        return;\n      }\n\n      const [remoteConfig, etag] = remoteConfigResponse;\n      this._storage.setItem(\n        LOCAL_STORAGE_REMOTE_CONFIG_KEY,\n        JSON.stringify({\n          config: remoteConfig,\n          etag,\n        } as StoredRemoteConfig),\n      );\n\n      this._sdkConfig = {\n        ...this._baseConfig,\n        ...parseRemoteConfig(remoteConfig),\n      };\n      this._etag = etag;\n    } catch (error: unknown) {\n      this._diag.warn(\n        `Failed to refresh remote config: ${error instanceof Error ? error.message : String(error)}`,\n      );\n    }\n  }\n\n  private _getRemoteConfigFromStorage(): StoredRemoteConfig | null {\n    const configString = this._storage.getItem(LOCAL_STORAGE_REMOTE_CONFIG_KEY);\n    if (!configString) {\n      return null;\n    }\n\n    try {\n      return JSON.parse(configString) as StoredRemoteConfig;\n    } catch (error) {\n      this._diag.warn(\n        `Failed to parse remote config from storage: ${error instanceof Error ? error.message : String(error)}`,\n      );\n\n      return null;\n    }\n  }\n\n  private async _fetchRemoteConfig(\n    url: string,\n  ): Promise<[RemoteConfig, string | null] | null> {\n    const response = await fetch(url, {\n      headers: this._etag ? { 'If-None-Match': this._etag } : {},\n    });\n\n    const etag = response.headers.get('etag');\n\n    // Nothing changed, return null\n    if (response.status === 304) {\n      return null;\n    }\n\n    if (!response.ok) {\n      this._diag.warn(\n        `Failed to fetch remote config from ${url}: ${response.statusText}`,\n      );\n\n      return null;\n    }\n\n    const remoteConfig = (await response.json()) as RemoteConfig;\n\n    return [remoteConfig, etag];\n  }\n}\n"],"mappings":";;;;;AAkBA,MAAM,qBAAqB,iBAAiD;CAC1E,MAAM,SAA2B,EAC/B,aAAa,aAAa,UAC5B;CAEA,IAAI,aAAa,4BAA4B,KAAA,GAC3C,OAAO,kCACL,aAAa,wBAAwB;CAGzC,IAAI,aAAa,cAAc,yBAAyB,KAAA,GACtD,OAAO,gCACL,aAAa,aAAa;CAG9B,IAAI,aAAa,cAAc,+BAA+B,KAAA,GAC5D,OAAO,sCACL,aAAa,aAAa;CAG9B,IACE,aAAa,cAAc,8CAC3B,KAAA,GAEA,OAAO,gDACL,aAAa,aAAa;CAG9B,OAAO;AACT;AAEA,IAAa,8BAAb,MAAyE;CAEvE,mBAAmD;CACnD;CACA;CACA;CAEA;CACA,QAA+B;CAE/B,YAAmB,EACjB,OACA,YACA,UACA,MAAM,YAAYA,mBAAAA,KAAK,sBAAsB,EAC3C,WAAW,yBACb,CAAC,GACD,SAEA,gBAAgB,CAAC,GACjB,oBACkC;EAClC,IAAI,SAAS,cAAc,UACzB,KAAK,mBAAmBC,4CAAAA,aACtB,OACA;GACE;GACA;GAEA,WAAW;EACb,GACA,gBACF;EAGF,KAAK,QAAQ;EACb,KAAK,WAAW;EAEhB,MAAM,qBAAqB,KAAK,4BAA4B;EAE5D,IAAI,oBACF,KAAK,QAAQ,mBAAmB;EAKlC,KAAK,cAAc;GACjB,GAAGC,gDAAAA;GACH,GAAG;EACL;EAEA,KAAK,aAAa;GAChB,GAAG,KAAK;GAER,GAAI,qBACA,kBAAkB,mBAAmB,MAAM,IAC3C,CAAC;EACP;CACF;CAEA,UAAiB,QAAyC;EACxD,KAAK,aAAa;GAChB,GAAG,KAAK;GACR,GAAG;EACL;CACF;CAEA,YAAqC;EACnC,OAAO,KAAK;CACd;CAGA,MAAa,sBAAqC;EAChD,IAAI,CAAC,KAAK,kBACR;EAGF,IAAI;GACF,MAAM,uBAAuB,MAAM,KAAK,mBACtC,KAAK,gBACP;GAEA,IAAI,CAAC,sBAAsB;IACzB,KAAK,MAAM,MAAM,8CAA8C;IAC/D;GACF;GAEA,MAAM,CAAC,cAAc,QAAQ;GAC7B,KAAK,SAAS,QACZC,gDAAAA,iCACA,KAAK,UAAU;IACb,QAAQ;IACR;GACF,CAAuB,CACzB;GAEA,KAAK,aAAa;IAChB,GAAG,KAAK;IACR,GAAG,kBAAkB,YAAY;GACnC;GACA,KAAK,QAAQ;EACf,SAAS,OAAgB;GACvB,KAAK,MAAM,KACT,oCAAoC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC3F;EACF;CACF;CAEA,8BAAiE;EAC/D,MAAM,eAAe,KAAK,SAAS,QAAQA,gDAAAA,+BAA+B;EAC1E,IAAI,CAAC,cACH,OAAO;EAGT,IAAI;GACF,OAAO,KAAK,MAAM,YAAY;EAChC,SAAS,OAAO;GACd,KAAK,MAAM,KACT,+CAA+C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACtG;GAEA,OAAO;EACT;CACF;CAEA,MAAc,mBACZ,KAC+C;EAC/C,MAAM,WAAW,MAAM,MAAM,KAAK,EAChC,SAAS,KAAK,QAAQ,EAAE,iBAAiB,KAAK,MAAM,IAAI,CAAC,EAC3D,CAAC;EAED,MAAM,OAAO,SAAS,QAAQ,IAAI,MAAM;EAGxC,IAAI,SAAS,WAAW,KACtB,OAAO;EAGT,IAAI,CAAC,SAAS,IAAI;GAChB,KAAK,MAAM,KACT,sCAAsC,IAAI,IAAI,SAAS,YACzD;GAEA,OAAO;EACT;EAIA,OAAO,CAAC,MAFoB,SAAS,KAAK,GAEpB,IAAI;CAC5B;AACF"}