@oneteme/inspect-ng-collector 1.3.1 → 1.3.2-beta.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.
@@ -1,97 +1,74 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core';
3
- import { HttpErrorResponse, HttpResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
2
+ import { InjectionToken, Inject, Injectable, APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core';
3
+ import { HttpResponse, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
4
4
  import { finalize, interval, startWith, tap as tap$1 } from 'rxjs';
5
5
  import { tap } from 'rxjs/operators';
6
6
  import { NavigationStart, NavigationEnd, NavigationCancel, Router } from '@angular/router';
7
7
 
8
- const WIN = window;
9
- function dateNow() {
10
- return Date.now() / 1_000;
11
- }
12
8
  var RequestMask;
13
9
  (function (RequestMask) {
14
10
  RequestMask[RequestMask["LOCAL"] = 1] = "LOCAL";
15
11
  RequestMask[RequestMask["REST"] = 4] = "REST";
16
12
  })(RequestMask || (RequestMask = {}));
17
- const DISPATCH = 'dispatch';
18
- const PRE_DISPATCH = 'pre-dispatch';
19
- const BEFOREUNLOAD = 'beforeunload';
20
- function createLogEntry(level, message, sessionId) {
21
- return {
22
- "@type": "00",
13
+ const TRACE_TYPE_LOG = '00';
14
+ const TRACE_TYPE_RESOURCE_USAGE = '01';
15
+ const TRACE_TYPE_COLLECTOR_CONFIGURATION = '02';
16
+ const TRACE_TYPE_SESSION_MASK_UPDATE = '03';
17
+ const TRACE_TYPE_MAIN_SESSION = '10';
18
+ const TRACE_TYPE_MAIN_SESSION_CALLBACK = '11';
19
+ const TRACE_TYPE_LOCAL_REQUEST = '110';
20
+ const TRACE_TYPE_LOCAL_REQUEST_CALLBACK = '111';
21
+ const TRACE_TYPE_REST_REQUEST = '120';
22
+ const TRACE_TYPE_REST_REQUEST_CALLBACK = '121';
23
+ const TRACE_TYPE_HTTP_REQUEST_STAGE = '220';
24
+ const TRACE_TYPE_USER_ACTION = '300';
25
+ function dateNow() {
26
+ return Date.now() / 1_000;
27
+ }
28
+
29
+ const eventTarget = new EventTarget();
30
+ const TRACE = 'trace', EXPORT = 'export', SHUTDOWN = 'shutdown', RELOAD = 'reload';
31
+ const WIN = window;
32
+ function dispatchReport(message, error) {
33
+ dispatchLog("REPORT", `${message} ${error && JSON.stringify(error)}`);
34
+ }
35
+ function dispatchLog(level, message, sessionId) {
36
+ dispatchTraces({
37
+ "@type": TRACE_TYPE_LOG,
23
38
  level: level,
24
39
  message: message,
25
40
  instant: dateNow(),
26
41
  sessionId: sessionId
27
- };
42
+ });
28
43
  }
29
- function createReport(message) {
30
- return {
31
- "@type": "00",
32
- level: "ERROR",
33
- message: message,
34
- instant: dateNow(),
35
- };
44
+ function dispatchTraces(...traces) {
45
+ eventTarget.dispatchEvent(new CustomEvent(TRACE, { detail: { traces } }));
36
46
  }
37
-
38
- const SLASH$1 = '/';
39
- function getNumberOrCall(o) {
40
- return typeof o === "function" ? o() : o;
47
+ function dispatchExport() {
48
+ eventTarget.dispatchEvent(new CustomEvent(EXPORT));
41
49
  }
42
- function getStringOrCall(o) {
43
- return typeof o === "function" ? o() : o;
50
+ function dispatchReload() {
51
+ eventTarget.dispatchEvent(new CustomEvent(RELOAD));
44
52
  }
45
- function getRegArrOrCall(o) {
46
- return typeof o === "function" ? o() : o;
53
+ function dispatchShutown() {
54
+ eventTarget.dispatchEvent(new CustomEvent(SHUTDOWN));
47
55
  }
48
- function detectBrowser() {
49
- try {
50
- const agent = window.navigator.userAgent.toLowerCase();
51
- switch (true) {
52
- case agent.indexOf('edg') > -1:
53
- return 'Edge';
54
- case agent.indexOf('opr') > -1:
55
- return 'Opera';
56
- case agent.indexOf('chrome') > -1:
57
- return 'Chrome';
58
- case agent.indexOf('firefox') > -1:
59
- return 'Firefox';
60
- case agent.indexOf('safari') > -1:
61
- return 'Safari';
62
- case agent.indexOf('msie') > -1:
63
- return 'Microsoft Internet Explorer';
64
- }
65
- }
66
- catch (e) {
67
- console.error(e);
68
- }
69
- return undefined;
56
+ function addTraceListener(fn) {
57
+ eventTarget.addEventListener(TRACE, fn);
70
58
  }
71
- function detectOs() {
72
- try {
73
- let versionMatch, version;
74
- const agent = window.navigator.userAgent.toLowerCase();
75
- switch (true) {
76
- case (/windows/.test(agent)):
77
- versionMatch = /windows nt (\d+\.\d+)/.exec(agent);
78
- version = versionMatch ? versionMatch[1] : 'Unknown';
79
- return `Windows ${version}`;
80
- case (/linux/.test(agent)):
81
- return 'Linux';
82
- case (/macintosh/.test(agent)):
83
- versionMatch = /mac os x (\d+[._]\d+[._]\d+)/.exec(agent);
84
- version = versionMatch ? versionMatch[1] : 'Unknown';
85
- return `MacOs ${version}`;
86
- }
87
- }
88
- catch (e) {
89
- console.error(e);
90
- }
91
- return undefined;
59
+ function addExportListener(fn) {
60
+ eventTarget.addEventListener(EXPORT, fn);
92
61
  }
93
- function toURL$1(host, path) {
94
- return host.endsWith(SLASH$1) || path.startsWith(SLASH$1) ? host + path : [host, path].join(SLASH$1);
62
+ function addReloadListener(fn) {
63
+ eventTarget.addEventListener(RELOAD, fn);
64
+ }
65
+ function addShutdownListener(fn) {
66
+ eventTarget.addEventListener(SHUTDOWN, fn);
67
+ }
68
+
69
+ const HOST_PATERN = /https?:\/\/[\w\-.]+(:\d{2,5})?\/?/;
70
+ function getOrCall(o) {
71
+ return typeof o === "function" ? o() : o;
95
72
  }
96
73
  function matchRegex(v, name, pattern, defaultValue) {
97
74
  if (v && pattern.exec(v)) {
@@ -116,7 +93,7 @@ function requirePostitiveValue(v, name, defaultValue) {
116
93
  }
117
94
  throw new Error(`bad value ${name}=${v} <= 0`);
118
95
  }
119
- function require(v, name) {
96
+ function require$1(v, name) {
120
97
  if (v != undefined) {
121
98
  return v;
122
99
  }
@@ -131,11 +108,11 @@ function adaptedConfig(conf) {
131
108
  },
132
109
  monitoring: {
133
110
  ...conf.monitoring,
134
- additionalProperties: String(conf.monitoring?.additionalProperties),
135
- name: String(conf.monitoring?.name),
136
- version: String(conf.monitoring?.version),
137
- env: String(conf.monitoring?.env),
138
- user: String(conf.monitoring?.user),
111
+ additionalProperties: String(conf.additionalProperties),
112
+ name: String(conf.name),
113
+ version: String(conf.version),
114
+ env: String(conf.env),
115
+ user: String(conf.user),
139
116
  httpRoute: {
140
117
  ...conf.monitoring?.httpRoute,
141
118
  excludes: {
@@ -147,283 +124,305 @@ function adaptedConfig(conf) {
147
124
  ...conf.tracing,
148
125
  remote: {
149
126
  ...conf.tracing?.remote,
150
- '@type': "02",
127
+ '@type': TRACE_TYPE_COLLECTOR_CONFIGURATION,
151
128
  retentionMaxAge: (conf.tracing?.remote?.retentionMaxAge ?? 10) * 60 * 60 * 24
152
129
  }
153
130
  }
154
131
  };
155
132
  }
156
-
157
- const SLASH = '/';
158
- const HOST_PATERN = /https?:\/\/[\w\-.]+(:\d{2,5})?\/?/;
159
- class ContextManager {
160
- constructor(_instanceEnv, _techConfig) {
161
- this._instanceEnv = _instanceEnv;
162
- this._techConfig = _techConfig;
163
- }
164
- get techConfig() {
165
- return this._techConfig;
166
- }
167
- get instanceEnv() {
168
- return this._instanceEnv;
133
+ function validateAndGetConfig(conf, instanceId) {
134
+ const host = matchRegex(getOrCall(conf?.tracing?.remote?.host), "host", HOST_PATERN);
135
+ return {
136
+ user: conf?.user,
137
+ queueCapacity: requirePostitiveValue(getOrCall(conf?.tracing?.queueCapacity), "queueCapacity", 1000),
138
+ interval: requirePostitiveValue(getOrCall(conf?.scheduling?.interval), "interval", 60000),
139
+ instanceApi: new URL('v4/trace/instance', host).href,
140
+ sessionApi: new URL(`v4/trace/instance/${instanceId}/session`, host).href,
141
+ exclude: getOrCall(conf?.monitoring?.httpRoute?.excludes?.path) || [],
142
+ hostExcludes: conf?.monitoring?.httpRequest?.excludes?.host || [],
143
+ debugMode: !!conf.debugMode,
144
+ analytics: !!conf?.monitoring?.analytics?.enabled,
145
+ resources: !!conf?.monitoring?.resources?.enabled,
146
+ storage: !!conf?.monitoring?.storage?.enabled,
147
+ enabled: !!conf.enabled
148
+ };
149
+ }
150
+ function createInstance(conf, instanceId) {
151
+ return {
152
+ id: instanceId,
153
+ instant: dateNow(),
154
+ name: require$1(getOrCall(conf?.name), 'configuration.name'),
155
+ version: getOrCall(conf?.version),
156
+ address: getClientID(), //server side
157
+ env: require$1(getOrCall(conf?.env), 'env'),
158
+ os: detectOs(),
159
+ re: detectBrowser(),
160
+ user: undefined, // cannot get user
161
+ type: "CLIENT",
162
+ collector: "inspect-ng-collector-1.3.2-beta.1",
163
+ resource: { maxHeap: (('memory' in performance) && performance.memory.jsHeapSizeLimit / (1024 * 1024)) || undefined },
164
+ additionalProperties: conf?.additionalProperties?.(),
165
+ configuration: adaptedConfig(conf)
166
+ };
167
+ }
168
+ function getClientID() {
169
+ let cid = localStorage.getItem("jarvis.inspect.cid");
170
+ if (!cid) {
171
+ localStorage.setItem("jarvis.inspect.cid", cid = crypto.randomUUID());
169
172
  }
170
- static get instance() {
171
- if (!ContextManager._instance) {
172
- console.warn("[Inspect-ng-collector] Error while initializing ContextManager");
173
+ return cid;
174
+ }
175
+ function detectOs() {
176
+ try {
177
+ let versionMatch, version;
178
+ const agent = globalThis.navigator.userAgent.toLowerCase(); //TODO see also https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
179
+ switch (true) {
180
+ case (/windows/.test(agent)):
181
+ versionMatch = /windows nt (\d+\.\d+)/.exec(agent);
182
+ version = versionMatch ? versionMatch[1] : '?';
183
+ return `Windows ${version}`;
184
+ case (/linux/.test(agent)):
185
+ return 'Linux';
186
+ case (/macintosh/.test(agent)):
187
+ versionMatch = /mac os x (\d+[._]\d+[._]\d+)/.exec(agent);
188
+ version = versionMatch ? versionMatch[1] : '?';
189
+ return `MacOs ${version}`;
173
190
  }
174
- return ContextManager._instance;
175
- }
176
- static init(conf) {
177
- let id = crypto.randomUUID();
178
- return ContextManager._instance = new ContextManager(ContextManager.createInstance(conf, id), ContextManager.validateAndGetConfig(conf, id));
179
- }
180
- static validateAndGetConfig(conf, instanceId) {
181
- let host = matchRegex(getStringOrCall(conf?.tracing?.remote?.host), "host", HOST_PATERN);
182
- let sessionApi = "v4/trace/instance/:id/session";
183
- let instanceApi = "v4/trace/instance";
184
- return {
185
- user: conf?.monitoring?.user,
186
- queueCapacity: requirePostitiveValue(getNumberOrCall(conf?.tracing?.queueCapacity), "queueCapacity", 1000),
187
- interval: requirePostitiveValue(getNumberOrCall(conf?.scheduling?.interval), "interval", 60000),
188
- delayIfPending: requirePostitiveValue(getNumberOrCall(conf?.tracing?.delayIfPending), "delayIfPending", 30),
189
- instanceApi: sessionApiURL(host, instanceApi),
190
- sessionApi: instanceApiURL(host, sessionApi).replace(':id', instanceId),
191
- exclude: getRegArrOrCall(conf?.monitoring?.httpRoute?.excludes?.path) || [],
192
- hostExcludes: conf?.monitoring?.httpRequest?.excludes?.host || [],
193
- debugMode: conf.debugMode ?? false,
194
- analytics: conf?.monitoring?.analytics?.enabled ?? false,
195
- resources: conf?.monitoring?.resources?.enabled ?? false,
196
- storage: conf?.monitoring?.storage?.enabled ?? false,
197
- enabled: conf.enabled ?? false
198
- };
199
191
  }
200
- static createInstance(conf, instanceId) {
201
- return {
202
- id: instanceId,
203
- name: require(getStringOrCall(conf?.monitoring?.name), 'name'),
204
- version: getStringOrCall(conf?.monitoring?.version),
205
- address: getSessionAddress(), //server side
206
- env: require(getStringOrCall(conf?.monitoring?.env), 'env'),
207
- os: detectOs(),
208
- re: detectBrowser(),
209
- user: undefined, // cannot get user
210
- type: "CLIENT",
211
- instant: dateNow(),
212
- collector: "inspect-ng-collector-0.0.1",
213
- resource: { maxHeap: (('memory' in performance) && performance.memory.jsHeapSizeLimit / (1024 * 1024)) || undefined },
214
- additionalProperties: conf?.monitoring?.additionalProperties(),
215
- configuration: adaptedConfig(conf)
216
- };
192
+ catch (e) {
193
+ dispatchReport("detectOs", e);
217
194
  }
195
+ return undefined;
218
196
  }
219
- function getSessionAddress() {
220
- let inspectId = localStorage.getItem("inspect-id");
221
- if (!inspectId) {
222
- inspectId = crypto.randomUUID();
223
- localStorage.setItem("inspect-id", inspectId);
197
+ function detectBrowser() {
198
+ try {
199
+ const agent = globalThis.navigator.userAgent.toLowerCase();
200
+ switch (true) {
201
+ case agent.includes('edg'):
202
+ return 'Edge';
203
+ case agent.includes('opr'):
204
+ return 'Opera';
205
+ case agent.includes('chrome'):
206
+ return 'Chrome';
207
+ case agent.includes('firefox'):
208
+ return 'Firefox';
209
+ case agent.includes('safari'):
210
+ return 'Safari';
211
+ case agent.includes('msie'):
212
+ return 'IE';
213
+ }
224
214
  }
225
- return inspectId;
226
- }
227
- function instanceApiURL(host, path) {
228
- return toURL(host, path);
229
- }
230
- function sessionApiURL(host, path) {
231
- return toURL(host, path);
215
+ catch (e) {
216
+ dispatchReport("detectBrowser", e);
217
+ }
218
+ return undefined;
232
219
  }
233
- function toURL(host, path) {
234
- return host.endsWith(SLASH) || path.startsWith(SLASH) ? host + path : [host, path].join(SLASH);
220
+ function refreshConfig(id, tech) {
221
+ const uuidRegex = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
222
+ tech.sessionApi = tech.sessionApi.replace(uuidRegex, id);
223
+ return id;
235
224
  }
236
225
 
237
226
  class SessionManager {
238
- constructor() {
239
- this.initialized = false;
227
+ constructor(_techConfig) {
228
+ this._techConfig = _techConfig;
240
229
  }
241
230
  static get instance() {
242
- if (!SessionManager._instance) {
243
- SessionManager._instance = new SessionManager();
244
- WIN["inspect-session-manager"] = SessionManager._instance;
245
- }
246
231
  return SessionManager._instance;
247
232
  }
248
233
  navigate(url) {
249
- this.getCurrentSession(s => {
250
- if (s) {
251
- this.getCurrentSessionCallBack(cb => cb.end = dateNow());
252
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { force: !url, traces: this.currentSessionCallBack } }));
253
- }
254
- this.currentSession = null;
255
- });
234
+ const now = dateNow();
235
+ this.endSession(now);
256
236
  if (url) {
257
- let id = crypto.randomUUID();
237
+ const id = crypto.randomUUID();
258
238
  this.currentSession = {
259
- '@type': "10",
239
+ '@type': TRACE_TYPE_MAIN_SESSION,
260
240
  id: id,
261
- user: getStringOrCall(ContextManager.instance.techConfig.user),
262
- start: dateNow(),
263
241
  type: "VIEW",
242
+ user: getOrCall(this._techConfig.user),
243
+ start: now,
264
244
  location: url,
265
- loading: true,
266
245
  requestMask: 0,
267
246
  };
268
- this.initialized = true;
269
247
  this.currentSessionCallBack = {
270
- '@type': "11",
248
+ '@type': TRACE_TYPE_MAIN_SESSION_CALLBACK,
271
249
  id: id,
272
250
  requestMask: 0,
273
251
  };
274
252
  }
275
253
  }
276
- updateSession() {
277
- this.getCurrentSession(s => {
278
- s.name = document.title;
279
- s.location = document.URL;
280
- if (!ContextManager.instance.techConfig.exclude?.some((e) => e.test(s.location))) {
281
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: s } }));
282
- }
254
+ endSession(end) {
255
+ this.getCurrentSessionCallBack(call => {
256
+ call.end = end;
257
+ dispatchTraces(call);
283
258
  });
259
+ this.currentSessionCallBack = undefined;
284
260
  }
285
- getCurrentSession(fn) {
261
+ updateSession() {
286
262
  if (this.currentSession) {
287
- return fn(this.currentSession);
263
+ this.currentSession.name = document.title; // add settimeout
264
+ this.currentSession.location = document.URL;
265
+ if (!this._techConfig.exclude?.some((e) => e.test(this.currentSession?.location))) {
266
+ dispatchTraces(this.currentSession);
267
+ }
268
+ this.currentSession = undefined;
269
+ }
270
+ else {
271
+ dispatchReport('updateSession', 'no active session');
288
272
  }
289
- this.initialized && window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("no active session found ") } }));
290
- return undefined;
291
273
  }
292
274
  getCurrentSessionCallBack(fn) {
293
275
  if (this.currentSessionCallBack) {
294
276
  return fn(this.currentSessionCallBack);
295
277
  }
296
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("no active session found ") } }));
278
+ dispatchReport('getCurrentSessionCallBack', 'no active session');
297
279
  return undefined;
298
280
  }
299
281
  currentSessionID() {
300
- return this.getCurrentSession(s => s.id);
301
- }
302
- updateMask(requestMask) {
303
- return this.getCurrentSessionCallBack(s => {
304
- let before = s.requestMask;
305
- s.requestMask |= requestMask;
306
- return s.requestMask !== before;
282
+ return this.getCurrentSessionCallBack(s => s.id);
283
+ }
284
+ initRestRequest(mask) {
285
+ const req = this.getCurrentSessionCallBack(s => {
286
+ if ((s.requestMask & mask) !== mask) {
287
+ s.requestMask |= mask;
288
+ dispatchTraces({
289
+ "@type": TRACE_TYPE_SESSION_MASK_UPDATE,
290
+ id: s.id,
291
+ main: true,
292
+ mask: s.requestMask
293
+ });
294
+ }
295
+ return { sessionId: s.id };
307
296
  });
297
+ return req || {};
298
+ }
299
+ initUserAction() {
300
+ const req = this.getCurrentSessionCallBack(s => ({ sessionId: s.id }));
301
+ return req || {};
308
302
  }
309
303
  addException(exception) {
310
304
  this.getCurrentSessionCallBack(s => s.exception = exception);
311
305
  }
306
+ info(message) {
307
+ this.log("INFO", message);
308
+ }
309
+ warn(message) {
310
+ this.log("WARN", message);
311
+ }
312
+ error(message) {
313
+ this.log("ERROR", message);
314
+ }
315
+ log(level, message) {
316
+ if (message) {
317
+ this.getCurrentSessionCallBack(s => dispatchLog(level, message, s.id));
318
+ }
319
+ }
320
+ }
321
+ function sessionlogger() {
322
+ return SessionManager.instance;
323
+ }
324
+ function sessionManager(tech) {
325
+ SessionManager._instance = new SessionManager(tech);
326
+ WIN["inspect-session-manager"] = SessionManager._instance;
312
327
  }
313
328
 
329
+ const TRACE_HEADER = 'x-tracert';
314
330
  class RestRequestMonitor {
315
- constructor(restRequest) {
316
- const start = dateNow();
317
- const url = toHref(restRequest.urlWithParams);
331
+ constructor() {
332
+ this.start = dateNow();
333
+ this.id = crypto.randomUUID();
334
+ }
335
+ preProcess(restRequest) {
336
+ const url = new URL(restRequest.urlWithParams, window.location.origin); //TDO check & remove toHref(restRequest.urlWithParams)
318
337
  const auth_user = extractAuthSchemeAnduser(restRequest.headers);
319
- this.restRequest = {
320
- "@type": "120",
321
- id: crypto.randomUUID(),
338
+ dispatchTraces({ ...SessionManager.instance.initRestRequest(RequestMask.REST),
339
+ "@type": TRACE_TYPE_REST_REQUEST,
340
+ id: this.id,
322
341
  method: restRequest.method,
323
342
  protocol: url.protocol.slice(0, -1),
324
- host: exctractHost(url.host),
325
- port: +url.port || -1,
343
+ host: url.hostname,
344
+ port: url.port ? Number(url.port) : 0,
326
345
  path: url.pathname,
327
346
  query: url.search.slice(1, url.search.length),
328
347
  contentType: restRequest.responseType,
329
348
  authScheme: auth_user.authScheme,
330
349
  user: auth_user.user,
331
350
  dataSize: sizeOf(restRequest.body),
332
- start: start,
333
- sessionId: SessionManager.instance.currentSessionID()
334
- };
335
- this.restRequestCallBack = {
336
- "@type": "121",
337
- id: this.restRequest.id,
351
+ start: this.start,
352
+ });
353
+ }
354
+ postProcess(response, error) {
355
+ const end = dateNow();
356
+ const callback = {
357
+ "@type": TRACE_TYPE_REST_REQUEST_CALLBACK,
358
+ id: this.id,
338
359
  dataSize: -1,
339
- linked: false
360
+ linked: false,
361
+ end: end
340
362
  };
341
- if (SessionManager.instance.currentSessionID() != null) {
342
- let doUpdateMask = SessionManager.instance.updateMask(RequestMask.REST);
343
- doUpdateMask && window.dispatchEvent(new CustomEvent(DISPATCH, { detail: {
344
- traces: {
345
- "@type": "03",
346
- id: SessionManager.instance.currentSessionID(),
347
- main: true,
348
- mask: SessionManager.instance.getCurrentSessionCallBack(s => s.requestMask)
349
- }
350
- }
351
- }));
352
- }
353
- }
354
- postProcess(event, error) {
355
- let status = 0, exception = null;
356
- if (event) {
357
- status = +event.status;
358
- this.restRequestCallBack.dataSize = sizeOf(event.body);
359
- this.restRequestCallBack.linked = this.assertSessionID(this.restRequest.id, event.headers);
360
- }
363
+ let status = 0, exception;
364
+ // HttpResponseBase couvre à la fois HttpResponse (succès) et HttpErrorResponse (erreur HTTP)
365
+ if (response) {
366
+ status = response.status;
367
+ callback.dataSize = response instanceof HttpResponse ? sizeOf(response.body) : -1;
368
+ callback.contentType = extractContentType(response.headers);
369
+ callback.linked = assertSessionID(this.id, response.headers);
370
+ }
371
+ // Erreur réseau/annulation
361
372
  if (error) {
362
- this.restRequestCallBack.linked = error?.headers && this.assertSessionID(this.restRequest.id, error.headers);
363
- status = +error.status;
373
+ status = error.status || 0;
364
374
  exception = {
365
375
  type: error.name,
366
376
  message: error.error && error.status ? JSON.stringify(error.error) : error.message
367
377
  };
368
- this.restRequestCallBack.bodyContent = error instanceof HttpErrorResponse ? JSON.stringify(error.error) : undefined;
369
- this.restRequestCallBack.dataSize = sizeOf(error.error);
370
- }
371
- this.restRequestCallBack.contentType = extractContentType(event?.headers);
372
- this.restRequestCallBack.end = dateNow();
373
- this.restRequestCallBack.status = +status;
374
- let stage = {
375
- "@type": "220",
378
+ callback.bodyContent = JSON.stringify(error.error);
379
+ callback.dataSize = sizeOf(error.error);
380
+ callback.contentType = extractContentType(error.headers);
381
+ callback.linked = assertSessionID(this.id, error.headers);
382
+ }
383
+ callback.status = status;
384
+ dispatchTraces(callback, {
385
+ "@type": TRACE_TYPE_HTTP_REQUEST_STAGE,
376
386
  name: "PROCESS",
377
- start: this.restRequest.start, // use request
378
- end: dateNow(),
387
+ start: this.start, // use request
388
+ end: end,
379
389
  order: 0,
380
390
  exception: exception,
381
- requestId: this.restRequestCallBack.id
382
- };
383
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: this.restRequestCallBack } }));
384
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: stage } }));
385
- }
386
- assertSessionID(id, headers) {
387
- if (headers?.has('x-tracert')) {
388
- if (id == headers.get('x-tracert')) {
389
- return true;
390
- }
391
- }
392
- return false;
391
+ requestId: this.id
392
+ });
393
393
  }
394
394
  }
395
- function toHref(url) {
396
- const href = document.createElement('a');
397
- href.setAttribute('href', url);
398
- return href;
399
- }
400
- function exctractHost(path) {
401
- const portregex = /:\d+/;
402
- return path.replace(portregex, '');
395
+ function assertSessionID(id, headers) {
396
+ return headers?.get(TRACE_HEADER) == id;
403
397
  }
404
398
  function extractAuthSchemeAnduser(headers) {
405
- let auth_user = {
406
- user: undefined,
407
- authScheme: undefined
399
+ const scheme = headers.has('authorization') && headers.get('authorization').match(/^(\w+) /)?.at(1);
400
+ return {
401
+ authScheme: scheme,
402
+ user: scheme ?? extractUser(scheme, headers.get('authorization').split(" ")[1])
408
403
  };
404
+ }
405
+ function extractUser(scheme, authorization) {
409
406
  try {
410
- auth_user.authScheme = headers.has('authorization') && headers.get('authorization').match(/^(\w+) /)?.at(1);
411
- switch (auth_user.authScheme) {
412
- case "Basic":
413
- auth_user.user = atob(headers.get('authorization').split(" ")[1]).toString().split(':')[0];
414
- break;
407
+ switch (scheme) {
408
+ case "Basic": return atob(authorization).toString().split(':')[0];
415
409
  case "Bearer": {
416
- const parts = headers.get('authorization').split(" ")[1].split('.');
410
+ const parts = authorization.split('.');
417
411
  if (parts.length == 3) {
418
- auth_user.user = JSON.parse(atob(parts[1]).toString()).sub;
412
+ return JSON.parse(atob(parts[1]).toString()).sub; //TODO regex .match(/^\w+\.\w+\.(\w+) /)?.at(1)
419
413
  }
414
+ dispatchReport('extractUser', `Invalid Bearer token format: expected 3 parts, got ${parts.length}`);
415
+ return undefined;
416
+ }
417
+ default: {
418
+ dispatchReport('extractUser', `Unknown auth scheme: ${scheme}`);
420
419
  }
421
420
  }
422
421
  }
423
- catch (err) {
424
- createReport(JSON.stringify(err));
422
+ catch (e) {
423
+ dispatchReport('extractUser', e);
425
424
  }
426
- return auth_user;
425
+ return undefined;
427
426
  }
428
427
  function extractContentType(headers) {
429
428
  try {
@@ -432,56 +431,71 @@ function extractContentType(headers) {
432
431
  }
433
432
  }
434
433
  catch (err) {
435
- createReport(JSON.stringify(err));
434
+ dispatchReport('extractContentType', err);
436
435
  }
437
436
  return undefined;
438
437
  }
439
438
  function sizeOf(body) {
440
- return body ? JSON.stringify(body).length : 0;
439
+ if (body) {
440
+ try {
441
+ return new Blob([JSON.stringify(body)]).size;
442
+ }
443
+ catch (e) {
444
+ dispatchReport('sizeOf', e);
445
+ }
446
+ }
447
+ return -1;
441
448
  }
442
449
 
450
+ const COLLECTOR_CONFIG$1 = new InjectionToken('COLLECTOR_CONFIG');
443
451
  class HttpInterceptorService {
444
- constructor() { }
452
+ constructor(techConfig) {
453
+ this.techConfig = techConfig;
454
+ }
445
455
  intercept(req, next) {
446
- let restRequestMonitor = new RestRequestMonitor(req);
447
- let e;
448
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: restRequestMonitor.restRequest } }));
456
+ const monitor = new RestRequestMonitor();
457
+ monitor.preProcess(req);
449
458
  const host = new URL(req.url, window.location.origin).host;
450
- if (!ContextManager.instance.techConfig.hostExcludes?.some((e) => e == host)) {
451
- req = req.clone({ headers: req.headers.set('x-tracert', restRequestMonitor.restRequest.id) });
459
+ if (!this.techConfig.hostExcludes?.some((e) => e == host)) { // exclude les hôtes qui ne permet pas la modification des headers
460
+ req = req.clone({ headers: req.headers.set(TRACE_HEADER, monitor.id) });
452
461
  }
462
+ let response = null;
463
+ let error = null;
453
464
  return next.handle(req).pipe(tap({
454
465
  next: (event) => {
455
466
  if (event instanceof HttpResponse) {
456
- e = event;
467
+ response = event;
457
468
  }
469
+ //todo autres types d'événements (UploadProgress, DownloadProgress, etc.) ignorés pour les traces
458
470
  },
459
- error: (error) => {
460
- e = error;
461
- },
462
- unsubscribe: () => {
463
- e = { status: 0,
464
- name: "Unsubscribed",
465
- message: "The request was cancelled before completion."
466
- };
471
+ error: (err) => {
472
+ error = err;
467
473
  }
468
474
  }), finalize(() => {
469
- e instanceof HttpResponse ? restRequestMonitor.postProcess(e, null) : restRequestMonitor.postProcess(null, e ?? {
470
- status: 0,
471
- name: "IOException",
472
- message: "The remote server is unavailable.",
473
- });
475
+ // Si pas de réponse et pas d'erreur capturé, la requête a été annulée
476
+ if (!response && !error) {
477
+ error = new HttpErrorResponse({
478
+ error: 'The request was cancelled before completion',
479
+ status: 0,
480
+ statusText: 'Cancelled',
481
+ url: req.url
482
+ });
483
+ }
484
+ monitor.postProcess(response, error);
474
485
  }));
475
486
  }
476
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: HttpInterceptorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
477
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: HttpInterceptorService, providedIn: 'root' }); }
487
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: HttpInterceptorService, deps: [{ token: COLLECTOR_CONFIG$1 }], target: i0.ɵɵFactoryTarget.Injectable }); }
488
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: HttpInterceptorService, providedIn: 'root' }); }
478
489
  }
479
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: HttpInterceptorService, decorators: [{
490
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: HttpInterceptorService, decorators: [{
480
491
  type: Injectable,
481
492
  args: [{ providedIn: 'root' }]
482
- }], ctorParameters: () => [] });
493
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
494
+ type: Inject,
495
+ args: [COLLECTOR_CONFIG$1]
496
+ }] }] });
483
497
 
484
- class GlobalErrorHandlerService {
498
+ class GlobalErrorHandler {
485
499
  handleError(error) {
486
500
  try {
487
501
  SessionManager.instance.addException({
@@ -490,211 +504,181 @@ class GlobalErrorHandlerService {
490
504
  });
491
505
  }
492
506
  catch (e) {
493
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("Erreur dans GlobalErrorHandlerService: " + JSON.stringify(e)) } }));
507
+ dispatchReport("GlobalErrorHandlerService.handleError", e);
494
508
  }
495
509
  throw error;
496
510
  }
497
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: GlobalErrorHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
498
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: GlobalErrorHandlerService, providedIn: 'root' }); }
511
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GlobalErrorHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
512
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GlobalErrorHandler, providedIn: 'root' }); }
499
513
  }
500
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: GlobalErrorHandlerService, decorators: [{
514
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: GlobalErrorHandler, decorators: [{
501
515
  type: Injectable,
502
516
  args: [{ providedIn: 'root' }]
503
517
  }] });
504
518
 
505
- function eventTraceScheduledDispatcher() {
506
- return EventTraceScheduledDispatcherService._instance = new EventTraceScheduledDispatcherService();
507
- }
519
+ const EMPTY_ARRAY = Object.freeze([]);
508
520
  class EventTraceScheduledDispatcherService {
509
- constructor() {
510
- this.traceQueue = new Set();
511
- this.sessionSendAttempts = 0;
512
- this.sendSessionfinished = true;
513
- this.instanceSaved = false;
514
- this.interval = interval(ContextManager.instance.techConfig.interval)
521
+ constructor(_techConfig) {
522
+ this._techConfig = _techConfig;
523
+ this.traceQueue = [];
524
+ this.dispatchAttempts = 0;
525
+ this.dispatching = false;
526
+ this.instanceDispatched = false;
527
+ this.wasDestroyed = false;
528
+ this.subscription = interval(_techConfig.interval)
515
529
  .pipe(startWith(0))
516
530
  .pipe(tap$1(() => {
517
- if (this.sendSessionfinished) {
518
- this.sendSessionfinished = false;
519
- window.dispatchEvent(new CustomEvent(PRE_DISPATCH));
520
- this.Dispatch()
531
+ if (!this.dispatching && !this.wasDestroyed && this.instance) {
532
+ this.dispatching = true;
533
+ dispatchExport();
534
+ this.dispatch()
521
535
  .then(arr => this.revertQueueSize(arr))
522
- .catch(err => { })
523
- .finally(() => { this.sendSessionfinished = true; });
536
+ .catch(err => dispatchReport('EventTraceScheduledDispatcher.dispatch', err))
537
+ .finally(() => { this.dispatching = false; });
524
538
  }
525
539
  }))
526
540
  .subscribe();
527
- window.addEventListener(DISPATCH, (e) => {
528
- e.detail.traces && this.addtoQueue(e.detail.traces);
529
- if (e.detail.force) {
530
- this.sendSessions(true);
531
- }
532
- });
541
+ addTraceListener(e => this.appendTrace(e.detail.traces));
542
+ addShutdownListener(e => this.destroy());
543
+ }
544
+ appendTrace(events) {
545
+ if (!this.wasDestroyed) {
546
+ events?.forEach(event => this.traceQueue.push(event));
547
+ }
533
548
  }
534
- Dispatch() {
535
- if (this.instanceSaved) {
536
- return this.sendSessions();
549
+ trace(instance) {
550
+ this.instance = instance;
551
+ }
552
+ dispatch() {
553
+ if (this.instanceDispatched) {
554
+ return this.dispatchTraces();
537
555
  }
538
- return this.postInstanceEnv().then((ok) => {
556
+ return this.dispatchInstance().then(ok => {
539
557
  if (ok) {
540
- return this.sendSessions();
558
+ this.instanceDispatched = true;
559
+ return this.dispatchTraces();
541
560
  }
542
- this.sessionSendAttempts % 5 == 0 && console.warn(`Error while attempting to send Environement instance, attempts ${this.sessionSendAttempts}`);
561
+ console.warn(`Error while attempting to send Environement instance, attempts ${this.dispatchAttempts}`);
543
562
  return Promise.reject(new Error('No instance id'));
544
563
  });
545
564
  }
546
- sendSessions(instanceComplete) {
547
- if (this.traceQueue.size === 0) {
548
- return Promise.resolve(new Set());
565
+ dispatchTraces(destroy) {
566
+ if (this.traceQueue.length === 0) {
567
+ return Promise.resolve(EMPTY_ARRAY);
549
568
  }
550
- let uri = ContextManager.instance.techConfig.sessionApi + "?attempts=" + ++this.sessionSendAttempts;
551
- if (instanceComplete) {
569
+ let uri = this._techConfig.sessionApi + "?attempts=" + ++this.dispatchAttempts;
570
+ if (destroy) {
552
571
  uri += "&end=" + new Date().toISOString();
553
572
  }
554
- const sessions = this.traceQueue;
555
- this.traceQueue = new Set();
556
- return fetch(uri, this.getRequestInit(sessions))
573
+ const traces = [...this.traceQueue];
574
+ this.traceQueue.length = 0;
575
+ return fetch(uri, {
576
+ method: 'PUT',
577
+ headers: { 'Content-Type': 'application/json' },
578
+ mode: 'cors',
579
+ keepalive: true,
580
+ body: JSON.stringify(traces)
581
+ })
557
582
  .then(res => {
558
583
  if (res.ok) {
559
- this.sessionSendAttempts = 0;
560
- return new Set();
584
+ this.dispatchAttempts = 0;
585
+ return EMPTY_ARRAY;
561
586
  }
587
+ console.warn(`Error while attempting to send sessions, attempts: ${this.dispatchAttempts}`);
562
588
  if (res.status >= 400 && res.status < 500) {
563
- return this.handleEventTraceSavingError(sessions);
589
+ return traces; //retry on bad request !?
564
590
  }
565
591
  return res.json()
566
- .then(body => body?.retry ? this.handleEventTraceSavingError(sessions) : new Set())
567
- .catch(() => new Set());
592
+ .then(body => body?.retry ? traces : EMPTY_ARRAY)
593
+ .catch(() => EMPTY_ARRAY);
568
594
  })
569
- .catch(() => this.handleEventTraceSavingError(sessions));
570
- }
571
- handleEventTraceSavingError(sessions) {
572
- this.sessionSendAttempts % 5 == 0 && console.warn(`Error while attempting to send sessions, attempts: ${this.sessionSendAttempts}`);
573
- return sessions;
574
- }
575
- getRequestInit(sessionList) {
576
- return {
577
- method: 'PUT',
578
- headers: { 'Content-Type': 'application/json' },
579
- mode: 'cors',
580
- keepalive: true,
581
- body: JSON.stringify(Array.from(sessionList))
582
- };
595
+ .catch(() => {
596
+ console.warn(`Error while attempting to send sessions, attempts: ${this.dispatchAttempts}`); //no cnx !
597
+ return traces;
598
+ });
583
599
  }
584
- postInstanceEnv() {
585
- this.sessionSendAttempts++;
586
- return fetch(ContextManager.instance.techConfig.instanceApi, {
600
+ dispatchInstance() {
601
+ this.dispatchAttempts++;
602
+ return fetch(this._techConfig.instanceApi, {
587
603
  method: 'POST',
588
604
  headers: { 'Content-Type': 'application/json' },
589
605
  mode: 'cors',
590
606
  keepalive: true,
591
- body: JSON.stringify(ContextManager.instance.instanceEnv)
607
+ body: JSON.stringify(this.instance)
592
608
  })
593
609
  .then(res => res.ok ? res.text().then(id => {
594
- this.sessionSendAttempts = 0;
595
- return this.instanceSaved = true;
610
+ this.dispatchAttempts = 0;
611
+ return true;
596
612
  }) : false)
597
613
  .catch(err => false);
598
614
  }
599
- revertQueueSize(sessions) {
600
- sessions.forEach(session => this.traceQueue.add(session));
601
- if (this.traceQueue.size > ContextManager.instance.techConfig.queueCapacity) {
602
- const items = Array.from(this.traceQueue).slice(0, ContextManager.instance.techConfig.queueCapacity);
603
- this.traceQueue = new Set(items);
604
- }
605
- }
606
- async addtoQueue(event) {
607
- try {
608
- if (event) {
609
- this.traceQueue.add(event);
615
+ revertQueueSize(traces) {
616
+ if (!this.wasDestroyed) {
617
+ this.traceQueue.unshift(...traces);
618
+ if (this.traceQueue.length > this._techConfig.queueCapacity) {
619
+ this.traceQueue.splice(this._techConfig.queueCapacity);
610
620
  }
611
621
  }
612
- catch (e) {
613
- this.addtoQueue(createReport(String(e)));
614
- }
615
622
  }
616
- onDestroy() {
617
- if (this.interval) {
618
- this.interval.unsubscribe();
619
- }
623
+ destroy() {
624
+ this.subscription?.unsubscribe();
625
+ this.wasDestroyed = true;
626
+ dispatchExport(); //last metric ??
627
+ this.dispatchTraces(true);
620
628
  }
621
629
  }
622
-
623
- function routeHandler(event) {
624
- if (event instanceof NavigationStart) {
625
- SessionManager.instance.navigate(event.url);
626
- }
627
- if (event instanceof NavigationEnd || event instanceof NavigationCancel) {
628
- setTimeout(() => SessionManager.instance.updateSession(), 0);
629
- }
630
- }
631
- function windowUnloadHandler(event) {
632
- SessionManager.instance.navigate();
633
- }
634
- function bfCacheHandler(event) {
635
- if (event.persisted) { // if the page was restored from bfcache
636
- initContextManagerAndDispatcher();
637
- SessionManager.instance.navigate(document.URL);
638
- setTimeout(() => SessionManager.instance.updateSession(), 0);
639
- }
630
+ function eventTraceScheduledDispatcher(tech) {
631
+ return EventTraceScheduledDispatcherService._instance = new EventTraceScheduledDispatcherService(tech);
640
632
  }
641
633
 
642
- function machineRessourceUsageHandler(event) {
643
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: getMemoryInfo() } }));
644
- }
645
- function getMemoryInfo() {
634
+ const eventHandlers = {
635
+ 'click': (target) => lookUpChild(target, 1),
636
+ };
637
+ function initUserActionMonitor() {
646
638
  try {
647
- const memory = performance.memory;
648
- return {
649
- '@type': '01',
650
- instant: dateNow(),
651
- usedHeap: memory.usedJSHeapSize / (1024 * 1024),
652
- commitedHeap: memory.totalJSHeapSize / (1024 * 1024),
653
- };
639
+ const body = window.document.body;
640
+ body.addEventListener('click', globalHandler, true);
641
+ body.addEventListener('change', globalHandler, true);
642
+ body.addEventListener('scrollend', globalHandler, true);
643
+ body.addEventListener('dragend', globalHandler, true);
644
+ window.document.addEventListener('DOMContentLoaded', globalHandler, true);
654
645
  }
655
646
  catch (e) {
656
- createReport(JSON.stringify(e));
647
+ dispatchReport("initUserActionMonitor", e);
657
648
  }
658
- return null;
659
- }
660
-
661
- function routerEventsListener(router) {
662
- router.events.subscribe(routeHandler);
663
649
  }
664
- function beforeUnloadListener() {
665
- window.addEventListener(BEFOREUNLOAD, windowUnloadHandler);
650
+ function globalHandler(event) {
651
+ const target = event.target;
652
+ const eventType = event.type;
653
+ try {
654
+ if (eventHandlers.hasOwnProperty(eventType) && !eventHandlers[eventType](target)) {
655
+ return;
656
+ }
657
+ addActionUser(eventType, target);
658
+ }
659
+ catch (err) {
660
+ dispatchReport("UserACTION.globalHandler", err);
661
+ }
666
662
  }
667
- function beforeDispatchListener() {
668
- if (ContextManager.instance.techConfig.resources && 'memory' in performance) {
669
- window.addEventListener(PRE_DISPATCH, machineRessourceUsageHandler);
663
+ function lookUpChild(t, depth) {
664
+ if (t.hasChildNodes() && t.children.length <= 5) {
665
+ if (++depth > 5) {
666
+ return false;
667
+ }
668
+ return Array.from(t.childNodes).reduce((acc, c) => acc && lookUpChild(c, depth), true);
670
669
  }
670
+ return true;
671
671
  }
672
- function bfCacheListener() {
673
- window.addEventListener('pageshow', bfCacheHandler);
672
+ function addActionUser(eventType, target) {
673
+ dispatchTraces({
674
+ ...SessionManager.instance.initUserAction(),
675
+ '@type': TRACE_TYPE_USER_ACTION,
676
+ type: eventType,
677
+ instant: dateNow(),
678
+ name: extractName(target),
679
+ nodeName: target.tagName?.toLowerCase()
680
+ });
674
681
  }
675
-
676
- const genericMap = [
677
- t => t.getAttribute('placeholder'),
678
- t => t.getAttribute('title'),
679
- t => t.innerText,
680
- t => t.getAttribute('name'),
681
- t => t.getAttribute('id'),
682
- ];
683
- const MAP = {
684
- 'img': [
685
- t => t.getAttribute('alt'),
686
- t => t.getAttribute('src'),
687
- ],
688
- 'input': [
689
- t => t.getAttribute('name'),
690
- ],
691
- 'a': [
692
- t => t.getAttribute('href'),
693
- ],
694
- 'label': [
695
- t => t.getAttribute('for'),
696
- ],
697
- };
698
682
  function getFirst(c, t) {
699
683
  for (const o of c) {
700
684
  let r = o(t)?.trim();
@@ -721,385 +705,202 @@ function extractName(t) {
721
705
  }
722
706
  return null;
723
707
  }
724
-
725
- let eventHandlers = {
726
- 'click': (target) => lookUpChild(target, 1),
708
+ const genericMap = [
709
+ t => t.getAttribute('placeholder'),
710
+ t => t.getAttribute('title'),
711
+ t => t.innerText,
712
+ t => t.getAttribute('name'),
713
+ t => t.getAttribute('id'),
714
+ ];
715
+ const MAP = {
716
+ 'img': [
717
+ t => t.getAttribute('alt'),
718
+ t => t.getAttribute('src'),
719
+ ],
720
+ 'input': [
721
+ t => t.getAttribute('name'),
722
+ ],
723
+ 'a': [
724
+ t => t.getAttribute('href'),
725
+ ],
726
+ 'label': [
727
+ t => t.getAttribute('for'),
728
+ ],
727
729
  };
728
- function analyticsEventsListener() {
729
- if (ContextManager.instance.techConfig.analytics) {
730
- try {
731
- const body = window.document.body;
732
- body.addEventListener('click', globalHandler, true);
733
- body.addEventListener('change', (event) => globalHandler(event), true);
734
- body.addEventListener('scrollend', (event) => globalHandler(event), true);
735
- body.addEventListener('dragend', (event) => globalHandler(event), true);
736
- window.document.addEventListener('DOMContentLoaded', (event) => globalHandler(event), true);
737
- }
738
- catch (e) {
739
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("Error while subscribing to analytics user events: " + JSON.stringify(e)) } }));
740
- }
741
- }
742
- }
743
- function globalHandler(event) {
744
- let target = event.target;
745
- let eventType = event.type;
730
+
731
+ function initNavigationMonitor(router) {
746
732
  try {
747
- if (eventHandlers.hasOwnProperty(eventType) && !eventHandlers[eventType](target)) {
748
- return;
749
- }
750
- addActionUser(eventType, target);
733
+ router.events.subscribe(routeHandler);
734
+ window.addEventListener('pageshow', bfCacheHandler);
735
+ window.addEventListener("beforeunload", windowUnloadHandler);
751
736
  }
752
- catch (err) {
753
- console.warn(err);
737
+ catch (e) {
738
+ dispatchReport('initNavigationMonitor', e);
754
739
  }
755
740
  }
756
- function lookUpChild(t, depth) {
757
- if (t.hasChildNodes() && t.children.length <= 5) {
758
- if (++depth > 5) {
759
- return false;
760
- }
761
- return Array.from(t.childNodes).reduce((acc, c) => acc && lookUpChild(c, depth), true);
741
+ function routeHandler(event) {
742
+ if (event instanceof NavigationStart) {
743
+ SessionManager.instance.navigate(event.url);
744
+ }
745
+ if (event instanceof NavigationEnd || event instanceof NavigationCancel) {
746
+ setTimeout(() => SessionManager.instance.updateSession(), 0);
762
747
  }
763
- return true;
764
- }
765
- function addActionUser(eventType, target) {
766
- const ua = {
767
- '@type': "300",
768
- type: eventType,
769
- start: dateNow(),
770
- name: extractName(target),
771
- nodeName: target.tagName?.toLowerCase(),
772
- sessionId: SessionManager.instance?.currentSessionID()
773
- };
774
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: ua } }));
775
748
  }
776
-
777
- function eventTraceDebugger() {
778
- EventTraceDebugger._instance = new EventTraceDebugger();
749
+ function windowUnloadHandler(event) {
750
+ SessionManager.instance.navigate();
751
+ dispatchShutown();
779
752
  }
780
- class EventTraceDebugger {
781
- constructor() {
782
- this.restRequests = [];
783
- this.httpRequestStages = [];
784
- this.localRequests = [];
785
- this.userActions = [];
786
- this.logEntries = [];
787
- try {
788
- if (ContextManager.instance.techConfig.debugMode || WIN['inspect']) {
789
- window.addEventListener(DISPATCH, this.handleTraces.bind(this));
790
- }
791
- WIN["inspect-setup"] = {
792
- init: () => {
793
- WIN['inspect'] = true;
794
- EventTraceDebugger.init();
795
- },
796
- printConfig: () => console.log(ContextManager.instance.techConfig),
797
- printInstance: () => console.log(ContextManager.instance)
798
- };
799
- }
800
- catch (e) {
801
- console.warn(e);
802
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("Error while setting up debug mode: " + JSON.stringify(e)) } }));
803
- }
753
+ function bfCacheHandler(event) {
754
+ if (event.persisted) { // if the page was restored from bfcache
755
+ dispatchReload();
756
+ SessionManager.instance.navigate(document.URL);
757
+ setTimeout(() => SessionManager.instance.updateSession(), 0);
804
758
  }
805
- handleTraces(e) {
806
- let trace = e.detail.traces;
807
- if (trace && !e.detail.force) {
808
- switch (trace['@type']) {
809
- case 'main-ses':
810
- if (trace.end) {
811
- console.log(this.prettySessionFormat(SessionManager.instance.currentSession));
812
- }
813
- break;
814
- case 'http-req':
815
- this.restRequests.push(trace);
816
- break;
817
- case 'http-stg':
818
- this.httpRequestStages.push(trace);
819
- break;
820
- case 'user-act':
821
- this.userActions.push(trace);
822
- break;
823
- case 'locl-req':
824
- this.localRequests.push(trace);
825
- break;
826
- case 'log':
827
- trace.sessionId ? this.logEntries.push(trace) : console.warn(this.prettyLogEntryFormat(trace));
828
- break;
829
- case 'rsrc-usg':
830
- console.log(this.prettyMachineResourceUsageFormat(trace));
831
- break;
832
- }
759
+ }
760
+
761
+ function initResourceUsageMonitor() {
762
+ try {
763
+ if ('memory' in performance) {
764
+ addExportListener(ressourceUsageHandler);
833
765
  }
834
766
  }
835
- static init() {
836
- return EventTraceDebugger._instance = new EventTraceDebugger();
767
+ catch (e) {
768
+ dispatchReport('initResourceUsageMonitor', e);
837
769
  }
838
- prettySessionFormat(session) {
839
- let s = `[${session.name}]`;
840
- if (session.user) {
841
- s += `<${session.user}>`;
842
- }
843
- if (session.location) {
844
- s += `(${session.location}) `;
845
- }
846
- //s+= this.prettyDurationFormat(session.start, session.end)+'\n';
847
- s += this.getChildPrint(this.restRequests, this.prettyRestRequestFormat.bind(this));
848
- s += this.getChildPrint(this.localRequests, this.prettyLocalRequestFormat.bind(this));
849
- s += this.getChildPrint(this.userActions, this.prettyActionUserFormat.bind(this));
850
- s += this.getChildPrint(this.logEntries, this.prettyLogEntryFormat.bind(this));
851
- this.resetList();
852
- return s;
853
- }
854
- prettyRestRequestFormat(rest) {
855
- let s = ` - [${rest.method}]`;
856
- if (rest.protocol) {
857
- s += `${rest.protocol}://`;
858
- }
859
- if (rest.host) {
860
- s += rest.host;
861
- }
862
- if (rest.port > 0) {
863
- s += `:${rest.port}`;
864
- }
865
- if (rest.path) {
866
- if (!rest.path.startsWith("/") && !s.endsWith("/")) {
867
- s += '/';
868
- }
869
- s += rest.path;
870
- }
871
- if (rest.query) {
872
- s += rest.query;
873
- }
874
- // s+= ` >> ${rest.status} `
875
- // s+= this.prettyDurationFormat(rest.start,rest.end);
876
- s += '\n';
877
- this.httpRequestStages.filter((s) => s.requestId == rest.id).forEach((stage) => {
878
- s += this.prettyHttpRequestStageFormat(stage);
770
+ }
771
+ function ressourceUsageHandler() {
772
+ try {
773
+ const memory = performance.memory;
774
+ dispatchTraces({
775
+ '@type': TRACE_TYPE_RESOURCE_USAGE,
776
+ instant: dateNow(),
777
+ usedHeap: memory.usedJSHeapSize / (1024 * 1024),
778
+ commitedHeap: memory.totalJSHeapSize / (1024 * 1024),
879
779
  });
880
- return s;
881
- }
882
- prettyHttpRequestStageFormat(stage) {
883
- let s = ` - [${stage.name}]`;
884
- s += " >> ";
885
- if (stage.exception?.type) {
886
- s += ` ${stage.exception?.type}:`;
887
- }
888
- if (stage.exception?.message) {
889
- s += ` ${stage.exception.message}`;
890
- }
891
- s += ` ${this.prettyDurationFormat(stage.start, stage.end)}`;
892
- return s;
893
- }
894
- prettyLocalRequestFormat(local) {
895
- let s = ` - [${local.name}]`;
896
- if (local.location) {
897
- s += `(${local.location})`;
898
- }
899
- /*s+= " >> ";
900
- if(local.exception?.type){
901
- s+= ` ${local.exception?.type}:`;
902
- }
903
- if(local.exception?.message){
904
- s+= ` ${local.exception.message}`
905
- }
906
- s+= ` ${this.prettyDurationFormat(local.start, local.end)}`;*/
907
- return s;
908
- }
909
- prettyLogEntryFormat(log) {
910
- let s = ` - [${log.level}]`;
911
- if (log.message) {
912
- s += ` ${log.message}`;
913
- }
914
- s += ` >> ${new Date(log.instant * 1000).toISOString()}`;
915
- return s;
916
- }
917
- prettyMachineResourceUsageFormat(resource) {
918
- return ` - [METRIC] heap :${resource.usedHeap.toFixed(2)}/${resource.commitedHeap.toFixed(2)} >> ${new Date(resource.instant * 1000).toISOString()}`;
919
- }
920
- prettyActionUserFormat(userAction) {
921
- let s = ` - `;
922
- if (userAction.type) {
923
- s += `[${userAction.type}]`;
924
- }
925
- if (userAction.nodeName) {
926
- s += `<${userAction.nodeName}>`;
927
- }
928
- if (userAction.name) {
929
- s += `(${userAction.name}) `;
930
- }
931
- s += ` >> ${new Date(userAction.start * 1000).toISOString()}`;
932
- return s;
933
- }
934
- getChildPrint(o, print) {
935
- let s = "";
936
- if (o.length) {
937
- o = o.filter((r) => {
938
- if (r.sessionId === SessionManager.instance.currentSessionID()) {
939
- s += print(r) + '\n';
940
- return false;
941
- }
942
- return true;
943
- });
944
- const remainingRequests = o.map((r) => print(r)).join('\n');
945
- if (remainingRequests) {
946
- console.warn(remainingRequests);
947
- }
948
- }
949
- return s;
950
- }
951
- prettyDurationFormat(start, end) {
952
- return start && end ? `(in ${(end - start).toFixed(2)} s)` : '';
953
780
  }
954
- resetList() {
955
- this.restRequests = [];
956
- this.httpRequestStages = [];
957
- this.localRequests = [];
958
- this.userActions = [];
781
+ catch (e) {
782
+ dispatchReport('ressourceUsageHandler', e);
959
783
  }
960
784
  }
961
785
 
786
+ const COLLECTOR_CONFIG = new InjectionToken('COLLECTOR_CONFIG');
962
787
  class NgCollectorModule {
963
- static { this.forRootCalled = false; }
964
788
  static forRoot(configuration) {
965
- this.configuration = configuration;
966
- if (configuration?.enabled && !NgCollectorModule.forRootCalled) {
967
- NgCollectorModule.forRootCalled = true;
789
+ const providers = [];
790
+ if (configuration?.enabled) {
968
791
  try {
969
- ContextManager.init(configuration);
970
- return {
971
- ngModule: NgCollectorModule,
972
- providers: [
973
- //provideAppInitializer(initializeEvents),
974
- { provide: APP_INITIALIZER, useFactory: initializeEvents, deps: [Router], multi: true },
975
- { provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, multi: true },
976
- { provide: ErrorHandler, useClass: GlobalErrorHandlerService }
977
- ]
978
- };
792
+ let config = initializeCollector(configuration);
793
+ providers.push({ provide: COLLECTOR_CONFIG, useValue: config }, { provide: APP_INITIALIZER, useFactory: initializeEventsFactory, deps: [COLLECTOR_CONFIG, Router], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, deps: [COLLECTOR_CONFIG], multi: true }, { provide: ErrorHandler, useClass: GlobalErrorHandler });
979
794
  }
980
795
  catch (e) {
981
796
  console.warn(`invalid Configuration, Ng-collector is disabled because of this ${e.message}`);
982
797
  }
983
798
  }
984
- return {
985
- ngModule: NgCollectorModule
986
- };
799
+ return { ngModule: NgCollectorModule, providers: providers };
987
800
  }
988
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: NgCollectorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
989
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.18", ngImport: i0, type: NgCollectorModule }); }
990
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: NgCollectorModule }); }
801
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: NgCollectorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
802
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: NgCollectorModule }); }
803
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: NgCollectorModule }); }
991
804
  }
992
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: NgCollectorModule, decorators: [{
805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: NgCollectorModule, decorators: [{
993
806
  type: NgModule
994
807
  }] });
995
- function initializeEvents(router) {
808
+ function initializeEventsFactory(config, router) {
996
809
  return () => {
997
- initContextManagerAndDispatcher();
998
- eventTraceDebugger();
999
- analyticsEventsListener();
810
+ if (config.tech.analytics) {
811
+ initUserActionMonitor();
812
+ }
813
+ if (config.tech.resources) {
814
+ initResourceUsageMonitor();
815
+ }
816
+ initNavigationMonitor(router);
1000
817
  //storageEventListener();
1001
- beforeDispatchListener();
1002
- beforeUnloadListener();
1003
- routerEventsListener(router);
1004
- bfCacheListener();
818
+ //eventTraceDebugger
819
+ addReloadListener(e => {
820
+ reloadCollector(config);
821
+ });
1005
822
  };
1006
823
  }
1007
- function initContextManagerAndDispatcher() {
1008
- ContextManager.init(NgCollectorModule.configuration);
1009
- const dispatcher = eventTraceScheduledDispatcher();
1010
- window.addEventListener(DISPATCH, (e) => {
1011
- if (e.detail.force) {
1012
- dispatcher.onDestroy();
1013
- }
1014
- });
824
+ function initializeCollector(config) {
825
+ const id = crypto.randomUUID();
826
+ const tech = validateAndGetConfig(config, id);
827
+ const dispatch = eventTraceScheduledDispatcher(tech);
828
+ const instance = createInstance(config, id);
829
+ dispatch.trace(instance);
830
+ sessionManager(tech);
831
+ return { tech: tech, instance: instance };
832
+ }
833
+ function reloadCollector(config) {
834
+ const id = crypto.randomUUID();
835
+ refreshConfig(id, config.tech);
836
+ const dispatch = eventTraceScheduledDispatcher(config.tech);
837
+ config.instance.id = id;
838
+ dispatch.trace(config.instance);
839
+ sessionManager(config.tech);
840
+ }
841
+
842
+ class LocalRequestMonitor {
843
+ constructor() {
844
+ this.start = dateNow();
845
+ this.id = crypto.randomUUID();
846
+ }
847
+ preProcess(propertyKey, target) {
848
+ dispatchTraces({
849
+ ...WIN["inspect-session-manager"]?.initRestRequest(RequestMask.LOCAL),
850
+ "@type": TRACE_TYPE_LOCAL_REQUEST,
851
+ id: this.id,
852
+ name: propertyKey,
853
+ location: target.constructor.name,
854
+ user: WIN["inspect-session-manager"]?.currentSession?.user, //bad access
855
+ start: this.start
856
+ });
857
+ }
858
+ postProcess(exception) {
859
+ dispatchTraces({
860
+ "@type": TRACE_TYPE_LOCAL_REQUEST_CALLBACK,
861
+ id: this.id,
862
+ exception: exception,
863
+ end: dateNow()
864
+ });
865
+ }
1015
866
  }
1016
867
 
1017
868
  function TraceableStage() {
1018
869
  return function (target, propertyKey, descriptor) {
1019
870
  const originalMethod = descriptor.value;
1020
871
  descriptor.value = function (...args) {
872
+ const monitor = new LocalRequestMonitor();
1021
873
  let exception;
1022
- let start, end;
1023
- start = Date.now();
1024
874
  try {
875
+ monitor.preProcess(propertyKey, target);
1025
876
  return originalMethod.apply(this, args);
1026
877
  }
1027
878
  catch (e) {
1028
- let type = null, message = null;
1029
- if (e) {
1030
- if (typeof e === "string") {
1031
- message = e;
1032
- }
1033
- else if (e instanceof Error) {
1034
- type = e.name;
1035
- message = e.message;
1036
- }
1037
- else {
1038
- message = JSON.stringify(e);
1039
- }
1040
- }
1041
- exception = {
1042
- type: type,
1043
- message: message
1044
- };
879
+ exception = resolveException(e);
1045
880
  throw e;
1046
881
  }
1047
882
  finally {
1048
- end = Date.now();
1049
- let id = crypto.randomUUID();
1050
- if (WIN["inspect-session-manager"]?.currentSessionID() != null) {
1051
- let doUpdateMask = WIN["inspect-session-manager"]?.updateMask(RequestMask.LOCAL);
1052
- doUpdateMask && window.dispatchEvent(new CustomEvent(DISPATCH, { detail: {
1053
- traces: {
1054
- "@type": "03",
1055
- id: WIN["inspect-session-manager"]?.currentSessionID(),
1056
- main: true,
1057
- mask: WIN["inspect-session-manager"]?.getCurrentSessionCallBack((s) => s.requestMask)
1058
- }
1059
- }
1060
- }));
1061
- }
1062
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: {
1063
- "@type": "110",
1064
- id: id,
1065
- name: propertyKey,
1066
- location: target.constructor.name,
1067
- user: WIN["inspect-session-manager"]?.currentSession?.user,
1068
- start: start,
1069
- sessionId: WIN["inspect-session-manager"]?.currentSession?.id
1070
- }
1071
- }
1072
- }));
1073
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: {
1074
- "@type": "111",
1075
- id: id,
1076
- exception: exception,
1077
- end: end
1078
- }
1079
- }
1080
- }));
883
+ monitor.postProcess(exception);
1081
884
  }
1082
885
  };
1083
886
  return descriptor;
1084
887
  };
1085
888
  }
1086
-
1087
- class LogService {
1088
- static info(message) {
1089
- if (message) {
1090
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createLogEntry("INFO", message, SessionManager.instance?.currentSessionID()) } }));
889
+ function resolveException(e) {
890
+ let type = null, message = null;
891
+ if (e) {
892
+ if (typeof e === "string") {
893
+ message = e;
1091
894
  }
1092
- }
1093
- static warn(message) {
1094
- if (message) {
1095
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createLogEntry("WARN", message, SessionManager.instance?.currentSessionID()) } }));
895
+ else if (e instanceof Error) {
896
+ type = e.name;
897
+ message = e.message;
1096
898
  }
1097
- }
1098
- static error(message) {
1099
- if (message) {
1100
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createLogEntry("ERROR", message, SessionManager.instance?.currentSessionID()) } }));
899
+ else {
900
+ message = JSON.stringify(e);
1101
901
  }
1102
902
  }
903
+ return { type, message };
1103
904
  }
1104
905
 
1105
906
  /*
@@ -1110,5 +911,5 @@ class LogService {
1110
911
  * Generated bundle index. Do not edit.
1111
912
  */
1112
913
 
1113
- export { LogService, NgCollectorModule, TraceableStage, initContextManagerAndDispatcher, initializeEvents };
914
+ export { NgCollectorModule, TraceableStage, initializeCollector, initializeEventsFactory, reloadCollector, resolveException, sessionlogger };
1114
915
  //# sourceMappingURL=oneteme-inspect-ng-collector.mjs.map