@amplitude/analytics-core 2.24.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/diagnostics/diagnostics-client.d.ts +156 -0
- package/lib/cjs/diagnostics/diagnostics-client.d.ts.map +1 -0
- package/lib/cjs/diagnostics/diagnostics-client.js +305 -0
- package/lib/cjs/diagnostics/diagnostics-client.js.map +1 -0
- package/lib/cjs/diagnostics/diagnostics-storage.d.ts +123 -0
- package/lib/cjs/diagnostics/diagnostics-storage.d.ts.map +1 -0
- package/lib/cjs/diagnostics/diagnostics-storage.js +492 -0
- package/lib/cjs/diagnostics/diagnostics-storage.js.map +1 -0
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +4 -2
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/utils/url-utils.d.ts +2 -0
- package/lib/cjs/utils/url-utils.d.ts.map +1 -1
- package/lib/cjs/utils/url-utils.js +13 -1
- package/lib/cjs/utils/url-utils.js.map +1 -1
- package/lib/esm/diagnostics/diagnostics-client.d.ts +156 -0
- package/lib/esm/diagnostics/diagnostics-client.d.ts.map +1 -0
- package/lib/esm/diagnostics/diagnostics-client.js +302 -0
- package/lib/esm/diagnostics/diagnostics-client.js.map +1 -0
- package/lib/esm/diagnostics/diagnostics-storage.d.ts +123 -0
- package/lib/esm/diagnostics/diagnostics-storage.d.ts.map +1 -0
- package/lib/esm/diagnostics/diagnostics-storage.js +489 -0
- package/lib/esm/diagnostics/diagnostics-storage.js.map +1 -0
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +2 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/utils/url-utils.d.ts +2 -0
- package/lib/esm/utils/url-utils.d.ts.map +1 -1
- package/lib/esm/utils/url-utils.js +11 -0
- package/lib/esm/utils/url-utils.js.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;AAAA,6CAA8C;AAArC,4GAAA,aAAa,OAAA;AAGtB,+DAA6D;AAApD,qHAAA,gBAAgB,OAAA;AACzB,uCAAiD;AAAxC,oGAAA,QAAQ,OAAA;AACjB,qCAA+D;AAAtD,kGAAA,OAAO,OAAA;AAAY,0GAAA,eAAe,OAAA;AAC3C,qDAAoD;AAA3C,0GAAA,WAAW,OAAA;AACpB,+CAAyD;AAAhD,+GAAA,mBAAmB,OAAA;AAC5B,mCAAmD;AAA1C,gGAAA,MAAM,OAAA;AAAE,yGAAA,eAAe,OAAA;AAEhC,mCAAsD;AAA7C,gGAAA,MAAM,OAAA;AACf,+CAAgD;AAAvC,8GAAA,cAAc,OAAA;AACvB,6DAAwG;AAA/F,4HAAA,qBAAqB,OAAA;AAAE,2HAAA,oBAAoB,OAAA;AAAE,yHAAA,kBAAkB,OAAA;AACxE,qCAAyC;AAAhC,uGAAA,YAAY,OAAA;AACrB,6CAAgE;AAAvD,4GAAA,aAAa,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AACxC,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AACpB,+CAAgD;AAAvC,8GAAA,cAAc,OAAA;AAEvB,yDAAwE;AAA/D,+GAAA,aAAa,OAAA;AACtB,uCAAkF;AAAzE,qGAAA,YAAY,OAAA;AAAE,2GAAA,kBAAkB,OAAA;AAAE,wGAAA,eAAe,OAAA;AAC1D,qCAAoC;AAA3B,4FAAA,IAAI,OAAA;AACb,uDAA4D;AAAnD,oHAAA,mBAAmB,OAAA;AAC5B,+
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;AAAA,6CAA8C;AAArC,4GAAA,aAAa,OAAA;AAGtB,+DAA6D;AAApD,qHAAA,gBAAgB,OAAA;AACzB,uCAAiD;AAAxC,oGAAA,QAAQ,OAAA;AACjB,qCAA+D;AAAtD,kGAAA,OAAO,OAAA;AAAY,0GAAA,eAAe,OAAA;AAC3C,qDAAoD;AAA3C,0GAAA,WAAW,OAAA;AACpB,+CAAyD;AAAhD,+GAAA,mBAAmB,OAAA;AAC5B,mCAAmD;AAA1C,gGAAA,MAAM,OAAA;AAAE,yGAAA,eAAe,OAAA;AAEhC,mCAAsD;AAA7C,gGAAA,MAAM,OAAA;AACf,+CAAgD;AAAvC,8GAAA,cAAc,OAAA;AACvB,6DAAwG;AAA/F,4HAAA,qBAAqB,OAAA;AAAE,2HAAA,oBAAoB,OAAA;AAAE,yHAAA,kBAAkB,OAAA;AACxE,qCAAyC;AAAhC,uGAAA,YAAY,OAAA;AACrB,6CAAgE;AAAvD,4GAAA,aAAa,OAAA;AAAE,+GAAA,gBAAgB,OAAA;AACxC,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AACpB,+CAAgD;AAAvC,8GAAA,cAAc,OAAA;AAEvB,yDAAwE;AAA/D,+GAAA,aAAa,OAAA;AACtB,uCAAkF;AAAzE,qGAAA,YAAY,OAAA;AAAE,2GAAA,kBAAkB,OAAA;AAAE,wGAAA,eAAe,OAAA;AAC1D,qCAAoC;AAA3B,4FAAA,IAAI,OAAA;AACb,uDAA4D;AAAnD,oHAAA,mBAAmB,OAAA;AAC5B,+CAAsE;AAA7D,gHAAA,mBAAmB,OAAA;AAAE,yGAAA,YAAY,OAAA;AAE1C,2CAAiD;AAAxC,uGAAA,aAAa,OAAA;AACtB,2CAAiD;AAAxC,uGAAA,aAAa,OAAA;AACtB,6CAAkD;AAAzC,wGAAA,aAAa,OAAA;AAEtB,6DAA2D;AAAlD,iHAAA,cAAc,OAAA;AAEvB,4FAA4F;AAE5F,0CAAkD;AAAzC,qGAAA,aAAa,OAAA;AACtB,4CAAoD;AAA3C,uGAAA,cAAc,OAAA;AAEvB,+DAA8G;AAArG,mHAAA,kBAAkB,OAAA;AAE3B,6CAA4C;AAAnC,oGAAA,QAAQ,OAAA;AACjB,+CAAqE;AAA5D,6GAAA,gBAAgB,OAAA;AAAE,2GAAA,cAAc,OAAA;AAEzC,6CAAoH;AAApG,0GAAA,iBAAiB,OAAA;AAAE,yGAAA,gBAAgB,OAAA;AAGnD,mDAAiE;AAAxC,yGAAA,UAAU,OAAA;AACnC,2CAAkD;AAAzC,0GAAA,eAAe,OAAA;AAexB,qEAOsC;AAHpC,sIAAA,8BAA8B,OAAA;AAC9B,qIAAA,6BAA6B,OAAA;AAC7B,sIAAA,8BAA8B,OAAA;AAGhC,6EAQ0C;AANxC,wIAAA,4BAA4B,OAAA;AAC5B,wIAAA,4BAA4B,OAAA;AAC5B,wIAAA,4BAA4B,OAAA;AAC5B,wIAAA,4BAA4B,OAAA;AAC5B,sJAAA,0CAA0C,OAAA;AAC1C,wIAAA,4BAA4B,OAAA;AAG9B,yCAAwC;AAA/B,gGAAA,MAAM,OAAA;AAEf,uDAA2E;AAAlE,wHAAA,oBAAoB,OAAA;AAAE,mHAAA,eAAe,OAAA;AAC9C,iEAAiH;AAAxG,4HAAA,mBAAmB,OAAA;AAE5B,+CAAoE;AAA3D,yGAAA,YAAY,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAMxC,+CAAqE;AAA5D,wGAAA,WAAW,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,iGAAA,IAAI,OAAA;AACzC,8DAA4D;AAAnD,iHAAA,cAAc,OAAA","sourcesContent":["export { AmplitudeCore } from './core-client';\nexport { CoreClient, PluginHost } from './types/client/core-client';\nexport { AnalyticsClient } from './types/client/analytics-client';\nexport { AmplitudeContext } from './types/amplitude-context';\nexport { Identify, IIdentify } from './identify';\nexport { Revenue, IRevenue, RevenueProperty } from './revenue';\nexport { Destination } from './plugins/destination';\nexport { IdentityEventSender } from './plugins/identity';\nexport { Config, RequestMetadata } from './config';\nexport { IConfig } from './types/config/core-config';\nexport { Logger, ILogger, LogConfig } from './logger';\nexport { getGlobalScope } from './global-scope';\nexport { getAnalyticsConnector, setConnectorDeviceId, setConnectorUserId } from './analytics-connector';\nexport { isNewSession } from './session';\nexport { getCookieName, getOldCookieName } from './cookie-name';\nexport { getLanguage } from './language';\nexport { getQueryParams } from './query-params';\n\nexport { returnWrapper, AmplitudeReturn } from './utils/return-wrapper';\nexport { debugWrapper, getClientLogConfig, getClientStates } from './utils/debug';\nexport { UUID } from './utils/uuid';\nexport { createIdentifyEvent } from './utils/event-builder';\nexport { isUrlMatchAllowlist, getDecodeURI } from './utils/url-utils';\n\nexport { MemoryStorage } from './storage/memory';\nexport { CookieStorage } from './storage/cookie';\nexport { getStorageKey } from './storage/helpers';\n\nexport { BrowserStorage } from './storage/browser-storage';\n\n// export { DiagnosticsClient, IDiagnosticsClient } from './diagnostics/diagnostics-client';\n\nexport { BaseTransport } from './transports/base';\nexport { FetchTransport } from './transports/fetch';\n\nexport { RemoteConfigClient, IRemoteConfigClient, RemoteConfig, Source } from './remote-config/remote-config';\n\nexport { LogLevel } from './types/loglevel';\nexport { AMPLITUDE_PREFIX, STORAGE_PREFIX } from './types/constants';\nexport { Storage, IdentityStorageType } from './types/storage';\nexport { Event, IdentifyOperation, SpecialEventType, IdentifyEvent, GroupIdentifyEvent } from './types/event/event';\nexport { EventOptions, BaseEvent } from './types/event/base-event';\nexport { IngestionMetadata } from './types/event/ingestion-metadata';\nexport { ServerZoneType, ServerZone } from './types/server-zone';\nexport { OfflineDisabled } from './types/offline';\nexport { Plan } from './types/event/plan';\nexport { TransportType, Transport } from './types/transport';\nexport { Payload } from './types/payload';\nexport { Response } from './types/response';\nexport { UserSession } from './types/user-session';\nexport {\n Plugin,\n BeforePlugin,\n DestinationPlugin,\n EnrichmentPlugin,\n PluginType,\n AnalyticsIdentity,\n} from './types/plugin';\nexport { Result } from './types/result';\nexport {\n ElementInteractionsOptions,\n Messenger,\n ActionType,\n DEFAULT_CSS_SELECTOR_ALLOWLIST,\n DEFAULT_DATA_ATTRIBUTE_PREFIX,\n DEFAULT_ACTION_CLICK_ALLOWLIST,\n} from './types/element-interactions';\n\nexport {\n FrustrationInteractionsOptions,\n DEFAULT_DEAD_CLICK_ALLOWLIST,\n DEFAULT_RAGE_CLICK_ALLOWLIST,\n DEFAULT_RAGE_CLICK_THRESHOLD,\n DEFAULT_RAGE_CLICK_WINDOW_MS,\n DEFAULT_RAGE_CLICK_OUT_OF_BOUNDS_THRESHOLD,\n DEFAULT_DEAD_CLICK_WINDOW_MS,\n} from './types/frustration-interactions';\nexport { PageTrackingOptions, PageTrackingTrackOn, PageTrackingHistoryChanges } from './types/page-view-tracking';\nexport { Status } from './types/status';\n\nexport { NetworkEventCallback, networkObserver } from './network-observer';\nexport { NetworkRequestEvent, IRequestWrapper, JsonObject, JsonValue, JsonArray } from './network-request-event';\nexport { NetworkTrackingOptions, NetworkCaptureRule } from './types/network-tracking';\nexport { SAFE_HEADERS, FORBIDDEN_HEADERS } from './types/constants';\n\nexport { PageUrlEnrichmentOptions } from './types/page-url-enrichment';\n\n// Campaign\nexport { Campaign, UTMParameters, ReferrerParameters, ClickIdParameters, ICampaignParser } from './types/campaign';\nexport { EMPTY_VALUE, BASE_CAMPAIGN, MKTG } from './types/constants';\nexport { CampaignParser } from './campaign/campaign-parser';\n\n// Browser\nexport {\n BrowserConfig,\n BrowserOptions,\n DefaultTrackingOptions,\n TrackingOptions,\n AutocaptureOptions,\n CookieOptions,\n AttributionOptions,\n} from './types/config/browser-config';\nexport { BrowserClient } from './types/client/browser-client';\n\n// Node\nexport { NodeClient } from './types/client/node-client';\nexport { NodeConfig, NodeOptions } from './types/config/node-config';\n\n// React Native\nexport {\n ReactNativeConfig,\n ReactNativeTrackingOptions,\n ReactNativeOptions,\n ReactNativeAttributionOptions,\n} from './types/config/react-native-config';\nexport { ReactNativeClient } from './types/client/react-native-client';\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ILogger } from '../logger';
|
|
1
2
|
/**
|
|
2
3
|
* Checks if a given URL matches any pattern in an allowlist of URLs or regex patterns.
|
|
3
4
|
* @param url - The URL to check
|
|
@@ -5,4 +6,5 @@
|
|
|
5
6
|
* @returns true if the URL matches any pattern in the allowlist, false otherwise
|
|
6
7
|
*/
|
|
7
8
|
export declare const isUrlMatchAllowlist: (url: string, allowlist: (string | RegExp)[] | undefined) => boolean;
|
|
9
|
+
export declare const getDecodeURI: (locationStr: string, loggerProvider?: ILogger) => string;
|
|
8
10
|
//# sourceMappingURL=url-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-utils.d.ts","sourceRoot":"","sources":["../../../src/utils/url-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAS,MAAM,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,SAAS,KAAG,OAU7F,CAAC"}
|
|
1
|
+
{"version":3,"file":"url-utils.d.ts","sourceRoot":"","sources":["../../../src/utils/url-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAS,MAAM,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,SAAS,KAAG,OAU7F,CAAC;AAEF,eAAO,MAAM,YAAY,gBAAiB,MAAM,mBAAmB,OAAO,KAAG,MAU5E,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isUrlMatchAllowlist = void 0;
|
|
3
|
+
exports.getDecodeURI = exports.isUrlMatchAllowlist = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Checks if a given URL matches any pattern in an allowlist of URLs or regex patterns.
|
|
6
6
|
* @param url - The URL to check
|
|
@@ -19,4 +19,16 @@ var isUrlMatchAllowlist = function (url, allowlist) {
|
|
|
19
19
|
});
|
|
20
20
|
};
|
|
21
21
|
exports.isUrlMatchAllowlist = isUrlMatchAllowlist;
|
|
22
|
+
var getDecodeURI = function (locationStr, loggerProvider) {
|
|
23
|
+
var decodedLocationStr = locationStr;
|
|
24
|
+
try {
|
|
25
|
+
decodedLocationStr = decodeURI(locationStr);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
/* istanbul ignore next */
|
|
29
|
+
loggerProvider === null || loggerProvider === void 0 ? void 0 : loggerProvider.error('Malformed URI sequence: ', e);
|
|
30
|
+
}
|
|
31
|
+
return decodedLocationStr;
|
|
32
|
+
};
|
|
33
|
+
exports.getDecodeURI = getDecodeURI;
|
|
22
34
|
//# sourceMappingURL=url-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-utils.js","sourceRoot":"","sources":["../../../src/utils/url-utils.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"url-utils.js","sourceRoot":"","sources":["../../../src/utils/url-utils.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACI,IAAM,mBAAmB,GAAG,UAAC,GAAW,EAAE,SAA0C;IACzF,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE;QACnC,OAAO,IAAI,CAAC;KACb;IACD,OAAO,SAAS,CAAC,IAAI,CAAC,UAAC,UAAU;QAC/B,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,OAAO,GAAG,KAAK,UAAU,CAAC;SAC3B;QACD,OAAO,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAVW,QAAA,mBAAmB,uBAU9B;AAEK,IAAM,YAAY,GAAG,UAAC,WAAmB,EAAE,cAAwB;IACxE,IAAI,kBAAkB,GAAG,WAAW,CAAC;IACrC,IAAI;QACF,kBAAkB,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;KAC7C;IAAC,OAAO,CAAC,EAAE;QACV,0BAA0B;QAC1B,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,KAAK,CAAC,0BAA0B,EAAE,CAAC,CAAC,CAAC;KACtD;IAED,OAAO,kBAAkB,CAAC;AAC5B,CAAC,CAAC;AAVW,QAAA,YAAY,gBAUvB","sourcesContent":["import { ILogger } from '../logger';\n\n/**\n * Checks if a given URL matches any pattern in an allowlist of URLs or regex patterns.\n * @param url - The URL to check\n * @param allowlist - Array of allowed URLs (strings) or regex patterns\n * @returns true if the URL matches any pattern in the allowlist, false otherwise\n */\nexport const isUrlMatchAllowlist = (url: string, allowlist: (string | RegExp)[] | undefined): boolean => {\n if (!allowlist || !allowlist.length) {\n return true;\n }\n return allowlist.some((allowedUrl) => {\n if (typeof allowedUrl === 'string') {\n return url === allowedUrl;\n }\n return url.match(allowedUrl);\n });\n};\n\nexport const getDecodeURI = (locationStr: string, loggerProvider?: ILogger): string => {\n let decodedLocationStr = locationStr;\n try {\n decodedLocationStr = decodeURI(locationStr);\n } catch (e) {\n /* istanbul ignore next */\n loggerProvider?.error('Malformed URI sequence: ', e);\n }\n\n return decodedLocationStr;\n};\n"]}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { ILogger } from '../logger';
|
|
2
|
+
import { IDiagnosticsStorage } from './diagnostics-storage';
|
|
3
|
+
import { ServerZoneType } from '../types/server-zone';
|
|
4
|
+
export declare const SAVE_INTERVAL_MS = 1000;
|
|
5
|
+
export declare const FLUSH_INTERVAL_MS: number;
|
|
6
|
+
export declare const DIAGNOSTICS_US_SERVER_URL = "https://diagnostics.prod.us-west-2.amplitude.com/v1/capture";
|
|
7
|
+
export declare const DIAGNOSTICS_EU_SERVER_URL = "https://diagnostics.prod.eu-central-1.amplitude.com/v1/capture";
|
|
8
|
+
export declare const MAX_MEMORY_STORAGE_COUNT = 10000;
|
|
9
|
+
export declare const MAX_MEMORY_STORAGE_EVENTS_COUNT = 10;
|
|
10
|
+
/**
|
|
11
|
+
* Key-value pairs for environment/context information
|
|
12
|
+
*/
|
|
13
|
+
type DiagnosticsTags = Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Numeric counters that can be incremented
|
|
16
|
+
*/
|
|
17
|
+
type DiagnosticsCounters = Record<string, number>;
|
|
18
|
+
/**
|
|
19
|
+
* Properties for diagnostic events
|
|
20
|
+
*/
|
|
21
|
+
type EventProperties = Record<string, any>;
|
|
22
|
+
/**
|
|
23
|
+
* Individual diagnostic event
|
|
24
|
+
*/
|
|
25
|
+
interface DiagnosticsEvent {
|
|
26
|
+
readonly event_name: string;
|
|
27
|
+
readonly time: number;
|
|
28
|
+
readonly event_properties: EventProperties;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Computed histogram statistics for final payload
|
|
32
|
+
*/
|
|
33
|
+
interface HistogramResult {
|
|
34
|
+
readonly count: number;
|
|
35
|
+
readonly min: number;
|
|
36
|
+
readonly max: number;
|
|
37
|
+
readonly avg: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Internal histogram statistics with sum for efficient incremental updates
|
|
41
|
+
*/
|
|
42
|
+
export interface HistogramStats {
|
|
43
|
+
count: number;
|
|
44
|
+
min: number;
|
|
45
|
+
max: number;
|
|
46
|
+
sum: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Collection of histogram results keyed by histogram name
|
|
50
|
+
*/
|
|
51
|
+
type DiagnosticsHistograms = Record<string, HistogramResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Collection of histogram stats keyed by histogram name (internal use for memory + persistence storage)
|
|
54
|
+
*/
|
|
55
|
+
type DiagnosticsHistogramStats = Record<string, HistogramStats>;
|
|
56
|
+
/**
|
|
57
|
+
* Complete diagnostics payload sent to backend
|
|
58
|
+
*/
|
|
59
|
+
interface FlushPayload {
|
|
60
|
+
readonly tags: DiagnosticsTags;
|
|
61
|
+
readonly histogram: DiagnosticsHistograms;
|
|
62
|
+
readonly counters: DiagnosticsCounters;
|
|
63
|
+
readonly events: readonly DiagnosticsEvent[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Amplitude Diagnostics Client
|
|
67
|
+
*
|
|
68
|
+
* A client for collecting and managing diagnostics data including tags, counters,
|
|
69
|
+
* histograms, and events. Data is stored persistently using IndexedDB to survive browser restarts and offline scenarios.
|
|
70
|
+
*
|
|
71
|
+
* Key Features:
|
|
72
|
+
* - IndexedDB storage
|
|
73
|
+
* - Time-based persistent storage flush interval (5 minutes since last flush)
|
|
74
|
+
* - 1 second time-based memory storage flush to persistent storage
|
|
75
|
+
* - Histogram statistics calculation (min, max, avg)
|
|
76
|
+
*/
|
|
77
|
+
export interface IDiagnosticsClient {
|
|
78
|
+
/**
|
|
79
|
+
* Set or update a tag
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* // Set environment tags
|
|
84
|
+
* diagnostics.setTag('library', 'amplitude-typescript/2.0.0');
|
|
85
|
+
* diagnostics.setTag('user_agent', navigator.userAgent);
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
setTag(name: string, value: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Increment a counter. If doesn't exist, create a counter and set value to 1
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // Track counters
|
|
95
|
+
* diagnostics.increment('analytics.fileNotFound');
|
|
96
|
+
* diagnostics.increment('network.retry', 3);
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
increment(name: string, size?: number): void;
|
|
100
|
+
/**
|
|
101
|
+
* Record a histogram value
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // Record performance metrics
|
|
106
|
+
* diagnostics.recordHistogram('sr.time', 5.2);
|
|
107
|
+
* diagnostics.recordHistogram('network.latency', 150);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
recordHistogram(name: string, value: number): void;
|
|
111
|
+
/**
|
|
112
|
+
* Record an event
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* // Record diagnostic events
|
|
117
|
+
* diagnostics.recordEvent('error', {
|
|
118
|
+
* stack_trace: '...',
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
recordEvent(name: string, properties: EventProperties): void;
|
|
123
|
+
_flush(): void;
|
|
124
|
+
}
|
|
125
|
+
export declare class DiagnosticsClient implements IDiagnosticsClient {
|
|
126
|
+
storage?: IDiagnosticsStorage;
|
|
127
|
+
logger: ILogger;
|
|
128
|
+
serverUrl: string;
|
|
129
|
+
apiKey: string;
|
|
130
|
+
inMemoryTags: DiagnosticsTags;
|
|
131
|
+
inMemoryCounters: DiagnosticsCounters;
|
|
132
|
+
inMemoryHistograms: DiagnosticsHistogramStats;
|
|
133
|
+
inMemoryEvents: DiagnosticsEvent[];
|
|
134
|
+
saveTimer: ReturnType<typeof setTimeout> | null;
|
|
135
|
+
flushTimer: ReturnType<typeof setTimeout> | null;
|
|
136
|
+
constructor(apiKey: string, logger: ILogger, serverZone?: ServerZoneType);
|
|
137
|
+
setTag(name: string, value: string): void;
|
|
138
|
+
increment(name: string, size?: number): void;
|
|
139
|
+
recordHistogram(name: string, value: number): void;
|
|
140
|
+
recordEvent(name: string, properties: EventProperties): void;
|
|
141
|
+
startTimersIfNeeded(): void;
|
|
142
|
+
saveAllDataToStorage(): Promise<void>;
|
|
143
|
+
_flush(): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* Send diagnostics data to the server
|
|
146
|
+
*/
|
|
147
|
+
fetch(payload: FlushPayload): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Initialize flush interval logic.
|
|
150
|
+
* Check if 5 minutes has passed since last flush, if so flush immediately.
|
|
151
|
+
* Otherwise set a timer to flush when the interval is reached.
|
|
152
|
+
*/
|
|
153
|
+
initializeFlushInterval(): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
export {};
|
|
156
|
+
//# sourceMappingURL=diagnostics-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics-client.d.ts","sourceRoot":"","sources":["../../../src/diagnostics/diagnostics-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { __assign, __awaiter, __generator, __read, __spreadArray } from "tslib";
|
|
2
|
+
import { DiagnosticsStorage } from './diagnostics-storage';
|
|
3
|
+
import { getGlobalScope } from '../global-scope';
|
|
4
|
+
export var SAVE_INTERVAL_MS = 1000; // 1 second
|
|
5
|
+
export var FLUSH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
6
|
+
export var DIAGNOSTICS_US_SERVER_URL = 'https://diagnostics.prod.us-west-2.amplitude.com/v1/capture';
|
|
7
|
+
export var DIAGNOSTICS_EU_SERVER_URL = 'https://diagnostics.prod.eu-central-1.amplitude.com/v1/capture';
|
|
8
|
+
// In-memory storage limits
|
|
9
|
+
export var MAX_MEMORY_STORAGE_COUNT = 10000; // for tags, counters, histograms separately
|
|
10
|
+
export var MAX_MEMORY_STORAGE_EVENTS_COUNT = 10;
|
|
11
|
+
var DiagnosticsClient = /** @class */ (function () {
|
|
12
|
+
function DiagnosticsClient(apiKey, logger, serverZone) {
|
|
13
|
+
if (serverZone === void 0) { serverZone = 'US'; }
|
|
14
|
+
// In-memory storages
|
|
15
|
+
this.inMemoryTags = {};
|
|
16
|
+
this.inMemoryCounters = {};
|
|
17
|
+
this.inMemoryHistograms = {};
|
|
18
|
+
this.inMemoryEvents = [];
|
|
19
|
+
// Timer for 1-second persistence
|
|
20
|
+
this.saveTimer = null;
|
|
21
|
+
// Timer for flush interval
|
|
22
|
+
this.flushTimer = null;
|
|
23
|
+
this.apiKey = apiKey;
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
this.serverUrl = serverZone === 'US' ? DIAGNOSTICS_US_SERVER_URL : DIAGNOSTICS_EU_SERVER_URL;
|
|
26
|
+
if (DiagnosticsStorage.isSupported()) {
|
|
27
|
+
this.storage = new DiagnosticsStorage(apiKey, logger);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
this.logger.debug('DiagnosticsClient: IndexedDB is not supported');
|
|
31
|
+
}
|
|
32
|
+
void this.initializeFlushInterval();
|
|
33
|
+
}
|
|
34
|
+
DiagnosticsClient.prototype.setTag = function (name, value) {
|
|
35
|
+
if (!this.storage) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (Object.keys(this.inMemoryTags).length >= MAX_MEMORY_STORAGE_COUNT) {
|
|
39
|
+
this.logger.debug('DiagnosticsClient: Early return setTags as reaching memory limit');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.inMemoryTags[name] = value;
|
|
43
|
+
this.startTimersIfNeeded();
|
|
44
|
+
};
|
|
45
|
+
DiagnosticsClient.prototype.increment = function (name, size) {
|
|
46
|
+
if (size === void 0) { size = 1; }
|
|
47
|
+
if (!this.storage) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (Object.keys(this.inMemoryCounters).length >= MAX_MEMORY_STORAGE_COUNT) {
|
|
51
|
+
this.logger.debug('DiagnosticsClient: Early return increment as reaching memory limit');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.inMemoryCounters[name] = (this.inMemoryCounters[name] || 0) + size;
|
|
55
|
+
this.startTimersIfNeeded();
|
|
56
|
+
};
|
|
57
|
+
DiagnosticsClient.prototype.recordHistogram = function (name, value) {
|
|
58
|
+
if (!this.storage) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (Object.keys(this.inMemoryHistograms).length >= MAX_MEMORY_STORAGE_COUNT) {
|
|
62
|
+
this.logger.debug('DiagnosticsClient: Early return recordHistogram as reaching memory limit');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
var existing = this.inMemoryHistograms[name];
|
|
66
|
+
if (existing) {
|
|
67
|
+
// Update existing stats incrementally
|
|
68
|
+
existing.count += 1;
|
|
69
|
+
existing.min = Math.min(existing.min, value);
|
|
70
|
+
existing.max = Math.max(existing.max, value);
|
|
71
|
+
existing.sum += value;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// Create new stats
|
|
75
|
+
this.inMemoryHistograms[name] = {
|
|
76
|
+
count: 1,
|
|
77
|
+
min: value,
|
|
78
|
+
max: value,
|
|
79
|
+
sum: value,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
this.startTimersIfNeeded();
|
|
83
|
+
};
|
|
84
|
+
DiagnosticsClient.prototype.recordEvent = function (name, properties) {
|
|
85
|
+
if (!this.storage) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (this.inMemoryEvents.length >= MAX_MEMORY_STORAGE_EVENTS_COUNT) {
|
|
89
|
+
this.logger.debug('DiagnosticsClient: Early return recordEvent as reaching memory limit');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.inMemoryEvents.push({
|
|
93
|
+
event_name: name,
|
|
94
|
+
time: Date.now(),
|
|
95
|
+
event_properties: properties,
|
|
96
|
+
});
|
|
97
|
+
this.startTimersIfNeeded();
|
|
98
|
+
};
|
|
99
|
+
DiagnosticsClient.prototype.startTimersIfNeeded = function () {
|
|
100
|
+
var _this = this;
|
|
101
|
+
if (!this.saveTimer) {
|
|
102
|
+
this.saveTimer = setTimeout(function () {
|
|
103
|
+
_this.saveAllDataToStorage()
|
|
104
|
+
.catch(function (error) {
|
|
105
|
+
_this.logger.debug('DiagnosticsClient: Failed to save all data to storage', error);
|
|
106
|
+
})
|
|
107
|
+
.finally(function () {
|
|
108
|
+
_this.saveTimer = null;
|
|
109
|
+
});
|
|
110
|
+
}, SAVE_INTERVAL_MS);
|
|
111
|
+
}
|
|
112
|
+
if (!this.flushTimer) {
|
|
113
|
+
this.flushTimer = setTimeout(function () {
|
|
114
|
+
_this._flush()
|
|
115
|
+
.catch(function (error) {
|
|
116
|
+
_this.logger.debug('DiagnosticsClient: Failed to flush', error);
|
|
117
|
+
})
|
|
118
|
+
.finally(function () {
|
|
119
|
+
_this.flushTimer = null;
|
|
120
|
+
});
|
|
121
|
+
}, FLUSH_INTERVAL_MS);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
DiagnosticsClient.prototype.saveAllDataToStorage = function () {
|
|
125
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
126
|
+
var tagsToSave, countersToSave, histogramsToSave, eventsToSave;
|
|
127
|
+
return __generator(this, function (_a) {
|
|
128
|
+
switch (_a.label) {
|
|
129
|
+
case 0:
|
|
130
|
+
if (!this.storage) {
|
|
131
|
+
return [2 /*return*/];
|
|
132
|
+
}
|
|
133
|
+
tagsToSave = __assign({}, this.inMemoryTags);
|
|
134
|
+
countersToSave = __assign({}, this.inMemoryCounters);
|
|
135
|
+
histogramsToSave = __assign({}, this.inMemoryHistograms);
|
|
136
|
+
eventsToSave = __spreadArray([], __read(this.inMemoryEvents), false);
|
|
137
|
+
this.inMemoryEvents = [];
|
|
138
|
+
this.inMemoryTags = {};
|
|
139
|
+
this.inMemoryCounters = {};
|
|
140
|
+
this.inMemoryHistograms = {};
|
|
141
|
+
return [4 /*yield*/, Promise.all([
|
|
142
|
+
this.storage.setTags(tagsToSave),
|
|
143
|
+
this.storage.incrementCounters(countersToSave),
|
|
144
|
+
this.storage.setHistogramStats(histogramsToSave),
|
|
145
|
+
this.storage.addEventRecords(eventsToSave),
|
|
146
|
+
])];
|
|
147
|
+
case 1:
|
|
148
|
+
_a.sent();
|
|
149
|
+
return [2 /*return*/];
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
DiagnosticsClient.prototype._flush = function () {
|
|
155
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
156
|
+
var _a, tagRecords, counterRecords, histogramStatsRecords, eventRecords, tags, counters, histogram, events, payload;
|
|
157
|
+
return __generator(this, function (_b) {
|
|
158
|
+
switch (_b.label) {
|
|
159
|
+
case 0:
|
|
160
|
+
if (!this.storage) {
|
|
161
|
+
return [2 /*return*/];
|
|
162
|
+
}
|
|
163
|
+
return [4 /*yield*/, this.saveAllDataToStorage()];
|
|
164
|
+
case 1:
|
|
165
|
+
_b.sent();
|
|
166
|
+
this.saveTimer = null;
|
|
167
|
+
this.flushTimer = null;
|
|
168
|
+
return [4 /*yield*/, this.storage.getAllAndClear()];
|
|
169
|
+
case 2:
|
|
170
|
+
_a = _b.sent(), tagRecords = _a.tags, counterRecords = _a.counters, histogramStatsRecords = _a.histogramStats, eventRecords = _a.events;
|
|
171
|
+
// Update the last flush timestamp
|
|
172
|
+
void this.storage.setLastFlushTimestamp(Date.now());
|
|
173
|
+
tags = {};
|
|
174
|
+
tagRecords.forEach(function (record) {
|
|
175
|
+
tags[record.key] = record.value;
|
|
176
|
+
});
|
|
177
|
+
counters = {};
|
|
178
|
+
counterRecords.forEach(function (record) {
|
|
179
|
+
counters[record.key] = record.value;
|
|
180
|
+
});
|
|
181
|
+
histogram = {};
|
|
182
|
+
histogramStatsRecords.forEach(function (stats) {
|
|
183
|
+
histogram[stats.key] = {
|
|
184
|
+
count: stats.count,
|
|
185
|
+
min: stats.min,
|
|
186
|
+
max: stats.max,
|
|
187
|
+
avg: Math.round((stats.sum / stats.count) * 100) / 100, // round the average to 2 decimal places.
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
events = eventRecords.map(function (record) { return ({
|
|
191
|
+
event_name: record.event_name,
|
|
192
|
+
time: record.time,
|
|
193
|
+
event_properties: record.event_properties,
|
|
194
|
+
}); });
|
|
195
|
+
// Early return if all data collections are empty
|
|
196
|
+
if (Object.keys(tags).length === 0 &&
|
|
197
|
+
Object.keys(counters).length === 0 &&
|
|
198
|
+
Object.keys(histogram).length === 0 &&
|
|
199
|
+
events.length === 0) {
|
|
200
|
+
return [2 /*return*/];
|
|
201
|
+
}
|
|
202
|
+
payload = {
|
|
203
|
+
tags: tags,
|
|
204
|
+
histogram: histogram,
|
|
205
|
+
counters: counters,
|
|
206
|
+
events: events,
|
|
207
|
+
};
|
|
208
|
+
// Send payload to diagnostics server
|
|
209
|
+
void this.fetch(payload);
|
|
210
|
+
return [2 /*return*/];
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Send diagnostics data to the server
|
|
217
|
+
*/
|
|
218
|
+
DiagnosticsClient.prototype.fetch = function (payload) {
|
|
219
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
220
|
+
var response, error_1;
|
|
221
|
+
return __generator(this, function (_a) {
|
|
222
|
+
switch (_a.label) {
|
|
223
|
+
case 0:
|
|
224
|
+
_a.trys.push([0, 2, , 3]);
|
|
225
|
+
if (!getGlobalScope()) {
|
|
226
|
+
throw new Error('DiagnosticsClient: Fetch is not supported');
|
|
227
|
+
}
|
|
228
|
+
return [4 /*yield*/, fetch(this.serverUrl, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: {
|
|
231
|
+
'X-ApiKey': this.apiKey,
|
|
232
|
+
'Content-Type': 'application/json',
|
|
233
|
+
},
|
|
234
|
+
body: JSON.stringify(payload),
|
|
235
|
+
})];
|
|
236
|
+
case 1:
|
|
237
|
+
response = _a.sent();
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
this.logger.debug('DiagnosticsClient: Failed to send diagnostics data.');
|
|
240
|
+
return [2 /*return*/];
|
|
241
|
+
}
|
|
242
|
+
this.logger.debug('DiagnosticsClient: Successfully sent diagnostics data');
|
|
243
|
+
return [3 /*break*/, 3];
|
|
244
|
+
case 2:
|
|
245
|
+
error_1 = _a.sent();
|
|
246
|
+
this.logger.debug('DiagnosticsClient: Failed to send diagnostics data. ', error_1);
|
|
247
|
+
return [3 /*break*/, 3];
|
|
248
|
+
case 3: return [2 /*return*/];
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Initialize flush interval logic.
|
|
255
|
+
* Check if 5 minutes has passed since last flush, if so flush immediately.
|
|
256
|
+
* Otherwise set a timer to flush when the interval is reached.
|
|
257
|
+
*/
|
|
258
|
+
DiagnosticsClient.prototype.initializeFlushInterval = function () {
|
|
259
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
260
|
+
var now, lastFlushTimestamp, timeSinceLastFlush, remainingTime;
|
|
261
|
+
var _this = this;
|
|
262
|
+
return __generator(this, function (_a) {
|
|
263
|
+
switch (_a.label) {
|
|
264
|
+
case 0:
|
|
265
|
+
if (!this.storage) {
|
|
266
|
+
return [2 /*return*/];
|
|
267
|
+
}
|
|
268
|
+
now = Date.now();
|
|
269
|
+
return [4 /*yield*/, this.storage.getLastFlushTimestamp()];
|
|
270
|
+
case 1:
|
|
271
|
+
lastFlushTimestamp = (_a.sent()) || -1;
|
|
272
|
+
timeSinceLastFlush = now - lastFlushTimestamp;
|
|
273
|
+
// If last flush timestamp is -1, it means this is a new client
|
|
274
|
+
if (lastFlushTimestamp === -1) {
|
|
275
|
+
return [2 /*return*/];
|
|
276
|
+
}
|
|
277
|
+
else if (timeSinceLastFlush >= FLUSH_INTERVAL_MS) {
|
|
278
|
+
// More than 5 minutes has passed, flush immediately
|
|
279
|
+
void this._flush();
|
|
280
|
+
return [2 /*return*/];
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
remainingTime = FLUSH_INTERVAL_MS - timeSinceLastFlush;
|
|
284
|
+
this.flushTimer = setTimeout(function () {
|
|
285
|
+
_this._flush()
|
|
286
|
+
.catch(function (error) {
|
|
287
|
+
_this.logger.debug('DiagnosticsClient: Failed to flush', error);
|
|
288
|
+
})
|
|
289
|
+
.finally(function () {
|
|
290
|
+
_this.flushTimer = null;
|
|
291
|
+
});
|
|
292
|
+
}, remainingTime);
|
|
293
|
+
}
|
|
294
|
+
return [2 /*return*/];
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
return DiagnosticsClient;
|
|
300
|
+
}());
|
|
301
|
+
export { DiagnosticsClient };
|
|
302
|
+
//# sourceMappingURL=diagnostics-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics-client.js","sourceRoot":"","sources":["../../../src/diagnostics/diagnostics-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILogger } from '../logger';\nimport { DiagnosticsStorage, IDiagnosticsStorage } from './diagnostics-storage';\nimport { ServerZoneType } from '../types/server-zone';\nimport { getGlobalScope } from '../global-scope';\n\nexport const SAVE_INTERVAL_MS = 1000; // 1 second\nexport const FLUSH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\nexport const DIAGNOSTICS_US_SERVER_URL = 'https://diagnostics.prod.us-west-2.amplitude.com/v1/capture';\nexport const DIAGNOSTICS_EU_SERVER_URL = 'https://diagnostics.prod.eu-central-1.amplitude.com/v1/capture';\n\n// In-memory storage limits\nexport const MAX_MEMORY_STORAGE_COUNT = 10000; // for tags, counters, histograms separately\nexport const MAX_MEMORY_STORAGE_EVENTS_COUNT = 10;\n\n// === Core Data Types ===\n\n/**\n * Key-value pairs for environment/context information\n */\ntype DiagnosticsTags = Record<string, string>;\n\n/**\n * Numeric counters that can be incremented\n */\ntype DiagnosticsCounters = Record<string, number>;\n\n/**\n * Properties for diagnostic events\n */\ntype EventProperties = Record<string, any>;\n\n/**\n * Individual diagnostic event\n */\ninterface DiagnosticsEvent {\n readonly event_name: string;\n readonly time: number;\n readonly event_properties: EventProperties;\n}\n\n/**\n * Computed histogram statistics for final payload\n */\ninterface HistogramResult {\n readonly count: number;\n readonly min: number;\n readonly max: number;\n readonly avg: number;\n}\n\n/**\n * Internal histogram statistics with sum for efficient incremental updates\n */\nexport interface HistogramStats {\n count: number;\n min: number;\n max: number;\n sum: number; // Used for avg calculation\n}\n\n/**\n * Collection of histogram results keyed by histogram name\n */\ntype DiagnosticsHistograms = Record<string, HistogramResult>;\n\n/**\n * Collection of histogram stats keyed by histogram name (internal use for memory + persistence storage)\n */\ntype DiagnosticsHistogramStats = Record<string, HistogramStats>;\n\n// === Payload Types ===\n\n/**\n * Complete diagnostics payload sent to backend\n */\ninterface FlushPayload {\n readonly tags: DiagnosticsTags;\n readonly histogram: DiagnosticsHistograms;\n readonly counters: DiagnosticsCounters;\n readonly events: readonly DiagnosticsEvent[];\n}\n\n/**\n * Amplitude Diagnostics Client\n *\n * A client for collecting and managing diagnostics data including tags, counters,\n * histograms, and events. Data is stored persistently using IndexedDB to survive browser restarts and offline scenarios.\n *\n * Key Features:\n * - IndexedDB storage\n * - Time-based persistent storage flush interval (5 minutes since last flush)\n * - 1 second time-based memory storage flush to persistent storage\n * - Histogram statistics calculation (min, max, avg)\n */\nexport interface IDiagnosticsClient {\n /**\n * Set or update a tag\n *\n * @example\n * ```typescript\n * // Set environment tags\n * diagnostics.setTag('library', 'amplitude-typescript/2.0.0');\n * diagnostics.setTag('user_agent', navigator.userAgent);\n * ```\n */\n setTag(name: string, value: string): void;\n\n /**\n * Increment a counter. If doesn't exist, create a counter and set value to 1\n *\n * @example\n * ```typescript\n * // Track counters\n * diagnostics.increment('analytics.fileNotFound');\n * diagnostics.increment('network.retry', 3);\n * ```\n */\n increment(name: string, size?: number): void;\n\n /**\n * Record a histogram value\n *\n * @example\n * ```typescript\n * // Record performance metrics\n * diagnostics.recordHistogram('sr.time', 5.2);\n * diagnostics.recordHistogram('network.latency', 150);\n * ```\n */\n recordHistogram(name: string, value: number): void;\n\n /**\n * Record an event\n *\n * @example\n * ```typescript\n * // Record diagnostic events\n * diagnostics.recordEvent('error', {\n * stack_trace: '...',\n * });\n * ```\n */\n recordEvent(name: string, properties: EventProperties): void;\n\n // Flush storage\n _flush(): void;\n}\n\nexport class DiagnosticsClient implements IDiagnosticsClient {\n storage?: IDiagnosticsStorage;\n logger: ILogger;\n serverUrl: string;\n apiKey: string;\n\n // In-memory storages\n inMemoryTags: DiagnosticsTags = {};\n inMemoryCounters: DiagnosticsCounters = {};\n inMemoryHistograms: DiagnosticsHistogramStats = {};\n inMemoryEvents: DiagnosticsEvent[] = [];\n\n // Timer for 1-second persistence\n saveTimer: ReturnType<typeof setTimeout> | null = null;\n // Timer for flush interval\n flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(apiKey: string, logger: ILogger, serverZone: ServerZoneType = 'US') {\n this.apiKey = apiKey;\n this.logger = logger;\n this.serverUrl = serverZone === 'US' ? DIAGNOSTICS_US_SERVER_URL : DIAGNOSTICS_EU_SERVER_URL;\n\n if (DiagnosticsStorage.isSupported()) {\n this.storage = new DiagnosticsStorage(apiKey, logger);\n } else {\n this.logger.debug('DiagnosticsClient: IndexedDB is not supported');\n }\n\n void this.initializeFlushInterval();\n }\n\n setTag(name: string, value: string) {\n if (!this.storage) {\n return;\n }\n\n if (Object.keys(this.inMemoryTags).length >= MAX_MEMORY_STORAGE_COUNT) {\n this.logger.debug('DiagnosticsClient: Early return setTags as reaching memory limit');\n return;\n }\n\n this.inMemoryTags[name] = value;\n this.startTimersIfNeeded();\n }\n\n increment(name: string, size = 1) {\n if (!this.storage) {\n return;\n }\n\n if (Object.keys(this.inMemoryCounters).length >= MAX_MEMORY_STORAGE_COUNT) {\n this.logger.debug('DiagnosticsClient: Early return increment as reaching memory limit');\n return;\n }\n\n this.inMemoryCounters[name] = (this.inMemoryCounters[name] || 0) + size;\n this.startTimersIfNeeded();\n }\n\n recordHistogram(name: string, value: number) {\n if (!this.storage) {\n return;\n }\n\n if (Object.keys(this.inMemoryHistograms).length >= MAX_MEMORY_STORAGE_COUNT) {\n this.logger.debug('DiagnosticsClient: Early return recordHistogram as reaching memory limit');\n return;\n }\n\n const existing = this.inMemoryHistograms[name];\n if (existing) {\n // Update existing stats incrementally\n existing.count += 1;\n existing.min = Math.min(existing.min, value);\n existing.max = Math.max(existing.max, value);\n existing.sum += value;\n } else {\n // Create new stats\n this.inMemoryHistograms[name] = {\n count: 1,\n min: value,\n max: value,\n sum: value,\n };\n }\n this.startTimersIfNeeded();\n }\n\n recordEvent(name: string, properties: EventProperties) {\n if (!this.storage) {\n return;\n }\n\n if (this.inMemoryEvents.length >= MAX_MEMORY_STORAGE_EVENTS_COUNT) {\n this.logger.debug('DiagnosticsClient: Early return recordEvent as reaching memory limit');\n return;\n }\n\n this.inMemoryEvents.push({\n event_name: name,\n time: Date.now(),\n event_properties: properties,\n });\n this.startTimersIfNeeded();\n }\n\n startTimersIfNeeded() {\n if (!this.saveTimer) {\n this.saveTimer = setTimeout(() => {\n this.saveAllDataToStorage()\n .catch((error) => {\n this.logger.debug('DiagnosticsClient: Failed to save all data to storage', error);\n })\n .finally(() => {\n this.saveTimer = null;\n });\n }, SAVE_INTERVAL_MS);\n }\n\n if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n this._flush()\n .catch((error) => {\n this.logger.debug('DiagnosticsClient: Failed to flush', error);\n })\n .finally(() => {\n this.flushTimer = null;\n });\n }, FLUSH_INTERVAL_MS);\n }\n }\n\n async saveAllDataToStorage() {\n if (!this.storage) {\n return;\n }\n const tagsToSave = { ...this.inMemoryTags };\n const countersToSave = { ...this.inMemoryCounters };\n const histogramsToSave = { ...this.inMemoryHistograms };\n const eventsToSave = [...this.inMemoryEvents];\n\n this.inMemoryEvents = [];\n this.inMemoryTags = {};\n this.inMemoryCounters = {};\n this.inMemoryHistograms = {};\n\n await Promise.all([\n this.storage.setTags(tagsToSave),\n this.storage.incrementCounters(countersToSave),\n this.storage.setHistogramStats(histogramsToSave),\n this.storage.addEventRecords(eventsToSave),\n ]);\n }\n\n async _flush() {\n if (!this.storage) {\n return;\n }\n\n await this.saveAllDataToStorage();\n this.saveTimer = null;\n this.flushTimer = null;\n\n // Get all data from storage and clear it\n const {\n tags: tagRecords,\n counters: counterRecords,\n histogramStats: histogramStatsRecords,\n events: eventRecords,\n } = await this.storage.getAllAndClear();\n\n // Update the last flush timestamp\n void this.storage.setLastFlushTimestamp(Date.now());\n\n // Convert metrics to the expected format\n const tags: DiagnosticsTags = {};\n tagRecords.forEach((record) => {\n tags[record.key] = record.value;\n });\n\n const counters: DiagnosticsCounters = {};\n counterRecords.forEach((record) => {\n counters[record.key] = record.value;\n });\n\n const histogram: DiagnosticsHistograms = {};\n histogramStatsRecords.forEach((stats) => {\n histogram[stats.key] = {\n count: stats.count,\n min: stats.min,\n max: stats.max,\n avg: Math.round((stats.sum / stats.count) * 100) / 100, // round the average to 2 decimal places.\n };\n });\n\n const events: DiagnosticsEvent[] = eventRecords.map((record) => ({\n event_name: record.event_name,\n time: record.time,\n event_properties: record.event_properties,\n }));\n\n // Early return if all data collections are empty\n if (\n Object.keys(tags).length === 0 &&\n Object.keys(counters).length === 0 &&\n Object.keys(histogram).length === 0 &&\n events.length === 0\n ) {\n return;\n }\n\n // Create flush payload\n const payload: FlushPayload = {\n tags,\n histogram,\n counters,\n events,\n };\n\n // Send payload to diagnostics server\n void this.fetch(payload);\n }\n\n /**\n * Send diagnostics data to the server\n */\n async fetch(payload: FlushPayload) {\n try {\n if (!getGlobalScope()) {\n throw new Error('DiagnosticsClient: Fetch is not supported');\n }\n\n const response = await fetch(this.serverUrl, {\n method: 'POST',\n headers: {\n 'X-ApiKey': this.apiKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n this.logger.debug('DiagnosticsClient: Failed to send diagnostics data.');\n return;\n }\n\n this.logger.debug('DiagnosticsClient: Successfully sent diagnostics data');\n } catch (error) {\n this.logger.debug('DiagnosticsClient: Failed to send diagnostics data. ', error);\n }\n }\n\n /**\n * Initialize flush interval logic.\n * Check if 5 minutes has passed since last flush, if so flush immediately.\n * Otherwise set a timer to flush when the interval is reached.\n */\n async initializeFlushInterval() {\n if (!this.storage) {\n return;\n }\n const now = Date.now();\n const lastFlushTimestamp = (await this.storage.getLastFlushTimestamp()) || -1;\n const timeSinceLastFlush = now - lastFlushTimestamp;\n\n // If last flush timestamp is -1, it means this is a new client\n if (lastFlushTimestamp === -1) {\n return;\n } else if (timeSinceLastFlush >= FLUSH_INTERVAL_MS) {\n // More than 5 minutes has passed, flush immediately\n void this._flush();\n return;\n } else {\n // Set timer for remaining time\n const remainingTime = FLUSH_INTERVAL_MS - timeSinceLastFlush;\n this.flushTimer = setTimeout(() => {\n this._flush()\n .catch((error) => {\n this.logger.debug('DiagnosticsClient: Failed to flush', error);\n })\n .finally(() => {\n this.flushTimer = null;\n });\n }, remainingTime);\n }\n }\n}\n"]}
|