@safaricom-mxl/web-sdk 0.0.7 → 0.0.8

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.
Files changed (29) hide show
  1. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Data/listenToRouterChanges.cjs +12 -11
  2. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Data/listenToRouterChanges.cjs.map +1 -1
  3. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Data/listenToRouterChanges.d.cts.map +1 -1
  4. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Data/listenToRouterChanges.d.ts.map +1 -1
  5. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Data/listenToRouterChanges.js +12 -11
  6. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Data/listenToRouterChanges.js.map +1 -1
  7. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Declarative/withMXLRouting.cjs +2 -1
  8. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Declarative/withMXLRouting.cjs.map +1 -1
  9. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Declarative/withMXLRouting.d.cts.map +1 -1
  10. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Declarative/withMXLRouting.d.ts.map +1 -1
  11. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Declarative/withMXLRouting.js +2 -1
  12. package/dist/instrumentations/navigation/NavigationInstrumentation/react/reactRouterV6Declarative/withMXLRouting.js.map +1 -1
  13. package/dist/mxl-web-sdk.js +1 -1
  14. package/dist/mxl-web-sdk.js.map +1 -1
  15. package/dist/resources/constants/index.cjs +1 -1
  16. package/dist/resources/constants/index.cjs.map +1 -1
  17. package/dist/resources/constants/index.d.cts +1 -1
  18. package/dist/resources/constants/index.d.ts +1 -1
  19. package/dist/resources/constants/index.js +1 -1
  20. package/dist/resources/constants/index.js.map +1 -1
  21. package/dist/transport/FetchTransport/FetchTransport.cjs +60 -10
  22. package/dist/transport/FetchTransport/FetchTransport.cjs.map +1 -1
  23. package/dist/transport/FetchTransport/FetchTransport.d.cts +3 -1
  24. package/dist/transport/FetchTransport/FetchTransport.d.cts.map +1 -1
  25. package/dist/transport/FetchTransport/FetchTransport.d.ts +3 -1
  26. package/dist/transport/FetchTransport/FetchTransport.d.ts.map +1 -1
  27. package/dist/transport/FetchTransport/FetchTransport.js +60 -11
  28. package/dist/transport/FetchTransport/FetchTransport.js.map +1 -1
  29. package/package.json +1 -1
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  //#region src/resources/constants/index.ts
4
4
  const TEMPLATE_APP_VERSION = "MXLAppVersionX.X.X";
5
- const SDK_VERSION = "0.0.7";
5
+ const SDK_VERSION = "0.0.8";
6
6
  const MXL_SERVICE_NAME = "mxl-web-sdk";
7
7
  const NATIVE_FRAMEWORK = 1;
8
8
  const MXL_APP_INSTANCE_ID_STORAGE_KEY = "mxl_app_instance_id";
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/resources/constants/index.ts"],"sourcesContent":["export const TEMPLATE_APP_VERSION = 'MXLAppVersionX.X.X';\nexport const SDK_VERSION = '0.0.7';\nexport const MXL_SERVICE_NAME = 'mxl-web-sdk';\n// NATIVE_FRAMEWORK is the representation MXL BE uses to differentiate between\n// sdks. It is an enum, so we just send a number here\nexport const NATIVE_FRAMEWORK = 1;\nexport const MXL_APP_INSTANCE_ID_STORAGE_KEY = 'mxl_app_instance_id';\n"],"mappings":";;;AAAA,MAAa,uBAAuB;AACpC,MAAa,cAAc;AAC3B,MAAa,mBAAmB;AAGhC,MAAa,mBAAmB;AAChC,MAAa,kCAAkC"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/resources/constants/index.ts"],"sourcesContent":["export const TEMPLATE_APP_VERSION = 'MXLAppVersionX.X.X';\nexport const SDK_VERSION = '0.0.8';\nexport const MXL_SERVICE_NAME = 'mxl-web-sdk';\n// NATIVE_FRAMEWORK is the representation MXL BE uses to differentiate between\n// sdks. It is an enum, so we just send a number here\nexport const NATIVE_FRAMEWORK = 1;\nexport const MXL_APP_INSTANCE_ID_STORAGE_KEY = 'mxl_app_instance_id';\n"],"mappings":";;;AAAA,MAAa,uBAAuB;AACpC,MAAa,cAAc;AAC3B,MAAa,mBAAmB;AAGhC,MAAa,mBAAmB;AAChC,MAAa,kCAAkC"}
@@ -1,6 +1,6 @@
1
1
  //#region src/resources/constants/index.d.ts
2
2
  declare const TEMPLATE_APP_VERSION = "MXLAppVersionX.X.X";
3
- declare const SDK_VERSION = "0.0.7";
3
+ declare const SDK_VERSION = "0.0.8";
4
4
  declare const MXL_SERVICE_NAME = "mxl-web-sdk";
5
5
  declare const NATIVE_FRAMEWORK = 1;
6
6
  declare const MXL_APP_INSTANCE_ID_STORAGE_KEY = "mxl_app_instance_id";
@@ -1,6 +1,6 @@
1
1
  //#region src/resources/constants/index.d.ts
2
2
  declare const TEMPLATE_APP_VERSION = "MXLAppVersionX.X.X";
3
- declare const SDK_VERSION = "0.0.7";
3
+ declare const SDK_VERSION = "0.0.8";
4
4
  declare const MXL_SERVICE_NAME = "mxl-web-sdk";
5
5
  declare const NATIVE_FRAMEWORK = 1;
6
6
  declare const MXL_APP_INSTANCE_ID_STORAGE_KEY = "mxl_app_instance_id";
@@ -1,6 +1,6 @@
1
1
  //#region src/resources/constants/index.ts
2
2
  const TEMPLATE_APP_VERSION = "MXLAppVersionX.X.X";
3
- const SDK_VERSION = "0.0.7";
3
+ const SDK_VERSION = "0.0.8";
4
4
  const MXL_SERVICE_NAME = "mxl-web-sdk";
5
5
  const NATIVE_FRAMEWORK = 1;
6
6
  const MXL_APP_INSTANCE_ID_STORAGE_KEY = "mxl_app_instance_id";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/resources/constants/index.ts"],"sourcesContent":["export const TEMPLATE_APP_VERSION = 'MXLAppVersionX.X.X';\nexport const SDK_VERSION = '0.0.7';\nexport const MXL_SERVICE_NAME = 'mxl-web-sdk';\n// NATIVE_FRAMEWORK is the representation MXL BE uses to differentiate between\n// sdks. It is an enum, so we just send a number here\nexport const NATIVE_FRAMEWORK = 1;\nexport const MXL_APP_INSTANCE_ID_STORAGE_KEY = 'mxl_app_instance_id';\n"],"mappings":";AAAA,MAAa,uBAAuB;AACpC,MAAa,cAAc;AAC3B,MAAa,mBAAmB;AAGhC,MAAa,mBAAmB;AAChC,MAAa,kCAAkC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/resources/constants/index.ts"],"sourcesContent":["export const TEMPLATE_APP_VERSION = 'MXLAppVersionX.X.X';\nexport const SDK_VERSION = '0.0.8';\nexport const MXL_SERVICE_NAME = 'mxl-web-sdk';\n// NATIVE_FRAMEWORK is the representation MXL BE uses to differentiate between\n// sdks. It is an enum, so we just send a number here\nexport const NATIVE_FRAMEWORK = 1;\nexport const MXL_APP_INSTANCE_ID_STORAGE_KEY = 'mxl_app_instance_id';\n"],"mappings":";AAAA,MAAa,uBAAuB;AACpC,MAAa,cAAc;AAC3B,MAAa,mBAAmB;AAGhC,MAAa,mBAAmB;AAChC,MAAa,kCAAkC"}
@@ -1,6 +1,17 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
3
+ let _opentelemetry_api = require("@opentelemetry/api");
2
4
 
3
5
  //#region src/transport/FetchTransport/FetchTransport.ts
6
+ const KEEPALIVE_BYTE_BUDGET = 49152;
7
+ const MAX_KEEPALIVE_REQUESTS = 9;
8
+ let inflightKeepaliveBytes = 0;
9
+ let inflightKeepaliveCount = 0;
10
+ /** @internal for testing only */
11
+ function _resetKeepaliveTracking() {
12
+ inflightKeepaliveBytes = 0;
13
+ inflightKeepaliveCount = 0;
14
+ }
4
15
  var FetchTransport = class FetchTransport {
5
16
  constructor(_config) {
6
17
  this._config = _config;
@@ -36,11 +47,6 @@ var FetchTransport = class FetchTransport {
36
47
  "Content-Type": "application/json",
37
48
  ...this._config.headers
38
49
  };
39
- if (this._config.compression === "gzip") {
40
- request = await FetchTransport._compressRequest(data);
41
- headers["Content-Encoding"] = "gzip";
42
- headers["Content-Length"] = request.length.toString();
43
- }
44
50
  let signal;
45
51
  let timeoutId;
46
52
  if ("timeout" in AbortSignal) signal = AbortSignal.timeout(timeoutMillis);
@@ -51,30 +57,74 @@ var FetchTransport = class FetchTransport {
51
57
  controller.abort(new DOMException("TimeoutError", "TimeoutError"));
52
58
  }, timeoutMillis);
53
59
  }
60
+ let keepalive = false;
54
61
  try {
62
+ if (this._config.compression === "gzip") {
63
+ try {
64
+ request = await FetchTransport._compressRequest(data);
65
+ } catch (error) {
66
+ const compressError = error instanceof Error ? error : new Error(String(error));
67
+ _opentelemetry_api.diag.warn(`Fetch transport gzip compression failed: ${compressError.message}`);
68
+ return {
69
+ status: "failure",
70
+ error: compressError
71
+ };
72
+ }
73
+ headers["Content-Encoding"] = "gzip";
74
+ headers["Content-Length"] = request.length.toString();
75
+ }
76
+ const wouldExceedBytes = inflightKeepaliveBytes + request.byteLength > KEEPALIVE_BYTE_BUDGET;
77
+ keepalive = !wouldExceedBytes && !(inflightKeepaliveCount >= MAX_KEEPALIVE_REQUESTS);
78
+ if (keepalive) {
79
+ inflightKeepaliveBytes += request.byteLength;
80
+ inflightKeepaliveCount++;
81
+ } else {
82
+ const reason = wouldExceedBytes ? `inflight bytes (${inflightKeepaliveBytes} + ${request.byteLength}) would exceed ${KEEPALIVE_BYTE_BUDGET}B budget` : `concurrent count (${inflightKeepaliveCount}) at limit of ${MAX_KEEPALIVE_REQUESTS}`;
83
+ _opentelemetry_api.diag.debug(`Sending without keepalive: ${reason}`);
84
+ }
55
85
  const response = await fetch(this._config.url, {
56
86
  method: "POST",
57
- keepalive: true,
87
+ keepalive,
58
88
  headers,
59
89
  body: request,
60
90
  signal
61
91
  });
62
92
  if (response.ok) return { status: "success" };
63
- else return {
93
+ const error = /* @__PURE__ */ new Error(`${response.status} Fetch request failed`);
94
+ const message = `Fetch transport received HTTP ${response.status} (keepalive=${keepalive})`;
95
+ if (response.status >= 500) {
96
+ _opentelemetry_api.diag.debug(message);
97
+ return {
98
+ status: "retryable",
99
+ error
100
+ };
101
+ }
102
+ _opentelemetry_api.diag.warn(message);
103
+ return {
64
104
  status: "failure",
65
- error: /* @__PURE__ */ new Error(`${response.status} Fetch request failed`)
105
+ error
66
106
  };
67
107
  } catch (error) {
108
+ const fetchError = error instanceof Error ? error : new Error(String(error));
109
+ const isRetryable = fetchError instanceof TypeError || fetchError instanceof DOMException && fetchError.name === "TimeoutError";
110
+ const message = `Fetch transport failed (keepalive=${keepalive}): ${fetchError.message}`;
111
+ if (isRetryable) _opentelemetry_api.diag.debug(message);
112
+ else _opentelemetry_api.diag.warn(message);
68
113
  return {
69
- status: "failure",
70
- error: error instanceof Error ? error : new Error(String(error))
114
+ status: isRetryable ? "retryable" : "failure",
115
+ error: fetchError
71
116
  };
72
117
  } finally {
73
118
  if (timeoutId !== void 0) clearTimeout(timeoutId);
119
+ if (keepalive) {
120
+ inflightKeepaliveBytes -= request.byteLength;
121
+ inflightKeepaliveCount--;
122
+ }
74
123
  }
75
124
  }
76
125
  };
77
126
 
78
127
  //#endregion
79
128
  exports.FetchTransport = FetchTransport;
129
+ exports._resetKeepaliveTracking = _resetKeepaliveTracking;
80
130
  //# sourceMappingURL=FetchTransport.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"FetchTransport.cjs","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"sourcesContent":["import type {\n ExportResponse,\n IExporterTransport,\n} from '@opentelemetry/otlp-exporter-base';\nimport type { FetchRequestParameters } from './types.ts';\n\nexport class FetchTransport implements IExporterTransport {\n public constructor(private readonly _config: FetchRequestParameters) {}\n\n // _compressRequest compresses the data using the gzip algorithm.\n // MXL Data endpoints require the data to be compressed.\n private static async _compressRequest(\n data: Uint8Array<ArrayBuffer>,\n ): Promise<Uint8Array<ArrayBuffer>> {\n const stream = new CompressionStream('gzip');\n const writer = stream.writable.getWriter();\n\n void writer.write(data);\n void writer.close();\n\n const compressedChunks: Uint8Array[] = [];\n const reader = stream.readable.getReader();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n\n if (result.value) {\n compressedChunks.push(result.value);\n }\n\n done = result.done;\n }\n\n const compressedData = new Uint8Array(\n compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0),\n );\n\n let offset = 0;\n\n for (const chunk of compressedChunks) {\n compressedData.set(chunk, offset);\n offset += chunk.length;\n }\n\n return compressedData;\n }\n\n public send(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n return this._asyncSend(data, timeoutMillis);\n }\n\n public shutdown(): void {\n // Intentionally left empty, nothing to do.\n }\n\n public async _asyncSend(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n let request = data;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this._config.headers,\n };\n\n if (this._config.compression === 'gzip') {\n request = await FetchTransport._compressRequest(data);\n\n headers['Content-Encoding'] = 'gzip';\n headers['Content-Length'] = request.length.toString();\n }\n\n // Use AbortSignal.timeout if available, otherwise fallback to AbortController\n // https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static\n let signal: AbortSignal;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if ('timeout' in AbortSignal) {\n signal = AbortSignal.timeout(timeoutMillis);\n } else {\n const controller = new AbortController();\n signal = controller.signal;\n timeoutId = setTimeout(() => {\n controller.abort(new DOMException('TimeoutError', 'TimeoutError'));\n }, timeoutMillis);\n }\n\n try {\n const response = await fetch(this._config.url, {\n method: 'POST',\n keepalive: true,\n headers,\n body: request,\n signal,\n });\n\n if (response.ok) {\n return { status: 'success' };\n } else {\n return {\n status: 'failure',\n error: new Error(`${response.status} Fetch request failed`),\n };\n }\n } catch (error) {\n return {\n status: 'failure',\n error: error instanceof Error ? error : new Error(String(error)),\n };\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n}\n"],"mappings":";;;AAMA,IAAa,iBAAb,MAAa,eAA6C;CACxD,AAAO,YAAY,AAAiB,SAAiC;EAAjC;;CAIpC,aAAqB,iBACnB,MACkC;EAClC,MAAM,SAAS,IAAI,kBAAkB,OAAO;EAC5C,MAAM,SAAS,OAAO,SAAS,WAAW;AAE1C,EAAK,OAAO,MAAM,KAAK;AACvB,EAAK,OAAO,OAAO;EAEnB,MAAM,mBAAiC,EAAE;EACzC,MAAM,SAAS,OAAO,SAAS,WAAW;EAE1C,IAAI,OAAO;AACX,SAAO,CAAC,MAAM;GACZ,MAAM,SAAS,MAAM,OAAO,MAAM;AAElC,OAAI,OAAO,MACT,kBAAiB,KAAK,OAAO,MAAM;AAGrC,UAAO,OAAO;;EAGhB,MAAM,iBAAiB,IAAI,WACzB,iBAAiB,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,EAAE,CAC/D;EAED,IAAI,SAAS;AAEb,OAAK,MAAM,SAAS,kBAAkB;AACpC,kBAAe,IAAI,OAAO,OAAO;AACjC,aAAU,MAAM;;AAGlB,SAAO;;CAGT,AAAO,KACL,MACA,eACyB;AACzB,SAAO,KAAK,WAAW,MAAM,cAAc;;CAG7C,AAAO,WAAiB;CAIxB,MAAa,WACX,MACA,eACyB;EACzB,IAAI,UAAU;EACd,MAAM,UAAkC;GACtC,gBAAgB;GAChB,GAAG,KAAK,QAAQ;GACjB;AAED,MAAI,KAAK,QAAQ,gBAAgB,QAAQ;AACvC,aAAU,MAAM,eAAe,iBAAiB,KAAK;AAErD,WAAQ,sBAAsB;AAC9B,WAAQ,oBAAoB,QAAQ,OAAO,UAAU;;EAKvD,IAAI;EACJ,IAAI;AAEJ,MAAI,aAAa,YACf,UAAS,YAAY,QAAQ,cAAc;OACtC;GACL,MAAM,aAAa,IAAI,iBAAiB;AACxC,YAAS,WAAW;AACpB,eAAY,iBAAiB;AAC3B,eAAW,MAAM,IAAI,aAAa,gBAAgB,eAAe,CAAC;MACjE,cAAc;;AAGnB,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK,QAAQ,KAAK;IAC7C,QAAQ;IACR,WAAW;IACX;IACA,MAAM;IACN;IACD,CAAC;AAEF,OAAI,SAAS,GACX,QAAO,EAAE,QAAQ,WAAW;OAE5B,QAAO;IACL,QAAQ;IACR,uBAAO,IAAI,MAAM,GAAG,SAAS,OAAO,uBAAuB;IAC5D;WAEI,OAAO;AACd,UAAO;IACL,QAAQ;IACR,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;IACjE;YACO;AACR,OAAI,cAAc,OAChB,cAAa,UAAU"}
1
+ {"version":3,"file":"FetchTransport.cjs","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"sourcesContent":["import { diag } from '@opentelemetry/api';\nimport type {\n ExportResponse,\n IExporterTransport,\n} from '@opentelemetry/otlp-exporter-base';\nimport type { FetchRequestParameters } from './types.ts';\n\n// The fetch spec limits inflight keepalive request bodies to 64KiB total per\n// page context. We use a conservative budget to leave room for third-party\n// scripts that may also use keepalive or sendBeacon.\n// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch\nconst KEEPALIVE_BYTE_BUDGET = 49152; // 48KiB\n\n// Chrome enforces a 9-request concurrent keepalive limit per renderer process.\nconst MAX_KEEPALIVE_REQUESTS = 9;\n\nlet inflightKeepaliveBytes = 0;\nlet inflightKeepaliveCount = 0;\n\n/** @internal for testing only */\nexport function _resetKeepaliveTracking(): void {\n inflightKeepaliveBytes = 0;\n inflightKeepaliveCount = 0;\n}\n\nexport class FetchTransport implements IExporterTransport {\n public constructor(private readonly _config: FetchRequestParameters) {}\n\n // Embrace endpoints require request bodies to be compressed with gzip\n private static async _compressRequest(\n data: Uint8Array<ArrayBuffer>,\n ): Promise<Uint8Array<ArrayBuffer>> {\n const stream = new CompressionStream('gzip');\n const writer = stream.writable.getWriter();\n\n void writer.write(data);\n void writer.close();\n\n const compressedChunks: Uint8Array[] = [];\n const reader = stream.readable.getReader();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n\n if (result.value) {\n compressedChunks.push(result.value);\n }\n\n done = result.done;\n }\n\n const compressedData = new Uint8Array(\n compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0),\n );\n\n let offset = 0;\n\n for (const chunk of compressedChunks) {\n compressedData.set(chunk, offset);\n offset += chunk.length;\n }\n\n return compressedData;\n }\n\n public send(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n return this._asyncSend(data, timeoutMillis);\n }\n\n public shutdown(): void {\n // Intentionally left empty, nothing to do.\n }\n\n public async _asyncSend(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n let request = data;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this._config.headers,\n };\n\n // Use AbortSignal.timeout if available, otherwise fallback to AbortController\n // https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static\n let signal: AbortSignal;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if ('timeout' in AbortSignal) {\n // ok to ignore because there is a guard\n signal = AbortSignal.timeout(timeoutMillis);\n } else {\n const controller = new AbortController();\n signal = controller.signal;\n timeoutId = setTimeout(() => {\n controller.abort(new DOMException('TimeoutError', 'TimeoutError'));\n }, timeoutMillis);\n }\n\n let keepalive = false;\n\n try {\n if (this._config.compression === 'gzip') {\n try {\n request = await FetchTransport._compressRequest(data);\n } catch (error) {\n const compressError =\n error instanceof Error ? error : new Error(String(error));\n diag.warn(\n `Fetch transport gzip compression failed: ${compressError.message}`,\n );\n return { status: 'failure', error: compressError };\n }\n headers['Content-Encoding'] = 'gzip';\n headers['Content-Length'] = request.length.toString();\n }\n\n const wouldExceedBytes =\n inflightKeepaliveBytes + request.byteLength > KEEPALIVE_BYTE_BUDGET;\n const wouldExceedCount = inflightKeepaliveCount >= MAX_KEEPALIVE_REQUESTS;\n keepalive = !wouldExceedBytes && !wouldExceedCount;\n\n if (keepalive) {\n inflightKeepaliveBytes += request.byteLength;\n inflightKeepaliveCount++;\n } else {\n const reason = wouldExceedBytes\n ? `inflight bytes (${inflightKeepaliveBytes} + ${request.byteLength}) would exceed ${KEEPALIVE_BYTE_BUDGET}B budget`\n : `concurrent count (${inflightKeepaliveCount}) at limit of ${MAX_KEEPALIVE_REQUESTS}`;\n diag.debug(`Sending without keepalive: ${reason}`);\n }\n\n const response = await fetch(this._config.url, {\n method: 'POST',\n keepalive,\n headers,\n body: request,\n signal,\n });\n\n if (response.ok) {\n return { status: 'success' };\n }\n\n const error = new Error(`${response.status} Fetch request failed`);\n const message = `Fetch transport received HTTP ${response.status} (keepalive=${keepalive})`;\n if (response.status >= 500) {\n diag.debug(message);\n return { status: 'retryable', error };\n }\n diag.warn(message);\n return { status: 'failure', error };\n } catch (error) {\n const fetchError =\n error instanceof Error ? error : new Error(String(error));\n const isRetryable =\n fetchError instanceof TypeError ||\n (fetchError instanceof DOMException &&\n fetchError.name === 'TimeoutError');\n const message = `Fetch transport failed (keepalive=${keepalive}): ${fetchError.message}`;\n if (isRetryable) {\n diag.debug(message);\n } else {\n diag.warn(message);\n }\n return {\n status: isRetryable ? 'retryable' : 'failure',\n error: fetchError,\n };\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n if (keepalive) {\n inflightKeepaliveBytes -= request.byteLength;\n inflightKeepaliveCount--;\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -2,6 +2,8 @@ import { FetchRequestParameters } from "./types.cjs";
2
2
  import { ExportResponse, IExporterTransport } from "@opentelemetry/otlp-exporter-base";
3
3
 
4
4
  //#region src/transport/FetchTransport/FetchTransport.d.ts
5
+ /** @internal for testing only */
6
+ declare function _resetKeepaliveTracking(): void;
5
7
  declare class FetchTransport implements IExporterTransport {
6
8
  private readonly _config;
7
9
  constructor(_config: FetchRequestParameters);
@@ -11,5 +13,5 @@ declare class FetchTransport implements IExporterTransport {
11
13
  _asyncSend(data: Uint8Array<ArrayBuffer>, timeoutMillis: number): Promise<ExportResponse>;
12
14
  }
13
15
  //#endregion
14
- export { FetchTransport };
16
+ export { FetchTransport, _resetKeepaliveTracking };
15
17
  //# sourceMappingURL=FetchTransport.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FetchTransport.d.cts","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"mappings":";;;;cAMa,cAAA,YAA0B,kBAAA;EAAA,iBACD,OAAA;cAAA,OAAA,EAAS,sBAAA;EAAA,eAIxB,gBAAA;EAqCd,IAAA,CACL,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;EAIJ,QAAA,CAAA;EAIM,UAAA,CACX,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;AAAA"}
1
+ {"version":3,"file":"FetchTransport.d.cts","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"mappings":";;;;;iBAoBgB,uBAAA,CAAA;AAAA,cAKH,cAAA,YAA0B,kBAAA;EAAA,iBACD,OAAA;cAAA,OAAA,EAAS,sBAAA;EAAA,eAGxB,gBAAA;EAqCd,IAAA,CACL,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;EAIJ,QAAA,CAAA;EAIM,UAAA,CACX,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;AAAA"}
@@ -2,6 +2,8 @@ import { FetchRequestParameters } from "./types.js";
2
2
  import { ExportResponse, IExporterTransport } from "@opentelemetry/otlp-exporter-base";
3
3
 
4
4
  //#region src/transport/FetchTransport/FetchTransport.d.ts
5
+ /** @internal for testing only */
6
+ declare function _resetKeepaliveTracking(): void;
5
7
  declare class FetchTransport implements IExporterTransport {
6
8
  private readonly _config;
7
9
  constructor(_config: FetchRequestParameters);
@@ -11,5 +13,5 @@ declare class FetchTransport implements IExporterTransport {
11
13
  _asyncSend(data: Uint8Array<ArrayBuffer>, timeoutMillis: number): Promise<ExportResponse>;
12
14
  }
13
15
  //#endregion
14
- export { FetchTransport };
16
+ export { FetchTransport, _resetKeepaliveTracking };
15
17
  //# sourceMappingURL=FetchTransport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FetchTransport.d.ts","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"mappings":";;;;cAMa,cAAA,YAA0B,kBAAA;EAAA,iBACD,OAAA;cAAA,OAAA,EAAS,sBAAA;EAAA,eAIxB,gBAAA;EAqCd,IAAA,CACL,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;EAIJ,QAAA,CAAA;EAIM,UAAA,CACX,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;AAAA"}
1
+ {"version":3,"file":"FetchTransport.d.ts","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"mappings":";;;;;iBAoBgB,uBAAA,CAAA;AAAA,cAKH,cAAA,YAA0B,kBAAA;EAAA,iBACD,OAAA;cAAA,OAAA,EAAS,sBAAA;EAAA,eAGxB,gBAAA;EAqCd,IAAA,CACL,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;EAIJ,QAAA,CAAA;EAIM,UAAA,CACX,IAAA,EAAM,UAAA,CAAW,WAAA,GACjB,aAAA,WACC,OAAA,CAAQ,cAAA;AAAA"}
@@ -1,4 +1,15 @@
1
+ import { diag } from "@opentelemetry/api";
2
+
1
3
  //#region src/transport/FetchTransport/FetchTransport.ts
4
+ const KEEPALIVE_BYTE_BUDGET = 49152;
5
+ const MAX_KEEPALIVE_REQUESTS = 9;
6
+ let inflightKeepaliveBytes = 0;
7
+ let inflightKeepaliveCount = 0;
8
+ /** @internal for testing only */
9
+ function _resetKeepaliveTracking() {
10
+ inflightKeepaliveBytes = 0;
11
+ inflightKeepaliveCount = 0;
12
+ }
2
13
  var FetchTransport = class FetchTransport {
3
14
  constructor(_config) {
4
15
  this._config = _config;
@@ -34,11 +45,6 @@ var FetchTransport = class FetchTransport {
34
45
  "Content-Type": "application/json",
35
46
  ...this._config.headers
36
47
  };
37
- if (this._config.compression === "gzip") {
38
- request = await FetchTransport._compressRequest(data);
39
- headers["Content-Encoding"] = "gzip";
40
- headers["Content-Length"] = request.length.toString();
41
- }
42
48
  let signal;
43
49
  let timeoutId;
44
50
  if ("timeout" in AbortSignal) signal = AbortSignal.timeout(timeoutMillis);
@@ -49,30 +55,73 @@ var FetchTransport = class FetchTransport {
49
55
  controller.abort(new DOMException("TimeoutError", "TimeoutError"));
50
56
  }, timeoutMillis);
51
57
  }
58
+ let keepalive = false;
52
59
  try {
60
+ if (this._config.compression === "gzip") {
61
+ try {
62
+ request = await FetchTransport._compressRequest(data);
63
+ } catch (error) {
64
+ const compressError = error instanceof Error ? error : new Error(String(error));
65
+ diag.warn(`Fetch transport gzip compression failed: ${compressError.message}`);
66
+ return {
67
+ status: "failure",
68
+ error: compressError
69
+ };
70
+ }
71
+ headers["Content-Encoding"] = "gzip";
72
+ headers["Content-Length"] = request.length.toString();
73
+ }
74
+ const wouldExceedBytes = inflightKeepaliveBytes + request.byteLength > KEEPALIVE_BYTE_BUDGET;
75
+ keepalive = !wouldExceedBytes && !(inflightKeepaliveCount >= MAX_KEEPALIVE_REQUESTS);
76
+ if (keepalive) {
77
+ inflightKeepaliveBytes += request.byteLength;
78
+ inflightKeepaliveCount++;
79
+ } else {
80
+ const reason = wouldExceedBytes ? `inflight bytes (${inflightKeepaliveBytes} + ${request.byteLength}) would exceed ${KEEPALIVE_BYTE_BUDGET}B budget` : `concurrent count (${inflightKeepaliveCount}) at limit of ${MAX_KEEPALIVE_REQUESTS}`;
81
+ diag.debug(`Sending without keepalive: ${reason}`);
82
+ }
53
83
  const response = await fetch(this._config.url, {
54
84
  method: "POST",
55
- keepalive: true,
85
+ keepalive,
56
86
  headers,
57
87
  body: request,
58
88
  signal
59
89
  });
60
90
  if (response.ok) return { status: "success" };
61
- else return {
91
+ const error = /* @__PURE__ */ new Error(`${response.status} Fetch request failed`);
92
+ const message = `Fetch transport received HTTP ${response.status} (keepalive=${keepalive})`;
93
+ if (response.status >= 500) {
94
+ diag.debug(message);
95
+ return {
96
+ status: "retryable",
97
+ error
98
+ };
99
+ }
100
+ diag.warn(message);
101
+ return {
62
102
  status: "failure",
63
- error: /* @__PURE__ */ new Error(`${response.status} Fetch request failed`)
103
+ error
64
104
  };
65
105
  } catch (error) {
106
+ const fetchError = error instanceof Error ? error : new Error(String(error));
107
+ const isRetryable = fetchError instanceof TypeError || fetchError instanceof DOMException && fetchError.name === "TimeoutError";
108
+ const message = `Fetch transport failed (keepalive=${keepalive}): ${fetchError.message}`;
109
+ if (isRetryable) diag.debug(message);
110
+ else diag.warn(message);
66
111
  return {
67
- status: "failure",
68
- error: error instanceof Error ? error : new Error(String(error))
112
+ status: isRetryable ? "retryable" : "failure",
113
+ error: fetchError
69
114
  };
70
115
  } finally {
71
116
  if (timeoutId !== void 0) clearTimeout(timeoutId);
117
+ if (keepalive) {
118
+ inflightKeepaliveBytes -= request.byteLength;
119
+ inflightKeepaliveCount--;
120
+ }
72
121
  }
73
122
  }
74
123
  };
75
124
 
76
125
  //#endregion
77
- export { FetchTransport };
126
+ export { FetchTransport, _resetKeepaliveTracking };
78
127
  //# sourceMappingURL=FetchTransport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FetchTransport.js","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"sourcesContent":["import type {\n ExportResponse,\n IExporterTransport,\n} from '@opentelemetry/otlp-exporter-base';\nimport type { FetchRequestParameters } from './types.ts';\n\nexport class FetchTransport implements IExporterTransport {\n public constructor(private readonly _config: FetchRequestParameters) {}\n\n // _compressRequest compresses the data using the gzip algorithm.\n // MXL Data endpoints require the data to be compressed.\n private static async _compressRequest(\n data: Uint8Array<ArrayBuffer>,\n ): Promise<Uint8Array<ArrayBuffer>> {\n const stream = new CompressionStream('gzip');\n const writer = stream.writable.getWriter();\n\n void writer.write(data);\n void writer.close();\n\n const compressedChunks: Uint8Array[] = [];\n const reader = stream.readable.getReader();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n\n if (result.value) {\n compressedChunks.push(result.value);\n }\n\n done = result.done;\n }\n\n const compressedData = new Uint8Array(\n compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0),\n );\n\n let offset = 0;\n\n for (const chunk of compressedChunks) {\n compressedData.set(chunk, offset);\n offset += chunk.length;\n }\n\n return compressedData;\n }\n\n public send(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n return this._asyncSend(data, timeoutMillis);\n }\n\n public shutdown(): void {\n // Intentionally left empty, nothing to do.\n }\n\n public async _asyncSend(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n let request = data;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this._config.headers,\n };\n\n if (this._config.compression === 'gzip') {\n request = await FetchTransport._compressRequest(data);\n\n headers['Content-Encoding'] = 'gzip';\n headers['Content-Length'] = request.length.toString();\n }\n\n // Use AbortSignal.timeout if available, otherwise fallback to AbortController\n // https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static\n let signal: AbortSignal;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if ('timeout' in AbortSignal) {\n signal = AbortSignal.timeout(timeoutMillis);\n } else {\n const controller = new AbortController();\n signal = controller.signal;\n timeoutId = setTimeout(() => {\n controller.abort(new DOMException('TimeoutError', 'TimeoutError'));\n }, timeoutMillis);\n }\n\n try {\n const response = await fetch(this._config.url, {\n method: 'POST',\n keepalive: true,\n headers,\n body: request,\n signal,\n });\n\n if (response.ok) {\n return { status: 'success' };\n } else {\n return {\n status: 'failure',\n error: new Error(`${response.status} Fetch request failed`),\n };\n }\n } catch (error) {\n return {\n status: 'failure',\n error: error instanceof Error ? error : new Error(String(error)),\n };\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n}\n"],"mappings":";AAMA,IAAa,iBAAb,MAAa,eAA6C;CACxD,AAAO,YAAY,AAAiB,SAAiC;EAAjC;;CAIpC,aAAqB,iBACnB,MACkC;EAClC,MAAM,SAAS,IAAI,kBAAkB,OAAO;EAC5C,MAAM,SAAS,OAAO,SAAS,WAAW;AAE1C,EAAK,OAAO,MAAM,KAAK;AACvB,EAAK,OAAO,OAAO;EAEnB,MAAM,mBAAiC,EAAE;EACzC,MAAM,SAAS,OAAO,SAAS,WAAW;EAE1C,IAAI,OAAO;AACX,SAAO,CAAC,MAAM;GACZ,MAAM,SAAS,MAAM,OAAO,MAAM;AAElC,OAAI,OAAO,MACT,kBAAiB,KAAK,OAAO,MAAM;AAGrC,UAAO,OAAO;;EAGhB,MAAM,iBAAiB,IAAI,WACzB,iBAAiB,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,EAAE,CAC/D;EAED,IAAI,SAAS;AAEb,OAAK,MAAM,SAAS,kBAAkB;AACpC,kBAAe,IAAI,OAAO,OAAO;AACjC,aAAU,MAAM;;AAGlB,SAAO;;CAGT,AAAO,KACL,MACA,eACyB;AACzB,SAAO,KAAK,WAAW,MAAM,cAAc;;CAG7C,AAAO,WAAiB;CAIxB,MAAa,WACX,MACA,eACyB;EACzB,IAAI,UAAU;EACd,MAAM,UAAkC;GACtC,gBAAgB;GAChB,GAAG,KAAK,QAAQ;GACjB;AAED,MAAI,KAAK,QAAQ,gBAAgB,QAAQ;AACvC,aAAU,MAAM,eAAe,iBAAiB,KAAK;AAErD,WAAQ,sBAAsB;AAC9B,WAAQ,oBAAoB,QAAQ,OAAO,UAAU;;EAKvD,IAAI;EACJ,IAAI;AAEJ,MAAI,aAAa,YACf,UAAS,YAAY,QAAQ,cAAc;OACtC;GACL,MAAM,aAAa,IAAI,iBAAiB;AACxC,YAAS,WAAW;AACpB,eAAY,iBAAiB;AAC3B,eAAW,MAAM,IAAI,aAAa,gBAAgB,eAAe,CAAC;MACjE,cAAc;;AAGnB,MAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK,QAAQ,KAAK;IAC7C,QAAQ;IACR,WAAW;IACX;IACA,MAAM;IACN;IACD,CAAC;AAEF,OAAI,SAAS,GACX,QAAO,EAAE,QAAQ,WAAW;OAE5B,QAAO;IACL,QAAQ;IACR,uBAAO,IAAI,MAAM,GAAG,SAAS,OAAO,uBAAuB;IAC5D;WAEI,OAAO;AACd,UAAO;IACL,QAAQ;IACR,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;IACjE;YACO;AACR,OAAI,cAAc,OAChB,cAAa,UAAU"}
1
+ {"version":3,"file":"FetchTransport.js","names":[],"sources":["../../../src/transport/FetchTransport/FetchTransport.ts"],"sourcesContent":["import { diag } from '@opentelemetry/api';\nimport type {\n ExportResponse,\n IExporterTransport,\n} from '@opentelemetry/otlp-exporter-base';\nimport type { FetchRequestParameters } from './types.ts';\n\n// The fetch spec limits inflight keepalive request bodies to 64KiB total per\n// page context. We use a conservative budget to leave room for third-party\n// scripts that may also use keepalive or sendBeacon.\n// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch\nconst KEEPALIVE_BYTE_BUDGET = 49152; // 48KiB\n\n// Chrome enforces a 9-request concurrent keepalive limit per renderer process.\nconst MAX_KEEPALIVE_REQUESTS = 9;\n\nlet inflightKeepaliveBytes = 0;\nlet inflightKeepaliveCount = 0;\n\n/** @internal for testing only */\nexport function _resetKeepaliveTracking(): void {\n inflightKeepaliveBytes = 0;\n inflightKeepaliveCount = 0;\n}\n\nexport class FetchTransport implements IExporterTransport {\n public constructor(private readonly _config: FetchRequestParameters) {}\n\n // Embrace endpoints require request bodies to be compressed with gzip\n private static async _compressRequest(\n data: Uint8Array<ArrayBuffer>,\n ): Promise<Uint8Array<ArrayBuffer>> {\n const stream = new CompressionStream('gzip');\n const writer = stream.writable.getWriter();\n\n void writer.write(data);\n void writer.close();\n\n const compressedChunks: Uint8Array[] = [];\n const reader = stream.readable.getReader();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n\n if (result.value) {\n compressedChunks.push(result.value);\n }\n\n done = result.done;\n }\n\n const compressedData = new Uint8Array(\n compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0),\n );\n\n let offset = 0;\n\n for (const chunk of compressedChunks) {\n compressedData.set(chunk, offset);\n offset += chunk.length;\n }\n\n return compressedData;\n }\n\n public send(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n return this._asyncSend(data, timeoutMillis);\n }\n\n public shutdown(): void {\n // Intentionally left empty, nothing to do.\n }\n\n public async _asyncSend(\n data: Uint8Array<ArrayBuffer>,\n timeoutMillis: number,\n ): Promise<ExportResponse> {\n let request = data;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this._config.headers,\n };\n\n // Use AbortSignal.timeout if available, otherwise fallback to AbortController\n // https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static\n let signal: AbortSignal;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if ('timeout' in AbortSignal) {\n // ok to ignore because there is a guard\n signal = AbortSignal.timeout(timeoutMillis);\n } else {\n const controller = new AbortController();\n signal = controller.signal;\n timeoutId = setTimeout(() => {\n controller.abort(new DOMException('TimeoutError', 'TimeoutError'));\n }, timeoutMillis);\n }\n\n let keepalive = false;\n\n try {\n if (this._config.compression === 'gzip') {\n try {\n request = await FetchTransport._compressRequest(data);\n } catch (error) {\n const compressError =\n error instanceof Error ? error : new Error(String(error));\n diag.warn(\n `Fetch transport gzip compression failed: ${compressError.message}`,\n );\n return { status: 'failure', error: compressError };\n }\n headers['Content-Encoding'] = 'gzip';\n headers['Content-Length'] = request.length.toString();\n }\n\n const wouldExceedBytes =\n inflightKeepaliveBytes + request.byteLength > KEEPALIVE_BYTE_BUDGET;\n const wouldExceedCount = inflightKeepaliveCount >= MAX_KEEPALIVE_REQUESTS;\n keepalive = !wouldExceedBytes && !wouldExceedCount;\n\n if (keepalive) {\n inflightKeepaliveBytes += request.byteLength;\n inflightKeepaliveCount++;\n } else {\n const reason = wouldExceedBytes\n ? `inflight bytes (${inflightKeepaliveBytes} + ${request.byteLength}) would exceed ${KEEPALIVE_BYTE_BUDGET}B budget`\n : `concurrent count (${inflightKeepaliveCount}) at limit of ${MAX_KEEPALIVE_REQUESTS}`;\n diag.debug(`Sending without keepalive: ${reason}`);\n }\n\n const response = await fetch(this._config.url, {\n method: 'POST',\n keepalive,\n headers,\n body: request,\n signal,\n });\n\n if (response.ok) {\n return { status: 'success' };\n }\n\n const error = new Error(`${response.status} Fetch request failed`);\n const message = `Fetch transport received HTTP ${response.status} (keepalive=${keepalive})`;\n if (response.status >= 500) {\n diag.debug(message);\n return { status: 'retryable', error };\n }\n diag.warn(message);\n return { status: 'failure', error };\n } catch (error) {\n const fetchError =\n error instanceof Error ? error : new Error(String(error));\n const isRetryable =\n fetchError instanceof TypeError ||\n (fetchError instanceof DOMException &&\n fetchError.name === 'TimeoutError');\n const message = `Fetch transport failed (keepalive=${keepalive}): ${fetchError.message}`;\n if (isRetryable) {\n diag.debug(message);\n } else {\n diag.warn(message);\n }\n return {\n status: isRetryable ? 'retryable' : 'failure',\n error: fetchError,\n };\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n if (keepalive) {\n inflightKeepaliveBytes -= request.byteLength;\n inflightKeepaliveCount--;\n }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safaricom-mxl/web-sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "MXL Web SDK",
5
5
  "type": "module",
6
6
  "imports": {