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