@oneteme/inspect-ng-collector 1.3.0 → 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);
61
+ }
62
+ function addReloadListener(fn) {
63
+ eventTarget.addEventListener(RELOAD, fn);
92
64
  }
93
- function toURL$1(host, path) {
94
- return host.endsWith(SLASH$1) || path.startsWith(SLASH$1) ? host + path : [host, path].join(SLASH$1);
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: getStringOrCall(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: 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.17", ngImport: i0, type: HttpInterceptorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
477
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", 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.17", 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,208 +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.17", ngImport: i0, type: GlobalErrorHandlerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
498
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", 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.17", 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
- 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()
521
- .catch(err => { })
522
- .finally(() => { this.sendSessionfinished = true; });
531
+ if (!this.dispatching && !this.wasDestroyed && this.instance) {
532
+ this.dispatching = true;
533
+ dispatchExport();
534
+ this.dispatch()
535
+ .then(arr => this.revertQueueSize(arr))
536
+ .catch(err => dispatchReport('EventTraceScheduledDispatcher.dispatch', err))
537
+ .finally(() => { this.dispatching = false; });
523
538
  }
524
539
  }))
525
540
  .subscribe();
526
- window.addEventListener(DISPATCH, (e) => {
527
- e.detail.traces && this.addtoQueue(e.detail.traces);
528
- if (e.detail.force) {
529
- this.sendSessions(true);
530
- this.onDestroy();
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
+ }
548
+ }
549
+ trace(instance) {
550
+ this.instance = instance;
533
551
  }
534
- Dispatch() {
535
- if (this.instanceSaved) {
536
- return this.sendSessions();
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
- let uri = ContextManager.instance.techConfig.sessionApi + "?attempts=" + ++this.sessionSendAttempts;
549
- if (instanceComplete) {
550
- uri += "&end=" + new Date().toISOString();
551
- }
552
- let sessions = this.traceQueue;
553
- this.traceQueue = new Set();
554
- return fetch(uri, this.getRequestInit(sessions))
555
- .then(res => {
556
- if (res.ok) {
557
- this.sessionSendAttempts = 0;
558
- return sessions.size;
559
- }
560
- return this.handleEventTraceSavingError(sessions);
561
- })
562
- .catch(() => this.handleEventTraceSavingError(sessions));
565
+ dispatchTraces(destroy) {
566
+ if (this.traceQueue.length === 0) {
567
+ return Promise.resolve(EMPTY_ARRAY);
563
568
  }
564
- return Promise.resolve(0);
565
- }
566
- handleEventTraceSavingError(sessions) {
567
- this.sessionSendAttempts % 5 == 0 && console.warn(`Error while attempting to send sessions, attempts: ${this.sessionSendAttempts}`);
568
- this.revertQueueSize(sessions);
569
- return -1;
570
- }
571
- getRequestInit(sessionList) {
572
- return {
569
+ let uri = this._techConfig.sessionApi + "?attempts=" + ++this.dispatchAttempts;
570
+ if (destroy) {
571
+ uri += "&end=" + new Date().toISOString();
572
+ }
573
+ const traces = [...this.traceQueue];
574
+ this.traceQueue.length = 0;
575
+ return fetch(uri, {
573
576
  method: 'PUT',
574
577
  headers: { 'Content-Type': 'application/json' },
575
578
  mode: 'cors',
576
579
  keepalive: true,
577
- body: JSON.stringify(Array.from(sessionList))
578
- };
580
+ body: JSON.stringify(traces)
581
+ })
582
+ .then(res => {
583
+ if (res.ok) {
584
+ this.dispatchAttempts = 0;
585
+ return EMPTY_ARRAY;
586
+ }
587
+ console.warn(`Error while attempting to send sessions, attempts: ${this.dispatchAttempts}`);
588
+ if (res.status >= 400 && res.status < 500) {
589
+ return traces; //retry on bad request !?
590
+ }
591
+ return res.json()
592
+ .then(body => body?.retry ? traces : EMPTY_ARRAY)
593
+ .catch(() => EMPTY_ARRAY);
594
+ })
595
+ .catch(() => {
596
+ console.warn(`Error while attempting to send sessions, attempts: ${this.dispatchAttempts}`); //no cnx !
597
+ return traces;
598
+ });
579
599
  }
580
- postInstanceEnv() {
581
- this.sessionSendAttempts++;
582
- return fetch(ContextManager.instance.techConfig.instanceApi, {
600
+ dispatchInstance() {
601
+ this.dispatchAttempts++;
602
+ return fetch(this._techConfig.instanceApi, {
583
603
  method: 'POST',
584
604
  headers: { 'Content-Type': 'application/json' },
585
605
  mode: 'cors',
586
606
  keepalive: true,
587
- body: JSON.stringify(ContextManager.instance.instanceEnv)
607
+ body: JSON.stringify(this.instance)
588
608
  })
589
609
  .then(res => res.ok ? res.text().then(id => {
590
- this.sessionSendAttempts = 0;
591
- return this.instanceSaved = true;
610
+ this.dispatchAttempts = 0;
611
+ return true;
592
612
  }) : false)
593
613
  .catch(err => false);
594
614
  }
595
- revertQueueSize(sessions) {
596
- sessions.forEach(session => this.traceQueue.add(session));
597
- if (this.traceQueue.size > ContextManager.instance.techConfig.queueCapacity) {
598
- const items = Array.from(this.traceQueue).slice(0, ContextManager.instance.techConfig.queueCapacity);
599
- this.traceQueue = new Set(items);
600
- }
601
- }
602
- async addtoQueue(event) {
603
- try {
604
- if (event) {
605
- 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);
606
620
  }
607
621
  }
608
- catch (e) {
609
- this.addtoQueue(createReport(String(e)));
610
- }
611
622
  }
612
- onDestroy() {
613
- this.traceQueue.clear();
614
- this.sendSessionfinished = true;
615
- this.instanceSaved = false;
616
- this.sessionSendAttempts = 0;
623
+ destroy() {
624
+ this.subscription?.unsubscribe();
625
+ this.wasDestroyed = true;
626
+ dispatchExport(); //last metric ??
627
+ this.dispatchTraces(true);
617
628
  }
618
629
  }
619
-
620
- function routeHandler(event) {
621
- if (event instanceof NavigationStart) {
622
- SessionManager.instance.navigate(event.url);
623
- }
624
- if (event instanceof NavigationEnd || event instanceof NavigationCancel) {
625
- setTimeout(() => SessionManager.instance.updateSession(), 0);
626
- }
627
- }
628
- function windowUnloadHandler(event) {
629
- SessionManager.instance.navigate();
630
- }
631
- function bfCacheHandler(event) {
632
- if (event.persisted) { // if the page was restored from bfcache
633
- ContextManager.init(NgCollectorModule.configuration);
634
- SessionManager.instance.navigate(document.URL);
635
- setTimeout(() => SessionManager.instance.updateSession(), 0);
636
- }
630
+ function eventTraceScheduledDispatcher(tech) {
631
+ return EventTraceScheduledDispatcherService._instance = new EventTraceScheduledDispatcherService(tech);
637
632
  }
638
633
 
639
- function machineRessourceUsageHandler(event) {
640
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: getMemoryInfo() } }));
641
- }
642
- function getMemoryInfo() {
634
+ const eventHandlers = {
635
+ 'click': (target) => lookUpChild(target, 1),
636
+ };
637
+ function initUserActionMonitor() {
643
638
  try {
644
- const memory = performance.memory;
645
- return {
646
- '@type': '01',
647
- instant: dateNow(),
648
- usedHeap: memory.usedJSHeapSize / (1024 * 1024),
649
- commitedHeap: memory.totalJSHeapSize / (1024 * 1024),
650
- };
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);
651
645
  }
652
646
  catch (e) {
653
- createReport(JSON.stringify(e));
647
+ dispatchReport("initUserActionMonitor", e);
654
648
  }
655
- return null;
656
649
  }
657
-
658
- function routerEventsListener(router) {
659
- router.events.subscribe(routeHandler);
660
- }
661
- function beforeUnloadListener() {
662
- 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
+ }
663
662
  }
664
- function beforeDispatchListener() {
665
- if (ContextManager.instance.techConfig.resources && 'memory' in performance) {
666
- 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);
667
669
  }
670
+ return true;
668
671
  }
669
- function bfCacheListener() {
670
- 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
+ });
671
681
  }
672
-
673
- const genericMap = [
674
- t => t.getAttribute('placeholder'),
675
- t => t.getAttribute('title'),
676
- t => t.innerText,
677
- t => t.getAttribute('name'),
678
- t => t.getAttribute('id'),
679
- ];
680
- const MAP = {
681
- 'img': [
682
- t => t.getAttribute('alt'),
683
- t => t.getAttribute('src'),
684
- ],
685
- 'input': [
686
- t => t.getAttribute('name'),
687
- ],
688
- 'a': [
689
- t => t.getAttribute('href'),
690
- ],
691
- 'label': [
692
- t => t.getAttribute('for'),
693
- ],
694
- };
695
682
  function getFirst(c, t) {
696
683
  for (const o of c) {
697
684
  let r = o(t)?.trim();
@@ -718,378 +705,202 @@ function extractName(t) {
718
705
  }
719
706
  return null;
720
707
  }
721
-
722
- let eventHandlers = {
723
- '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
+ ],
724
729
  };
725
- function analyticsEventsListener() {
726
- if (ContextManager.instance.techConfig.analytics) {
727
- try {
728
- const body = window.document.body;
729
- body.addEventListener('click', globalHandler, true);
730
- body.addEventListener('change', (event) => globalHandler(event), true);
731
- body.addEventListener('scrollend', (event) => globalHandler(event), true);
732
- body.addEventListener('dragend', (event) => globalHandler(event), true);
733
- window.document.addEventListener('DOMContentLoaded', (event) => globalHandler(event), true);
734
- }
735
- catch (e) {
736
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("Error while subscribing to analytics user events: " + JSON.stringify(e)) } }));
737
- }
738
- }
739
- }
740
- function globalHandler(event) {
741
- let target = event.target;
742
- let eventType = event.type;
730
+
731
+ function initNavigationMonitor(router) {
743
732
  try {
744
- if (eventHandlers.hasOwnProperty(eventType) && !eventHandlers[eventType](target)) {
745
- return;
746
- }
747
- addActionUser(eventType, target);
733
+ router.events.subscribe(routeHandler);
734
+ window.addEventListener('pageshow', bfCacheHandler);
735
+ window.addEventListener("beforeunload", windowUnloadHandler);
748
736
  }
749
- catch (err) {
750
- console.warn(err);
737
+ catch (e) {
738
+ dispatchReport('initNavigationMonitor', e);
751
739
  }
752
740
  }
753
- function lookUpChild(t, depth) {
754
- if (t.hasChildNodes() && t.children.length <= 5) {
755
- if (++depth > 5) {
756
- return false;
757
- }
758
- 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);
759
747
  }
760
- return true;
761
- }
762
- function addActionUser(eventType, target) {
763
- const ua = {
764
- '@type': "300",
765
- type: eventType,
766
- start: dateNow(),
767
- name: extractName(target),
768
- nodeName: target.tagName?.toLowerCase(),
769
- sessionId: SessionManager.instance?.currentSessionID()
770
- };
771
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: ua } }));
772
748
  }
773
-
774
- function eventTraceDebugger() {
775
- EventTraceDebugger._instance = new EventTraceDebugger();
749
+ function windowUnloadHandler(event) {
750
+ SessionManager.instance.navigate();
751
+ dispatchShutown();
776
752
  }
777
- class EventTraceDebugger {
778
- constructor() {
779
- this.restRequests = [];
780
- this.httpRequestStages = [];
781
- this.localRequests = [];
782
- this.userActions = [];
783
- this.logEntries = [];
784
- try {
785
- if (ContextManager.instance.techConfig.debugMode || WIN['inspect']) {
786
- window.addEventListener(DISPATCH, this.handleTraces.bind(this));
787
- }
788
- WIN["inspect-setup"] = {
789
- init: () => {
790
- WIN['inspect'] = true;
791
- EventTraceDebugger.init();
792
- },
793
- printConfig: () => console.log(ContextManager.instance.techConfig),
794
- printInstance: () => console.log(ContextManager.instance)
795
- };
796
- }
797
- catch (e) {
798
- console.warn(e);
799
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createReport("Error while setting up debug mode: " + JSON.stringify(e)) } }));
800
- }
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);
801
758
  }
802
- handleTraces(e) {
803
- let trace = e.detail.traces;
804
- if (trace && !e.detail.force) {
805
- switch (trace['@type']) {
806
- case 'main-ses':
807
- if (trace.end) {
808
- console.log(this.prettySessionFormat(SessionManager.instance.currentSession));
809
- }
810
- break;
811
- case 'http-req':
812
- this.restRequests.push(trace);
813
- break;
814
- case 'http-stg':
815
- this.httpRequestStages.push(trace);
816
- break;
817
- case 'user-act':
818
- this.userActions.push(trace);
819
- break;
820
- case 'locl-req':
821
- this.localRequests.push(trace);
822
- break;
823
- case 'log':
824
- trace.sessionId ? this.logEntries.push(trace) : console.warn(this.prettyLogEntryFormat(trace));
825
- break;
826
- case 'rsrc-usg':
827
- console.log(this.prettyMachineResourceUsageFormat(trace));
828
- break;
829
- }
759
+ }
760
+
761
+ function initResourceUsageMonitor() {
762
+ try {
763
+ if ('memory' in performance) {
764
+ addExportListener(ressourceUsageHandler);
830
765
  }
831
766
  }
832
- static init() {
833
- return EventTraceDebugger._instance = new EventTraceDebugger();
767
+ catch (e) {
768
+ dispatchReport('initResourceUsageMonitor', e);
834
769
  }
835
- prettySessionFormat(session) {
836
- let s = `[${session.name}]`;
837
- if (session.user) {
838
- s += `<${session.user}>`;
839
- }
840
- if (session.location) {
841
- s += `(${session.location}) `;
842
- }
843
- //s+= this.prettyDurationFormat(session.start, session.end)+'\n';
844
- s += this.getChildPrint(this.restRequests, this.prettyRestRequestFormat.bind(this));
845
- s += this.getChildPrint(this.localRequests, this.prettyLocalRequestFormat.bind(this));
846
- s += this.getChildPrint(this.userActions, this.prettyActionUserFormat.bind(this));
847
- s += this.getChildPrint(this.logEntries, this.prettyLogEntryFormat.bind(this));
848
- this.resetList();
849
- return s;
850
- }
851
- prettyRestRequestFormat(rest) {
852
- let s = ` - [${rest.method}]`;
853
- if (rest.protocol) {
854
- s += `${rest.protocol}://`;
855
- }
856
- if (rest.host) {
857
- s += rest.host;
858
- }
859
- if (rest.port > 0) {
860
- s += `:${rest.port}`;
861
- }
862
- if (rest.path) {
863
- if (!rest.path.startsWith("/") && !s.endsWith("/")) {
864
- s += '/';
865
- }
866
- s += rest.path;
867
- }
868
- if (rest.query) {
869
- s += rest.query;
870
- }
871
- // s+= ` >> ${rest.status} `
872
- // s+= this.prettyDurationFormat(rest.start,rest.end);
873
- s += '\n';
874
- this.httpRequestStages.filter((s) => s.requestId == rest.id).forEach((stage) => {
875
- 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),
876
779
  });
877
- return s;
878
- }
879
- prettyHttpRequestStageFormat(stage) {
880
- let s = ` - [${stage.name}]`;
881
- s += " >> ";
882
- if (stage.exception?.type) {
883
- s += ` ${stage.exception?.type}:`;
884
- }
885
- if (stage.exception?.message) {
886
- s += ` ${stage.exception.message}`;
887
- }
888
- s += ` ${this.prettyDurationFormat(stage.start, stage.end)}`;
889
- return s;
890
- }
891
- prettyLocalRequestFormat(local) {
892
- let s = ` - [${local.name}]`;
893
- if (local.location) {
894
- s += `(${local.location})`;
895
- }
896
- /*s+= " >> ";
897
- if(local.exception?.type){
898
- s+= ` ${local.exception?.type}:`;
899
- }
900
- if(local.exception?.message){
901
- s+= ` ${local.exception.message}`
902
- }
903
- s+= ` ${this.prettyDurationFormat(local.start, local.end)}`;*/
904
- return s;
905
- }
906
- prettyLogEntryFormat(log) {
907
- let s = ` - [${log.level}]`;
908
- if (log.message) {
909
- s += ` ${log.message}`;
910
- }
911
- s += ` >> ${new Date(log.instant * 1000).toISOString()}`;
912
- return s;
913
- }
914
- prettyMachineResourceUsageFormat(resource) {
915
- return ` - [METRIC] heap :${resource.usedHeap.toFixed(2)}/${resource.commitedHeap.toFixed(2)} >> ${new Date(resource.instant * 1000).toISOString()}`;
916
- }
917
- prettyActionUserFormat(userAction) {
918
- let s = ` - `;
919
- if (userAction.type) {
920
- s += `[${userAction.type}]`;
921
- }
922
- if (userAction.nodeName) {
923
- s += `<${userAction.nodeName}>`;
924
- }
925
- if (userAction.name) {
926
- s += `(${userAction.name}) `;
927
- }
928
- s += ` >> ${new Date(userAction.start * 1000).toISOString()}`;
929
- return s;
930
- }
931
- getChildPrint(o, print) {
932
- let s = "";
933
- if (o.length) {
934
- o = o.filter((r) => {
935
- if (r.sessionId === SessionManager.instance.currentSessionID()) {
936
- s += print(r) + '\n';
937
- return false;
938
- }
939
- return true;
940
- });
941
- const remainingRequests = o.map((r) => print(r)).join('\n');
942
- if (remainingRequests) {
943
- console.warn(remainingRequests);
944
- }
945
- }
946
- return s;
947
- }
948
- prettyDurationFormat(start, end) {
949
- return start && end ? `(in ${(end - start).toFixed(2)} s)` : '';
950
780
  }
951
- resetList() {
952
- this.restRequests = [];
953
- this.httpRequestStages = [];
954
- this.localRequests = [];
955
- this.userActions = [];
781
+ catch (e) {
782
+ dispatchReport('ressourceUsageHandler', e);
956
783
  }
957
784
  }
958
785
 
786
+ const COLLECTOR_CONFIG = new InjectionToken('COLLECTOR_CONFIG');
959
787
  class NgCollectorModule {
960
- static { this.forRootCalled = false; }
961
788
  static forRoot(configuration) {
962
- console.log("forRootCalled");
963
- this.configuration = configuration;
964
- if (configuration?.enabled && !NgCollectorModule.forRootCalled) {
965
- NgCollectorModule.forRootCalled = true;
789
+ const providers = [];
790
+ if (configuration?.enabled) {
966
791
  try {
967
- ContextManager.init(configuration);
968
- return {
969
- ngModule: NgCollectorModule,
970
- providers: [
971
- //provideAppInitializer(initializeEvents),
972
- { provide: APP_INITIALIZER, useFactory: initializeEvents, deps: [Router], multi: true },
973
- { provide: HTTP_INTERCEPTORS, useClass: HttpInterceptorService, multi: true },
974
- { provide: ErrorHandler, useClass: GlobalErrorHandlerService }
975
- ]
976
- };
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 });
977
794
  }
978
795
  catch (e) {
979
796
  console.warn(`invalid Configuration, Ng-collector is disabled because of this ${e.message}`);
980
797
  }
981
798
  }
982
- return {
983
- ngModule: NgCollectorModule
984
- };
799
+ return { ngModule: NgCollectorModule, providers: providers };
985
800
  }
986
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgCollectorModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
987
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: NgCollectorModule }); }
988
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.17", 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 }); }
989
804
  }
990
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgCollectorModule, decorators: [{
805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: NgCollectorModule, decorators: [{
991
806
  type: NgModule
992
807
  }] });
993
- function initializeEvents(router) {
808
+ function initializeEventsFactory(config, router) {
994
809
  return () => {
995
- ContextManager.init(NgCollectorModule.configuration);
996
- eventTraceScheduledDispatcher();
997
- eventTraceDebugger();
998
- analyticsEventsListener();
810
+ if (config.tech.analytics) {
811
+ initUserActionMonitor();
812
+ }
813
+ if (config.tech.resources) {
814
+ initResourceUsageMonitor();
815
+ }
816
+ initNavigationMonitor(router);
999
817
  //storageEventListener();
1000
- beforeDispatchListener();
1001
- beforeUnloadListener();
1002
- routerEventsListener(router);
1003
- bfCacheListener();
818
+ //eventTraceDebugger
819
+ addReloadListener(e => {
820
+ reloadCollector(config);
821
+ });
1004
822
  };
1005
823
  }
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
+ }
866
+ }
1006
867
 
1007
868
  function TraceableStage() {
1008
869
  return function (target, propertyKey, descriptor) {
1009
870
  const originalMethod = descriptor.value;
1010
871
  descriptor.value = function (...args) {
872
+ const monitor = new LocalRequestMonitor();
1011
873
  let exception;
1012
- let start, end;
1013
- start = Date.now();
1014
874
  try {
875
+ monitor.preProcess(propertyKey, target);
1015
876
  return originalMethod.apply(this, args);
1016
877
  }
1017
878
  catch (e) {
1018
- let type = null, message = null;
1019
- if (e) {
1020
- if (typeof e === "string") {
1021
- message = e;
1022
- }
1023
- else if (e instanceof Error) {
1024
- type = e.name;
1025
- message = e.message;
1026
- }
1027
- else {
1028
- message = JSON.stringify(e);
1029
- }
1030
- }
1031
- exception = {
1032
- type: type,
1033
- message: message
1034
- };
879
+ exception = resolveException(e);
1035
880
  throw e;
1036
881
  }
1037
882
  finally {
1038
- end = Date.now();
1039
- let id = crypto.randomUUID();
1040
- if (WIN["inspect-session-manager"]?.currentSessionID() != null) {
1041
- let doUpdateMask = WIN["inspect-session-manager"]?.updateMask(RequestMask.LOCAL);
1042
- doUpdateMask && window.dispatchEvent(new CustomEvent(DISPATCH, { detail: {
1043
- traces: {
1044
- "@type": "03",
1045
- id: WIN["inspect-session-manager"]?.currentSessionID(),
1046
- main: true,
1047
- mask: WIN["inspect-session-manager"]?.getCurrentSessionCallBack((s) => s.requestMask)
1048
- }
1049
- }
1050
- }));
1051
- }
1052
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: {
1053
- "@type": "110",
1054
- id: id,
1055
- name: propertyKey,
1056
- location: target.constructor.name,
1057
- user: WIN["inspect-session-manager"]?.currentSession?.user,
1058
- start: start,
1059
- sessionId: WIN["inspect-session-manager"]?.currentSession?.id
1060
- }
1061
- }
1062
- }));
1063
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: {
1064
- "@type": "111",
1065
- id: id,
1066
- exception: exception,
1067
- end: end
1068
- }
1069
- }
1070
- }));
883
+ monitor.postProcess(exception);
1071
884
  }
1072
885
  };
1073
886
  return descriptor;
1074
887
  };
1075
888
  }
1076
-
1077
- class LogService {
1078
- static info(message) {
1079
- if (message) {
1080
- 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;
1081
894
  }
1082
- }
1083
- static warn(message) {
1084
- if (message) {
1085
- 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;
1086
898
  }
1087
- }
1088
- static error(message) {
1089
- if (message) {
1090
- window.dispatchEvent(new CustomEvent(DISPATCH, { detail: { traces: createLogEntry("ERROR", message, SessionManager.instance?.currentSessionID()) } }));
899
+ else {
900
+ message = JSON.stringify(e);
1091
901
  }
1092
902
  }
903
+ return { type, message };
1093
904
  }
1094
905
 
1095
906
  /*
@@ -1100,5 +911,5 @@ class LogService {
1100
911
  * Generated bundle index. Do not edit.
1101
912
  */
1102
913
 
1103
- export { LogService, NgCollectorModule, TraceableStage, initializeEvents };
914
+ export { NgCollectorModule, TraceableStage, initializeCollector, initializeEventsFactory, reloadCollector, resolveException, sessionlogger };
1104
915
  //# sourceMappingURL=oneteme-inspect-ng-collector.mjs.map