@ideem/zsm-client-sdk 2.3.0 → 2.4.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/RELEASE_NOTES.txt CHANGED
@@ -15,7 +15,15 @@ and Universal MFA into a browser application.
15
15
 
16
16
  ----
17
17
 
18
- ## Release 2.3.0 - Mar 24, 2025 [ NPM PACKAGE RELEASE ] **[ LATEST ]**
18
+ ## Release 2.4.0 - May 1, 2025 [ NPM PACKAGE RELEASE ] **[ LATEST ]**
19
+ - Minor bug fixes and improvements
20
+ - Updates to documentation
21
+ - Adds event emitters to the UMFAClient and FIDO2Client for on-the-fly initializations
22
+ - Adds extended multi-tenancy support for the UMFAClient and FIDO2Client
23
+ - Adds increased detail to performance logging
24
+ - Extends secure local storage support
25
+
26
+ ## Release 2.3.0 - Mar 24, 2025
19
27
  - Minor bug fixes and improvements
20
28
  - Updates to documentation
21
29
  - Introduces web-worker-driven JS SDK Client for browser-based applications
package/ZSMClientSDK.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // Alias environment-specific global object
2
- const GLOBAL = typeof globalThis !== "undefined" ? globalThis
3
- : typeof self !== "undefined" ? self
4
- : typeof window !== "undefined" ? window
5
- : typeof global !== "undefined" ? global
6
- : (() => { throw new Error("Unable to determine global scope"); })();
2
+ const GLOBAL = typeof globalThis !== "undefined" ? globalThis
3
+ : typeof self !== "undefined" ? self
4
+ : typeof window !== "undefined" ? window
5
+ : typeof global !== "undefined" ? global
6
+ : (() => { throw new Error("Unable to determine global scope"); })();
7
7
 
8
8
  // Error class for worker errors
9
9
  class WorkerError extends Error {
@@ -26,49 +26,84 @@ class ZSMClientBase {
26
26
  this.timeoutTimer = null;
27
27
  GLOBAL.zsmClient = null;
28
28
  }
29
-
30
- async init(clientType = 'UMFAClient', config = this.config) {
29
+
30
+ /**
31
+ * @name init
32
+ * @description Initializes the ZSMClient instance and sets up the worker.
33
+ * @param {String} clientType The type of client to initialize [default: 'UMFAClient']
34
+ * @param {Object} config The configuration object for the client [default: this.config]
35
+ * @returns {Promise<ZSMClientBase>} A promise that resolves to the initialized ZSMClient instance.
36
+ */
37
+ async init(clientType='UMFAClient', config=this.config) {
31
38
  if(GLOBAL.clientWorker) GLOBAL.clientWorker.terminate();
32
39
  const workerPath = GLOBAL?.workerPath ? GLOBAL.workerPath
33
- : config?.workerPath ? config.workerPath
34
- : './worker.js';
40
+ : config?.workerPath ? config.workerPath
41
+ : './worker.js';
35
42
  const pathResolver = (import.meta && import.meta?.resolve) ? import.meta?.resolve(workerPath)
36
- : (import.meta && import.meta?.url) ? import.meta?.url.replace(/\/[^\/]+$/, '/' + workerPath)
37
- : document?.currentScript ? document?.currentScript?.src?.replace(/\/[^\/]+$/, '/' + workerPath)
38
- : workerPath;
43
+ : (import.meta && import.meta?.url) ? import.meta?.url.replace(/\/[^\/]+$/, '/' + workerPath)
44
+ : document?.currentScript ? document?.currentScript?.src?.replace(/\/[^\/]+$/, '/' + workerPath)
45
+ : workerPath;
39
46
  GLOBAL.clientWorker = this.clientWorker = new Worker(new URL(pathResolver));
40
47
  GLOBAL.zsmClient = this;
41
48
  await this.callWorkerFunction("init", {config, clientType}, false);
42
49
  return this;
43
50
  }
44
-
45
- async callWorkerFunction(functionName, data, acknowledgementExpected=true) {
46
- return new Promise((resolve, reject) => {
51
+
52
+ /**
53
+ * @name callWorkerFunction
54
+ * @description Calls a function in the worker and returns a promise that resolves with the result.
55
+ * @param {String} functionName The name of the function to call in the worker.
56
+ * @param {Object} data The data to pass to the worker function.
57
+ * @param {Boolean} responseExpected Whether an acknowledgement is expected [Default: true]
58
+ * @returns {Promise<Object>} A promise that resolves with the result of the worker function.
59
+ */
60
+ async callWorkerFunction(functionName, data, responseExpected=true) {
61
+ let workerPromise = new Promise((resolve, reject) => {
47
62
  this.clientWorker.postMessage({functionName, data });
48
- if(acknowledgementExpected) this.timeoutTimer = GLOBAL.setTimeout(() => reject(new WorkerError('Transaction', 'Transaction timed out after 10 seconds!')), 10000);
49
-
63
+ if(responseExpected) this.timeoutTimer = GLOBAL.setTimeout(() => reject(new WorkerError('Transaction', 'Transaction timed out after 60 seconds!')), 60000);
64
+
50
65
  this.clientWorker.onmessage = (event) => {
51
66
  GLOBAL.clearTimeout(this.timeoutTimer);
52
-
67
+
53
68
  let workerResponse = event.data;
69
+
54
70
  if(GLOBAL.showWorkerTelemetry && (workerResponse.result || workerResponse.error)) {
55
71
  console.groupCollapsed(`%cWorker posted to Main Thread a response to invocation:`, 'font-weight:bold; color:#F60;', functionName)
56
72
  console.log(`%c Function Invoked:`, 'font-weight:100; color:#AAA;', workerResponse.functionName);
57
73
  console.log(`%c Success Status:`, 'font-weight:100; color:#AAA;', workerResponse.success ? 'true' : 'false');
58
- console.log(`%c Result value:`, 'font-weight:100; color:#AAA;', (workerResponse.success ? workerResponse.result : workerResponse.error));
74
+ console.log(`%c Result value:`, 'font-weight:100; color:#AAA;', (workerResponse.success ? workerResponse.result : workerResponse.error));
59
75
  console.log(`%c Response Object:`, 'font-weight:100; color:#AAA;', workerResponse);
60
76
  console.groupEnd();
61
77
  }
62
-
78
+
63
79
  if (workerResponse instanceof Error || workerResponse.error || !workerResponse.success) {
64
80
  return reject(workerResponse.error);
65
81
  }
66
82
  resolve(workerResponse.result);
67
83
  };
68
-
84
+
69
85
  this.clientWorker.onerror = (err) => { GLOBAL.clearTimeout(this.timeoutTimer); reject(new WorkerError('', err.message)); };
70
86
  this.clientWorker.onmessageerror = (err) => { GLOBAL.clearTimeout(this.timeoutTimer); reject(new WorkerError('Message', err.message)); };
71
87
  });
88
+
89
+ /**
90
+ * @event workerEvent
91
+ * @description Event triggered when the worker posts an event to the main thread.
92
+ * @param {String} eventName The name of the event.
93
+ * @param {Object} eventData The data associated with the event.
94
+ * @returns {void}
95
+ * @emits {CustomEvent} event The event object containing the received event name and data.
96
+ * @example GLOBAL.addEventListener('workerEvent', (event) => {
97
+ * @ console.log('Received event from worker:', event.detail.eventName, event.detail.eventData);
98
+ * @ });
99
+ */
100
+ this.clientWorker.addEventListener('message', (event) => {
101
+ if(!event.data || !event.data.eventName) return;
102
+ const { eventName, eventData } = event.data;
103
+ if(GLOBAL.showWorkerTelemetry) console.log(`%cWorker posted to Main Thread an event:`, 'font-weight:bold; color:#F60;', eventName, eventData);
104
+ GLOBAL.dispatchEvent(new CustomEvent(eventName, { detail: eventData }));
105
+ });
106
+ return workerPromise;
72
107
  }
73
108
  }
74
109
 
@@ -84,18 +119,18 @@ class UMFAClient extends ZSMClientBase {
84
119
  this.optimizedAuthenticate = this.optimizedAuthenticate.bind(this);
85
120
  this.init('UMFAClient');
86
121
  }
87
-
122
+
88
123
  async checkEnrollment(user) { return await this.callWorkerFunction('checkEnrollment', user); }
89
124
  async enroll(user) { return await this.callWorkerFunction('enroll', user); }
90
125
  async authenticate(user) { return await this.callWorkerFunction('authenticate', user); }
91
126
  async optimizedEnroll(user) { return await this.callWorkerFunction('optimizedEnroll', user); }
92
- async optimizedAuthenticate(user) { return await this.callWorkerFunction('optimizedAuthenticate', user); }
127
+ async optimizedAuthenticate(user) { return await this.callWorkerFunction('optimizedAuthenticate', user); }
93
128
  async unenroll(user) { return await this.callWorkerFunction('unenroll', user); }
94
129
  }
95
130
 
96
131
  class FIDO2Client extends ZSMClientBase {
97
132
  constructor(config) {
98
- super(config);
133
+ super(config);
99
134
  this.init = super.init.bind(this);
100
135
  this.callWorkerFunction = super.callWorkerFunction.bind(this);
101
136
  this.checkEnrollment = this.checkEnrollment.bind(this);
@@ -106,7 +141,7 @@ class FIDO2Client extends ZSMClientBase {
106
141
  this.webauthnDelete = this.webauthnDelete.bind(this);
107
142
  this.init('FIDO2Client');
108
143
  }
109
-
144
+
110
145
  async checkIdentity(user, auto=true) { return await this.callWorkerFunction('checkIdentity', [user, auto]); }
111
146
  async checkEnrollment(user) { return await this.callWorkerFunction('checkEnrollment', user); }
112
147
  async webauthnCreate(user) { return await this.callWorkerFunction('webauthnCreate', user); }
@@ -119,16 +154,16 @@ function amendPerformanceLoggingToClass(cls) {
119
154
  GLOBAL.sessionStamp = new Date();
120
155
  GLOBAL.sessionStamp = new Date(GLOBAL.sessionStamp.getTime() - (GLOBAL.sessionStamp.getTimezoneOffset() * 60000)).toISOString().slice(11, -5);
121
156
  GLOBAL.exportCount = 0;
122
-
157
+
123
158
  if(GLOBAL.logStack == null) {
124
159
  GLOBAL.logStack = [];
125
160
  GLOBAL.getLogs = () => GLOBAL.logStack.sort((a, b) => a.stamp - b.stamp);
126
161
  GLOBAL.clearLogs = () => {GLOBAL.logStack = []; };
127
162
  GLOBAL.showLogs = () => {
128
163
  if(!GLOBAL.logStack || GLOBAL.logStack.length === 0) return console.info('There are no logs currently in the stack!');
129
-
164
+
130
165
  const getTime = (ISOStamp) => new Date(ISOStamp).toISOString().split('T')[1].slice(0,-1);
131
-
166
+
132
167
  (function parseLog(stack=[...GLOBAL.logStack], parentEndTime = Infinity) {
133
168
  while (stack.length > 0 && stack[0].startTime < parentEndTime) {
134
169
  let markS, markE, spanName, indE, duration, stamps;
@@ -138,14 +173,14 @@ function amendPerformanceLoggingToClass(cls) {
138
173
  if (indE !== -1) markE = stack.splice(indE, 1)[0];
139
174
  duration = (markE != null) ? (markE.startTime - markS.startTime).toFixed(4) + 'ms' : 'ONGOING';
140
175
  stamps = (markE != null) ? getTime(markS.stamp) + " - " + getTime(markE.stamp) : getTime(markS.stamp) + " - ...";
141
-
176
+
142
177
  console.groupCollapsed(spanName.replace(/-/g, ' :: ') + ' :: ' + duration + " (" + stamps + ")");
143
178
  parseLog(stack, markE.startTime);
144
179
  console.groupEnd();
145
180
  }
146
181
  })()
147
182
  }
148
-
183
+
149
184
  GLOBAL.addPerfMarker = (tag) => {
150
185
  const {name, startTime} = performance.mark(tag).toJSON();
151
186
  GLOBAL.logStack.push({
@@ -157,14 +192,14 @@ function amendPerformanceLoggingToClass(cls) {
157
192
  });
158
193
  }
159
194
  }
160
-
195
+
161
196
  return class extends cls {
162
197
  constructor(...args) {
163
198
  super(...args);
164
-
199
+
165
200
  Object.getOwnPropertyNames(this).forEach(propName => {
166
201
  if (propName === "constructor" || typeof this[propName] !== "function") return;
167
-
202
+
168
203
  const originalMethod = this[propName];
169
204
  let markerTagPrefix = `[MAINTHREAD]-${cls.name}-${propName}`;
170
205
  this[propName] = function(...args) {
@@ -190,12 +225,11 @@ if(typeof FIDO2Client !== "undefined" && FIDO2Client instanceof Function) FIDO2C
190
225
  let masterLogs = [];
191
226
  if(window){
192
227
  let tzO = new Date().getTimezoneOffset() * 60000;
193
-
228
+
194
229
  GLOBAL.getAllLogs = async (showCSV) => {
195
- let res = await zsmClient.callWorkerFunction("getLogs");
230
+ let res = await zsmClient.callWorkerFunction("getLogs");
196
231
  let {logs, timeOrigin} = JSON.parse(res);
197
232
  let timeDifferential = performance.timeOrigin - timeOrigin;
198
- console.log('timeDifferential :', timeDifferential);
199
233
  if(!showCSV) {
200
234
  console.groupCollapsed('RAW logs');
201
235
  GLOBAL.logStack.forEach(mainRecord => console.info('mainRecord :', mainRecord));
@@ -214,20 +248,32 @@ if(window){
214
248
  allLogs.forEach(log => console.info('log :', log));
215
249
  console.groupEnd();
216
250
  }
251
+ console.groupCollapsed('ALL logs (Combined and Sorted)');
252
+ allLogs = allLogs.map(log => {
253
+ if(!localStorage) return log;
254
+ let activeEnv = localStorage.getItem('activeEnv');
255
+ if(!activeEnv) return log;
256
+ if(! /RELPARTYSERVER|CRYPTOSERVER/.test(log.type)) return log;
257
+ // log.type += '-' + activeEnv.toUpperCase();
258
+ return log;
259
+ });
260
+ allLogs.forEach(log => console.info('log :', log));
261
+ console.groupEnd();
262
+
217
263
  return allLogs;
218
264
  };
219
-
265
+
220
266
  GLOBAL.showLogs = async (showCSV=false) => {
221
- const getTime = (ISOStamp) => {
267
+ const getTime = (ISOStamp, timeOnly=true) => {
222
268
  try{
223
- return new Date((+ISOStamp - tzO)).toISOString().slice(11,-1);
269
+ return timeOnly ? new Date((+ISOStamp - tzO)).toISOString().slice(11,-1) : new Date(+ISOStamp - tzO).toISOString();
224
270
  }catch(e){
225
271
  console.error('Error parsing ISOStamp:', ISOStamp, e);
226
272
  return ISOStamp;
227
273
  }
228
274
  }
229
275
  const stripStartEnd = (name) => name.replace(/-(START|END)$/gi, '');
230
-
276
+
231
277
  function recursivelyMoveChildLogsIntoStartNode(stack) {
232
278
  let processedLogs = [];
233
279
  while (stack.length > 0) {
@@ -237,42 +283,46 @@ if(window){
237
283
  }
238
284
  let endingMarker = stack.findIndex(log => log.name === stripStartEnd(startingMarker.name) + '-END'),
239
285
  dateTimeStamps = [],
286
+ fullDateStamps = [],
240
287
  stackSubset = [],
241
288
  totalDuration = 0,
242
289
  networkLatency = 0,
243
290
  computeDuration = 0;
244
-
291
+
245
292
  if(!~endingMarker) {
246
293
  stackSubset = stack;
247
294
  endingMarker = stack.length;
248
295
  totalDuration = 'ONGOING';
249
296
  dateTimeStamps.push(getTime(startingMarker.stamp), '...?');
250
-
297
+ fullDateStamps.push(getTime(startingMarker.stamp, false), '...?');
298
+
251
299
  }else{
252
300
  stackSubset = stack.splice(0, endingMarker+1);
253
301
  endingMarker = stackSubset[stackSubset.length-1];
254
302
  totalDuration = endingMarker.absTime - startingMarker.absTime;
255
303
  dateTimeStamps.push(getTime(startingMarker.stamp), getTime(endingMarker.stamp));
256
-
304
+ fullDateStamps.push(getTime(startingMarker.stamp, false), getTime(endingMarker.stamp, false));
305
+
257
306
  }
258
-
307
+
259
308
  processedLogs.push(Object.assign(startingMarker, {
260
309
  name: stripStartEnd(startingMarker.name),
261
310
  children: recursivelyMoveChildLogsIntoStartNode(stackSubset),
262
- totalDuration,
311
+ totalDuration,
263
312
  dateTimeStamps,
313
+ fullDateStamps,
264
314
  networkLatency,
265
315
  computeDuration
266
316
  }));
267
317
  }
268
318
  return processedLogs;
269
319
  }
270
-
320
+
271
321
  function recursivelyComputeCumulativeNetworkLatency(node) {
272
322
  let nodeLatency = 0;
273
323
  if(node.clientToServerLatency) nodeLatency += node.clientToServerLatency;
274
324
  if(node.serverToClientLatency) nodeLatency += node.serverToClientLatency;
275
-
325
+
276
326
  if (Array.isArray(node.children) && node.children.length > 0) {
277
327
  node.children.forEach(child => {
278
328
  nodeLatency += recursivelyComputeCumulativeNetworkLatency(child);
@@ -281,7 +331,7 @@ if(window){
281
331
  node.networkLatency = nodeLatency;
282
332
  return nodeLatency;
283
333
  }
284
-
334
+
285
335
  function calculateComputeDurationForNode(node) {
286
336
  if(!node.children) return node.computeDuration = totalDuration;
287
337
  node.computeDuration = (node.children.reduce((acc,child)=>{
@@ -289,9 +339,9 @@ if(window){
289
339
  }, node.totalDuration))
290
340
  node.children.forEach(calculateComputeDurationForNode)
291
341
  }
292
-
342
+
293
343
  let exportableStack = [];
294
-
344
+
295
345
  function recursivelyOutputLogsToConsole(stack, depth = 0) {
296
346
  let outputColors={
297
347
  'MAINTHREAD': '#509',
@@ -301,20 +351,20 @@ if(window){
301
351
  'RELPARTY': '#076',
302
352
  'CRYPTO': '#700',
303
353
  };
304
-
354
+
305
355
  function formatNumberColumn(num, NaNValue='0.000') {
306
356
  let opValue = isNaN(num) ? NaNValue : Math.abs(num).toFixed(3);
307
357
  let lPadStr = ' '.repeat(12 - opValue.length);
308
358
  return lPadStr + opValue;
309
359
  }
310
-
360
+
311
361
  stack.forEach(item => {
312
362
  let spacePadding = ' '.repeat(100);
313
363
  let exportableRecord = {}
314
364
  let sequence = item.order;
315
- let nameElements = item.name.replace(/[\[\]]/g, '').split(/(SERVER)?-/);
316
- let threadName = (nameElements[0] + spacePadding).slice(0, 14);
317
- let processName = (nameElements[1] ? nameElements[1] : item.name + spacePadding).slice(0, 70-(depth*2));
365
+ let nameElements = (item.name.match(/\[(.*?)\]-(.*)$/) || new Array(3).fill('')).slice(1,3);
366
+ let threadName = (nameElements[0] + spacePadding).replace('SERVER', '').slice(0, 14);
367
+ let processName = (nameElements[1] + spacePadding).slice(0, 70-(depth*2));
318
368
  let totalDuration = formatNumberColumn(item?.totalDuration, 'ONGOING');
319
369
  let computeDuration = (/ONGOING/.test(totalDuration)) ? formatNumberColumn('ONGOING', 'ONGOING') : formatNumberColumn(item?.computeDuration);
320
370
  let networkLatency = (/ONGOING/.test(totalDuration)) ? formatNumberColumn('ONGOING', 'ONGOING') : formatNumberColumn(item?.networkLatency);
@@ -324,11 +374,17 @@ if(window){
324
374
  if(totalDuration < (Math.abs(networkLatency) + Math.abs(computeDuration))) totalDuration = formatNumberColumn(Math.abs(networkLatency) + Math.abs(computeDuration));
325
375
  cumulativeCompute = formatNumberColumn((totalDuration - networkLatency - computeDuration), 'ONGOING');
326
376
  }
327
- let indentionPads = depth > 0 ? ' '.repeat(depth-1) + ' ' : ' ';
328
- let detailPads = depth > 0 ? ' '.repeat(depth-1) + ' '.repeat(threadName.length) + ' ' : ' ' + ' '.repeat(threadName.length);
377
+ let indentionDepth = depth;
378
+ if (/^CRYPTO/.test(threadName)) {
379
+ indentionDepth = depth + 2;
380
+ processName = processName.slice(0, -4);
381
+ }
382
+
383
+ let indentionPads = depth > 0 ? ' '.repeat(indentionDepth-1) + ' ' : ' ';
384
+ let detailPads = depth > 0 ? ' '.repeat(indentionDepth-1) + ' '.repeat(threadName.length) + ' ' : ' ' + ' '.repeat(threadName.length);
329
385
  let timeSpan = item?.dateTimeStamps ? (' ' + item.dateTimeStamps[0]) : 'N/A';
330
-
331
- if(!showCSV) {
386
+
387
+ // if(!showCSV) {
332
388
  let outputLine = ` ${threadName}${indentionPads} ${processName} ${totalDuration} ${networkLatency} ${cumulativeCompute} ${computeDuration} ${timeSpan}`;
333
389
  console.groupCollapsed(`%c${outputLine}`, `color: ${outputColors[item.type]}; font-weight: 500;`);
334
390
  console.info(`%c${detailPads} Sequence: `, `color: ${outputColors[item.type]}; font-weight: 700;`, sequence);
@@ -343,7 +399,13 @@ if(window){
343
399
  console.info(`%c${detailPads} Process Compute Duration: `, `color: ${outputColors[item.type]}; font-weight: 700;`, (/ONGOING/.test(computeDuration) ? 'ONGOING' : computeDuration.trim() + 'ms'));
344
400
  console.info(`%c${detailPads} ================================================================`, `color: ${outputColors[item.type]}; font-weight: 700;`, "\n\n");
345
401
  console.groupEnd();
346
- } else {
402
+ // } else if(!showCSV || showCSV) {
403
+ let activeEnv = '';
404
+ let pageHost = window.location.host;
405
+
406
+ if(localStorage && localStorage.getItem('activeEnv')) {
407
+ activeEnv = (/RELPARTY|CRYPTO/.test(threadName)) ? localStorage.getItem('activeEnv') : 'N/A';
408
+ }
347
409
  exportableRecord = {
348
410
  sequence : sequence,
349
411
  threadName : threadName.trim(),
@@ -353,24 +415,27 @@ if(window){
353
415
  networkLatency : (/ONGOING/.test(networkLatency) ? 'ONGOING' : +(networkLatency.trim())),
354
416
  cumulativeCompute : (/ONGOING/.test(cumulativeCompute) ? 'ONGOING' : +(cumulativeCompute.trim())),
355
417
  computeDuration : (/ONGOING/.test(computeDuration) ? 'ONGOING' : +(computeDuration.trim())),
356
- startTime : (item?.dateTimeStamps[0]??''),
357
- endTime : (item?.dateTimeStamps[1]??''),
358
- spawnsChildren : item?.children ? item.children.length : 0
418
+ startTime : (item?.fullDateStamps[0]?.replace(/[TZ]/g, ' ').trim() ?? ''),
419
+ endTime : (item?.fullDateStamps[1]?.replace(/[TZ]/g, ' ').trim() ?? ''),
420
+ spawnsChildren : item?.children ? item.children.length : 0,
421
+ host : pageHost,
422
+ remoteAPIEnv : activeEnv,
359
423
  };
360
-
424
+
361
425
  exportableStack.push(exportableRecord);
362
- }
426
+ // }
363
427
  if(item.children.length > 0) recursivelyOutputLogsToConsole(item.children, depth + 1);
364
428
  });
365
429
  return exportableStack.length ? exportableStack : undefined;
366
430
  }
367
-
431
+
368
432
  let allLogs = await GLOBAL.getAllLogs(showCSV);
369
433
  let processedStack = recursivelyMoveChildLogsIntoStartNode(allLogs);
370
434
  processedStack.forEach(root => recursivelyComputeCumulativeNetworkLatency(root));
371
435
  processedStack.forEach(root => calculateComputeDurationForNode(root));
372
436
  if(!showCSV) console.info(`%c${' THREAD PROCESS NAME DURATION NETWORK TOT.COMP. COMPUTE START TIME'}`, 'color: #000; font-weight: 700;');
373
437
  let exportableData = recursivelyOutputLogsToConsole(processedStack);
438
+ console.log('exportableData :', exportableData);
374
439
  if(!showCSV || !exportableData) return;
375
440
  let csvData = exportableData.flatMap((log, i) => {
376
441
  let retVal = [Object.values(log)];
@@ -379,7 +444,7 @@ if(window){
379
444
  if(i === 0) retVal.unshift('"' + Object.keys(log).join('","') + '"');
380
445
  return retVal;
381
446
  });
382
-
447
+
383
448
  try {
384
449
  GLOBAL.exportCount++;
385
450
  console.groupCollapsed(`CSV Data Exported as %c${GLOBAL.sessionStamp}-${(GLOBAL.exportCount)}.csv`, 'color: #F60; font-weight: 700;');
@@ -394,15 +459,15 @@ if(window){
394
459
  URL.revokeObjectURL(url);
395
460
  GLOBAL.logStack = [];
396
461
  zsmClient.callWorkerFunction("clearLogs", true);
397
- console.groupEnd();
462
+ console.groupEnd();
398
463
  } catch (error) {
399
464
  console.error('Error during download:', error);
400
465
  console.groupEnd();
401
466
  }
402
-
467
+
403
468
  return exportableData;
404
469
  }
405
-
470
+
406
471
  }
407
472
 
408
473
  export { UMFAClient, FIDO2Client };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ideem/zsm-client-sdk",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no reliance on specific hardware. Our cryptographic module has completed FIPS 140-3 NIST approved lab testing, is FIDO2-ready, and is on the Modules In Progress (MIP) list. Superior Usability. no opt-in, no codes, no downloads, no authenticator apps. Better Security.",
5
5
  "author": "@JJ (Jason Jurusz) <jj@useideem.com>/<nerdy.deeds@done.cheap>",
6
6
  "keywords": [