import { Identify } from '@amplitude/identify' ; import { Client , Event , Middleware , MiddlewareExtra , Options , Response , Retry , SKIPPED_RESPONSE , } from '@amplitude/types' ; import { logger , isNodeEnv , isValidEvent } from '@amplitude/utils' ; import { RetryHandler } from './retry/defaultRetry' ; import { SDK_NAME , SDK_VERSION , DEFAULT_OPTIONS } from './constants' ; import { MiddlewareRunner } from './middleware/middlewareRunner' ; export class NodeClient implements Client < Options > { /** Project Api Key */ protected readonly _apiKey : string ; /** Options for the client. */ protected readonly _options : Options ; private _events : Event [ ] = [ ] ; private _responseListeners : Array < { resolve : ( response : Response ) => void ; reject : ( err : Error ) => void ; } > = [ ] ; private readonly _transportWithRetry : Retry ; private _flushTimer : NodeJS . Timeout | null = null ; private readonly _middlewareRunner : MiddlewareRunner ; param apiKey param options public constructor ( apiKey , options = { } ) { this . _apiKey = apiKey ; this . _options = Object . assign ( { } , DEFAULT_OPTIONS , options ) ; this . _setUpLogging ( ) ; this . _transportWithRetry = this . _options . retryClass ?? this . _setupDefaultTransport ( ) ; this . _middlewareRunner = new MiddlewareRunner ( ) ; if ( ! isNodeEnv ( ) ) { logger . warn ( '@amplitude/node initialized in a non-node environment and will not work. If you are planning to add Amplitude to a browser environment, please use amplitude-js' ) ; } } inheritDoc public getOptions ( ) { return this . _options ; } inheritDoc public async flush ( ) { if ( this . _flushTimer !== null ) { clearTimeout ( this . _flushTimer ) ; this . _flushTimer = null ; } if ( this . _events . length === 0 ) { return SKIPPED_RESPONSE ; } const responseListeners = this . _responseListeners ; this . _responseListeners = [ ] ; const eventsToSend = this . _events ; this . _events = [ ] ; try { const response = await this . _transportWithRetry . sendEventsWithRetry ( eventsToSend ) ; responseListeners . forEach ( ( { resolve } ) => resolve ( response ) ) ; return response ; } catch ( err ) { responseListeners . forEach ( ( { reject } ) => reject ( err ) ) ; throw err ; } } inheritDoc public async logEvent ( event , extra ? ) { if ( this . _options . optOut ) { return await Promise . resolve ( SKIPPED_RESPONSE ) ; } this . _annotateEvent ( event ) ; this . _observeEvent ( event ) ; let middlewareCompleted = false ; this . _middlewareRunner . run ( { event , extra } , ( ) => { middlewareCompleted = true ; } ) ; if ( ! middlewareCompleted ) { logger . warn ( 'Middleware chain skipped logEvent action.' ) ; return await Promise . resolve ( SKIPPED_RESPONSE ) ; } if ( ! isValidEvent ( event ) ) { logger . warn ( 'Found invalid event - skipping logEvent action.' ) ; return await Promise . resolve ( SKIPPED_RESPONSE ) ; } return await new Promise ( ( resolve , reject ) => { this . _events . push ( event ) ; this . _responseListeners . push ( { resolve , reject } ) ; if ( this . _events . length >= this . _options . maxCachedEvents ) { this . flush ( ) ; } else { if ( this . _flushTimer === null ) { this . _flushTimer = setTimeout ( ( ) => { this . flush ( ) ; } , this . _options . uploadIntervalInSec * 1000 ) ; } } } ) ; } param userId param deviceId param identify returns public async identify ( userId , deviceId , identify ) { if ( ! ( identify instanceof Identify ) ) { logger . warn ( 'Invalid Identify object. Skipping operation.' ) ; return await Promise . resolve ( SKIPPED_RESPONSE ) ; } const identifyEvent = identify . identifyUser ( userId , deviceId ) ; return await this . logEvent ( identifyEvent ) ; } public addEventMiddleware ( middleware ) { this . _middlewareRunner . add ( middleware ) ; return this ; } /** Add platform dependent field onto event. */ private _annotateEvent ( event ) { event . library = `${ SDK_NAME }/${ SDK_VERSION }` ; } /** Merge plan field into event */ private _observeEvent ( event ) { if ( typeof this . _options . plan !== 'undefined' ) { event . plan = { ... this . _options . plan , ... event . plan , } ; } } private _setupDefaultTransport ( ) { return new RetryHandler ( this . _apiKey , this . _options ) ; } private _setUpLogging ( ) { if ( this . _options . debug || this . _options . logLevel > 0 ) { if ( this . _options . logLevel > 0 ) { logger . enable ( this . _options . logLevel ) ; } else { logger . enable ( ) ; } } } }