@embrace-io/web-sdk
Version:
1 lines • 7.88 kB
Source Map (JSON)
{"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"}