55 */
66import type { LDContext , LDEvaluationDetail } from '@launchdarkly/js-client-sdk' ;
77
8- import { LDClientLogging , LDClientTracking , MinLogger } from './api' ;
8+ import { LDClientInitialization , LDClientLogging , LDClientTracking , MinLogger } from './api' ;
99import { Breadcrumb , FeatureManagementBreadcrumb } from './api/Breadcrumb' ;
1010import { BrowserTelemetry } from './api/BrowserTelemetry' ;
1111import { BrowserTelemetryInspector } from './api/client/BrowserTelemetryInspector' ;
@@ -34,6 +34,12 @@ const GENERIC_EXCEPTION = 'generic';
3434const NULL_EXCEPTION_MESSAGE = 'exception was null or undefined' ;
3535const MISSING_MESSAGE = 'exception had no message' ;
3636
37+ // Timeout for client initialization. The telemetry SDK doesn't require that the client be initialized, but it does
38+ // require that the context processing that happens during initialization complete. This is some subset of the total
39+ // initialization time, but we don't care if initialization actually completes within the, just that the context
40+ // is available for event sending.
41+ const INITIALIZATION_TIMEOUT = 5 ;
42+
3743/**
3844 * Given a flag value ensure it is safe for analytics.
3945 *
@@ -83,6 +89,10 @@ function isLDClientLogging(client: unknown): client is LDClientLogging {
8389 return ( client as any ) . logger !== undefined ;
8490}
8591
92+ function isLDClientInitialization ( client : unknown ) : client is LDClientInitialization {
93+ return ( client as any ) . waitForInitialization !== undefined ;
94+ }
95+
8696export default class BrowserTelemetryImpl implements BrowserTelemetry {
8797 private _maxPendingEvents : number ;
8898 private _maxBreadcrumbs : number ;
@@ -98,6 +108,10 @@ export default class BrowserTelemetryImpl implements BrowserTelemetry {
98108
99109 private _logger : MinLogger ;
100110
111+ private _registrationComplete : boolean = false ;
112+
113+ // Used to ensure we only log the event dropped message once.
114+ private _clientRegistered : boolean = false ;
101115 // Used to ensure we only log the event dropped message once.
102116 private _eventsDropped : boolean = false ;
103117 // Used to ensure we only log the breadcrumb filter error once.
@@ -159,17 +173,45 @@ export default class BrowserTelemetryImpl implements BrowserTelemetry {
159173 }
160174
161175 register ( client : LDClientTracking ) : void {
176+ if ( this . _client !== undefined ) {
177+ return ;
178+ }
179+
162180 this . _client = client ;
181+
163182 // When the client is registered, we need to set the logger again, because we may be able to use the client's
164183 // logger.
165184 this . _setLogger ( ) ;
166185
167- this . _client . track ( SESSION_INIT_KEY , { sessionId : this . _sessionId } ) ;
186+ const completeRegistration = ( ) => {
187+ this . _client ?. track ( SESSION_INIT_KEY , { sessionId : this . _sessionId } ) ;
168188
169- this . _pendingEvents . forEach ( ( event ) => {
170- this . _client ?. track ( event . type , event . data ) ;
171- } ) ;
172- this . _pendingEvents = [ ] ;
189+ this . _pendingEvents . forEach ( ( event ) => {
190+ this . _client ?. track ( event . type , event . data ) ;
191+ } ) ;
192+ this . _pendingEvents = [ ] ;
193+ this . _registrationComplete = true ;
194+ } ;
195+
196+ if ( isLDClientInitialization ( client ) ) {
197+ // We don't actually need the client initialization to complete, but we do need the context processing that
198+ // happens during initialization to complete. This time will be some time greater than that, but we don't
199+ // care if initialization actually completes within the timeout.
200+
201+ // An immediately invoked async function is used to ensure that the registration method can be called synchronously.
202+ // Making the `register` method async would increase the complexity for application developers.
203+ ( async ( ) => {
204+ try {
205+ await client . waitForInitialization ( INITIALIZATION_TIMEOUT ) ;
206+ } catch {
207+ // We don't care if the initialization fails.
208+ }
209+ completeRegistration ( ) ;
210+ } ) ( ) ;
211+ } else {
212+ // TODO(EMSR-36): Figure out how to handle the 4.x implementation.
213+ completeRegistration ( ) ;
214+ }
173215 }
174216
175217 private _setLogger ( ) {
@@ -207,7 +249,9 @@ export default class BrowserTelemetryImpl implements BrowserTelemetry {
207249 return ;
208250 }
209251
210- if ( this . _client === undefined ) {
252+ if ( this . _registrationComplete ) {
253+ this . _client ?. track ( type , filteredEvent ) ;
254+ } else {
211255 this . _pendingEvents . push ( { type, data : filteredEvent } ) ;
212256 if ( this . _pendingEvents . length > this . _maxPendingEvents ) {
213257 if ( ! this . _eventsDropped ) {
@@ -221,7 +265,6 @@ export default class BrowserTelemetryImpl implements BrowserTelemetry {
221265 this . _pendingEvents . shift ( ) ;
222266 }
223267 }
224- this . _client ?. track ( type , filteredEvent ) ;
225268 }
226269
227270 captureError ( exception : Error ) : void {
0 commit comments