@syntropysoft/syntropyfront 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,33 +3,33 @@
3
3
  * Responsabilidad única: Almacenar y gestionar breadcrumbs
4
4
  */
5
5
  class BreadcrumbManager {
6
- constructor() {
7
- this.breadcrumbs = [];
8
- }
9
-
10
- add(category, message, data = {}) {
11
- const breadcrumb = {
12
- category,
13
- message,
14
- data,
15
- timestamp: new Date().toISOString()
16
- };
6
+ constructor() {
7
+ this.breadcrumbs = [];
8
+ }
9
+
10
+ add(category, message, data = {}) {
11
+ const breadcrumb = {
12
+ category,
13
+ message,
14
+ data,
15
+ timestamp: new Date().toISOString()
16
+ };
17
17
 
18
- this.breadcrumbs.push(breadcrumb);
19
- return breadcrumb;
20
- }
18
+ this.breadcrumbs.push(breadcrumb);
19
+ return breadcrumb;
20
+ }
21
21
 
22
- getAll() {
23
- return this.breadcrumbs;
24
- }
22
+ getAll() {
23
+ return this.breadcrumbs;
24
+ }
25
25
 
26
- clear() {
27
- this.breadcrumbs = [];
28
- }
26
+ clear() {
27
+ this.breadcrumbs = [];
28
+ }
29
29
 
30
- getCount() {
31
- return this.breadcrumbs.length;
32
- }
30
+ getCount() {
31
+ return this.breadcrumbs.length;
32
+ }
33
33
  }
34
34
 
35
35
  /**
@@ -37,33 +37,33 @@ class BreadcrumbManager {
37
37
  * Responsabilidad única: Formatear y gestionar errores
38
38
  */
39
39
  class ErrorManager {
40
- constructor() {
41
- this.errors = [];
42
- }
43
-
44
- send(error, context = {}) {
45
- const errorData = {
46
- message: error.message,
47
- stack: error.stack,
48
- context,
49
- timestamp: new Date().toISOString()
50
- };
40
+ constructor() {
41
+ this.errors = [];
42
+ }
43
+
44
+ send(error, context = {}) {
45
+ const errorData = {
46
+ message: error.message,
47
+ stack: error.stack,
48
+ context,
49
+ timestamp: new Date().toISOString()
50
+ };
51
51
 
52
- this.errors.push(errorData);
53
- return errorData;
54
- }
52
+ this.errors.push(errorData);
53
+ return errorData;
54
+ }
55
55
 
56
- getAll() {
57
- return this.errors;
58
- }
56
+ getAll() {
57
+ return this.errors;
58
+ }
59
59
 
60
- clear() {
61
- this.errors = [];
62
- }
60
+ clear() {
61
+ this.errors = [];
62
+ }
63
63
 
64
- getCount() {
65
- return this.errors.length;
66
- }
64
+ getCount() {
65
+ return this.errors.length;
66
+ }
67
67
  }
68
68
 
69
69
  /**
@@ -71,85 +71,97 @@ class ErrorManager {
71
71
  * Responsabilidad única: Mostrar mensajes solo cuando hay errores
72
72
  */
73
73
  class Logger {
74
- constructor() {
75
- this.isSilent = true; // Por defecto silente
76
- }
74
+ constructor() {
75
+ this.isSilent = true; // Por defecto silente
76
+ }
77
77
 
78
- log(message, data = null) {
79
- // No loggear nada en modo silente
80
- if (this.isSilent) return;
78
+ log(message, data = null) {
79
+ // No loggear nada en modo silente
80
+ if (this.isSilent) return;
81
81
 
82
- if (data) {
83
- console.log(message, data);
84
- } else {
85
- console.log(message);
86
- }
87
- }
88
-
89
- error(message, data = null) {
90
- // SIEMPRE loggear errores
91
- if (data) {
92
- console.error(message, data);
93
- } else {
94
- console.error(message);
95
- }
96
- }
97
-
98
- warn(message, data = null) {
99
- // Solo warnings importantes
100
- if (data) {
101
- console.warn(message, data);
102
- } else {
103
- console.warn(message);
104
- }
105
- }
106
-
107
- // Método para activar logging (solo para debug)
108
- enableLogging() {
109
- this.isSilent = false;
110
- }
111
-
112
- // Método para desactivar logging
113
- disableLogging() {
114
- this.isSilent = true;
115
- }
82
+ if (data) {
83
+ console.log(message, data);
84
+ } else {
85
+ console.log(message);
86
+ }
87
+ }
88
+
89
+ error(message, data = null) {
90
+ // SIEMPRE loggear errores (ignora modo silencioso)
91
+ if (data) {
92
+ console.error(message, data);
93
+ } else {
94
+ console.error(message);
95
+ }
96
+ }
97
+
98
+ warn(message, data = null) {
99
+ // Solo warnings importantes
100
+ if (data) {
101
+ console.warn(message, data);
102
+ } else {
103
+ console.warn(message);
104
+ }
105
+ }
106
+
107
+ // Método para activar logging (solo para debug)
108
+ enableLogging() {
109
+ this.isSilent = false;
110
+ }
111
+
112
+ // Método para desactivar logging
113
+ disableLogging() {
114
+ this.isSilent = true;
115
+ }
116
116
  }
117
117
 
118
118
  /**
119
- * SyntropyFront - Observability library with automatic capture
120
- * Single responsibility: Automatically capture events and send errors with context
119
+ * Copyright 2024 Syntropysoft
120
+ *
121
+ * Licensed under the Apache License, Version 2.0 (the "License");
122
+ * you may not use this file except in compliance with the License.
123
+ * You may obtain a copy of the License at
124
+ *
125
+ * http://www.apache.org/licenses/LICENSE-2.0
126
+ *
127
+ * Unless required by applicable law or agreed to in writing, software
128
+ * distributed under the License is distributed on an "AS IS" BASIS,
129
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130
+ * See the License for the specific language governing permissions and
131
+ * limitations under the License.
121
132
  */
122
133
 
134
+
123
135
  class SyntropyFront {
124
- constructor() {
125
- // Basic managers
126
- this.breadcrumbManager = new BreadcrumbManager();
127
- this.errorManager = new ErrorManager();
128
- this.logger = new Logger();
136
+ constructor() {
137
+ // Basic managers
138
+ this.breadcrumbManager = new BreadcrumbManager();
139
+ this.errorManager = new ErrorManager();
140
+ this.logger = new Logger();
129
141
 
130
- // Default configuration
131
- this.maxEvents = 50;
132
- this.fetchConfig = null; // Complete fetch configuration
133
- this.onErrorCallback = null; // User-defined error handler
134
- this.isActive = false;
142
+ // Default configuration
143
+ this.maxEvents = 50;
144
+ this.fetchConfig = null; // Complete fetch configuration
145
+ this.onErrorCallback = null; // User-defined error handler
146
+ this.isActive = false;
135
147
 
136
- // Automatic capture
137
- this.originalHandlers = {};
148
+ // Automatic capture
149
+ this.originalHandlers = {};
138
150
 
139
- // Auto-initialize
140
- this.init();
141
- }
151
+ // Auto-initialize
152
+ this.init();
153
+ }
142
154
 
143
- init() {
144
- this.isActive = true;
155
+ init() {
156
+ this.isActive = true;
145
157
 
146
- // Configure automatic capture immediately
147
- this.setupAutomaticCapture();
158
+ // Configure automatic capture immediately
159
+ this.setupAutomaticCapture();
148
160
 
149
- console.log('🚀 SyntropyFront: Initialized with automatic capture');
150
- }
161
+ console.log('🚀 SyntropyFront: Initialized with automatic capture');
162
+ }
151
163
 
152
- /**
164
+ /**
153
165
  * Configure SyntropyFront
154
166
  * @param {Object} config - Configuration
155
167
  * @param {number} config.maxEvents - Maximum number of events to store
@@ -158,266 +170,266 @@ class SyntropyFront {
158
170
  * @param {Object} config.fetch.options - Fetch options (headers, method, etc.)
159
171
  * @param {Function} config.onError - User-defined error handler callback
160
172
  */
161
- configure(config = {}) {
162
- this.maxEvents = config.maxEvents || this.maxEvents;
163
- this.fetchConfig = config.fetch;
164
- this.onErrorCallback = config.onError;
173
+ configure(config = {}) {
174
+ this.maxEvents = config.maxEvents || this.maxEvents;
175
+ this.fetchConfig = config.fetch;
176
+ this.onErrorCallback = config.onError;
165
177
 
166
- if (this.onErrorCallback) {
167
- console.log(`✅ SyntropyFront: Configured - maxEvents: ${this.maxEvents}, custom error handler`);
168
- } else if (this.fetchConfig) {
169
- console.log(`✅ SyntropyFront: Configured - maxEvents: ${this.maxEvents}, endpoint: ${this.fetchConfig.url}`);
170
- } else {
171
- console.log(`✅ SyntropyFront: Configured - maxEvents: ${this.maxEvents}, console only`);
172
- }
178
+ if (this.onErrorCallback) {
179
+ console.log(`✅ SyntropyFront: Configured - maxEvents: ${this.maxEvents}, custom error handler`);
180
+ } else if (this.fetchConfig) {
181
+ console.log(`✅ SyntropyFront: Configured - maxEvents: ${this.maxEvents}, endpoint: ${this.fetchConfig.url}`);
182
+ } else {
183
+ console.log(`✅ SyntropyFront: Configured - maxEvents: ${this.maxEvents}, console only`);
173
184
  }
185
+ }
174
186
 
175
- /**
187
+ /**
176
188
  * Configure automatic event capture
177
189
  */
178
- setupAutomaticCapture() {
179
- if (typeof window === 'undefined') return;
190
+ setupAutomaticCapture() {
191
+ if (typeof window === 'undefined') return;
180
192
 
181
- // Capture clicks
182
- this.setupClickCapture();
193
+ // Capture clicks
194
+ this.setupClickCapture();
183
195
 
184
- // Capture errors
185
- this.setupErrorCapture();
196
+ // Capture errors
197
+ this.setupErrorCapture();
186
198
 
187
- // Capture HTTP calls
188
- this.setupHttpCapture();
199
+ // Capture HTTP calls
200
+ this.setupHttpCapture();
189
201
 
190
- // Capture console logs
191
- this.setupConsoleCapture();
192
- }
202
+ // Capture console logs
203
+ this.setupConsoleCapture();
204
+ }
193
205
 
194
- /**
206
+ /**
195
207
  * Capture user clicks
196
208
  */
197
- setupClickCapture() {
198
- const clickHandler = (event) => {
199
- const element = event.target;
200
- this.addBreadcrumb('user', 'click', {
201
- element: element.tagName,
202
- id: element.id,
203
- className: element.className,
204
- x: event.clientX,
205
- y: event.clientY
206
- });
207
- };
208
-
209
- document.addEventListener('click', clickHandler);
210
- }
211
-
212
- /**
209
+ setupClickCapture() {
210
+ const clickHandler = (event) => {
211
+ const element = event.target;
212
+ this.addBreadcrumb('user', 'click', {
213
+ element: element.tagName,
214
+ id: element.id,
215
+ className: element.className,
216
+ x: event.clientX,
217
+ y: event.clientY
218
+ });
219
+ };
220
+
221
+ document.addEventListener('click', clickHandler);
222
+ }
223
+
224
+ /**
213
225
  * Automatically capture errors
214
226
  */
215
- setupErrorCapture() {
216
- // Save original handlers
217
- this.originalHandlers.onerror = window.onerror;
218
- this.originalHandlers.onunhandledrejection = window.onunhandledrejection;
219
-
220
- // Intercept errors
221
- window.onerror = (message, source, lineno, colno, error) => {
222
- const errorPayload = {
223
- type: 'uncaught_exception',
224
- error: { message, source, lineno, colno, stack: error?.stack },
225
- breadcrumbs: this.getBreadcrumbs(),
226
- timestamp: new Date().toISOString()
227
- };
228
-
229
- this.handleError(errorPayload);
227
+ setupErrorCapture() {
228
+ // Save original handlers
229
+ this.originalHandlers.onerror = window.onerror;
230
+ this.originalHandlers.onunhandledrejection = window.onunhandledrejection;
231
+
232
+ // Intercept errors
233
+ window.onerror = (message, source, lineno, colno, error) => {
234
+ const errorPayload = {
235
+ type: 'uncaught_exception',
236
+ error: { message, source, lineno, colno, stack: error?.stack },
237
+ breadcrumbs: this.getBreadcrumbs(),
238
+ timestamp: new Date().toISOString()
239
+ };
240
+
241
+ this.handleError(errorPayload);
230
242
 
231
- // Call original handler
232
- if (this.originalHandlers.onerror) {
233
- return this.originalHandlers.onerror(message, source, lineno, colno, error);
234
- }
243
+ // Call original handler
244
+ if (this.originalHandlers.onerror) {
245
+ return this.originalHandlers.onerror(message, source, lineno, colno, error);
246
+ }
235
247
 
236
- return false;
237
- };
238
-
239
- // Intercept rejected promises
240
- window.onunhandledrejection = (event) => {
241
- const errorPayload = {
242
- type: 'unhandled_rejection',
243
- error: {
244
- message: event.reason?.message || 'Promise rejection without message',
245
- stack: event.reason?.stack,
246
- },
247
- breadcrumbs: this.getBreadcrumbs(),
248
- timestamp: new Date().toISOString()
249
- };
250
-
251
- this.handleError(errorPayload);
248
+ return false;
249
+ };
250
+
251
+ // Intercept rejected promises
252
+ window.onunhandledrejection = (event) => {
253
+ const errorPayload = {
254
+ type: 'unhandled_rejection',
255
+ error: {
256
+ message: event.reason?.message || 'Promise rejection without message',
257
+ stack: event.reason?.stack,
258
+ },
259
+ breadcrumbs: this.getBreadcrumbs(),
260
+ timestamp: new Date().toISOString()
261
+ };
262
+
263
+ this.handleError(errorPayload);
252
264
 
253
- // Call original handler
254
- if (this.originalHandlers.onunhandledrejection) {
255
- this.originalHandlers.onunhandledrejection(event);
256
- }
257
- };
258
- }
259
-
260
- /**
265
+ // Call original handler
266
+ if (this.originalHandlers.onunhandledrejection) {
267
+ this.originalHandlers.onunhandledrejection(event);
268
+ }
269
+ };
270
+ }
271
+
272
+ /**
261
273
  * Capture HTTP calls
262
274
  */
263
- setupHttpCapture() {
264
- // Intercept fetch
265
- const originalFetch = window.fetch;
266
- window.fetch = (...args) => {
267
- const [url, options] = args;
275
+ setupHttpCapture() {
276
+ // Intercept fetch
277
+ const originalFetch = window.fetch;
278
+ window.fetch = (...args) => {
279
+ const [url, options] = args;
268
280
 
269
- this.addBreadcrumb('http', 'fetch', {
270
- url,
271
- method: options?.method || 'GET'
272
- });
273
-
274
- return originalFetch(...args).then(response => {
275
- this.addBreadcrumb('http', 'fetch_response', {
276
- url,
277
- status: response.status
278
- });
279
- return response;
280
- }).catch(error => {
281
- this.addBreadcrumb('http', 'fetch_error', {
282
- url,
283
- error: error.message
284
- });
285
- throw error;
286
- });
287
- };
288
- }
281
+ this.addBreadcrumb('http', 'fetch', {
282
+ url,
283
+ method: options?.method || 'GET'
284
+ });
285
+
286
+ return originalFetch(...args).then(response => {
287
+ this.addBreadcrumb('http', 'fetch_response', {
288
+ url,
289
+ status: response.status
290
+ });
291
+ return response;
292
+ }).catch(error => {
293
+ this.addBreadcrumb('http', 'fetch_error', {
294
+ url,
295
+ error: error.message
296
+ });
297
+ throw error;
298
+ });
299
+ };
300
+ }
289
301
 
290
- /**
302
+ /**
291
303
  * Capture console logs
292
304
  */
293
- setupConsoleCapture() {
294
- const originalLog = console.log;
295
- const originalError = console.error;
296
- const originalWarn = console.warn;
297
-
298
- console.log = (...args) => {
299
- this.addBreadcrumb('console', 'log', { message: args.join(' ') });
300
- originalLog.apply(console, args);
301
- };
302
-
303
- console.error = (...args) => {
304
- this.addBreadcrumb('console', 'error', { message: args.join(' ') });
305
- originalError.apply(console, args);
306
- };
307
-
308
- console.warn = (...args) => {
309
- this.addBreadcrumb('console', 'warn', { message: args.join(' ') });
310
- originalWarn.apply(console, args);
311
- };
312
- }
313
-
314
- /**
305
+ setupConsoleCapture() {
306
+ const originalLog = console.log;
307
+ const originalError = console.error;
308
+ const originalWarn = console.warn;
309
+
310
+ console.log = (...args) => {
311
+ this.addBreadcrumb('console', 'log', { message: args.join(' ') });
312
+ originalLog.apply(console, args);
313
+ };
314
+
315
+ console.error = (...args) => {
316
+ this.addBreadcrumb('console', 'error', { message: args.join(' ') });
317
+ originalError.apply(console, args);
318
+ };
319
+
320
+ console.warn = (...args) => {
321
+ this.addBreadcrumb('console', 'warn', { message: args.join(' ') });
322
+ originalWarn.apply(console, args);
323
+ };
324
+ }
325
+
326
+ /**
315
327
  * Handle errors - priority: onError callback > fetch > console
316
328
  */
317
- handleError(errorPayload) {
318
- // Default log
319
- this.logger.error('❌ Error:', errorPayload);
320
-
321
- // Priority 1: User-defined callback (maximum flexibility)
322
- if (this.onErrorCallback) {
323
- try {
324
- this.onErrorCallback(errorPayload);
325
- } catch (callbackError) {
326
- console.warn('SyntropyFront: Error in user callback:', callbackError);
327
- }
328
- return;
329
- }
329
+ handleError(errorPayload) {
330
+ // Default log
331
+ this.logger.error('❌ Error:', errorPayload);
330
332
 
331
- // Priority 2: Fetch to endpoint
332
- if (this.fetchConfig) {
333
- this.postToEndpoint(errorPayload);
334
- return;
335
- }
333
+ // Priority 1: User-defined callback (maximum flexibility)
334
+ if (this.onErrorCallback) {
335
+ try {
336
+ this.onErrorCallback(errorPayload);
337
+ } catch (callbackError) {
338
+ console.warn('SyntropyFront: Error in user callback:', callbackError);
339
+ }
340
+ return;
341
+ }
336
342
 
337
- // Priority 3: Console only (default)
338
- // Already logged above
343
+ // Priority 2: Fetch to endpoint
344
+ if (this.fetchConfig) {
345
+ this.postToEndpoint(errorPayload);
346
+ return;
339
347
  }
348
+
349
+ // Priority 3: Console only (default)
350
+ // Already logged above
351
+ }
340
352
 
341
- /**
353
+ /**
342
354
  * Post error object using fetch configuration
343
355
  */
344
- postToEndpoint(errorPayload) {
345
- const { url, options = {} } = this.fetchConfig;
346
-
347
- // Default configuration
348
- const defaultOptions = {
349
- method: 'POST',
350
- headers: {
351
- 'Content-Type': 'application/json',
352
- ...options.headers
353
- },
354
- body: JSON.stringify(errorPayload),
355
- ...options
356
- };
357
-
358
- fetch(url, defaultOptions).catch(error => {
359
- console.warn('SyntropyFront: Error posting to endpoint:', error);
360
- });
361
- }
362
-
363
- // Public API
364
- addBreadcrumb(category, message, data = {}) {
365
- if (!this.isActive) return;
356
+ postToEndpoint(errorPayload) {
357
+ const { url, options = {} } = this.fetchConfig;
366
358
 
367
- const breadcrumb = this.breadcrumbManager.add(category, message, data);
359
+ // Default configuration
360
+ const defaultOptions = {
361
+ method: 'POST',
362
+ headers: {
363
+ 'Content-Type': 'application/json',
364
+ ...options.headers
365
+ },
366
+ body: JSON.stringify(errorPayload),
367
+ ...options
368
+ };
369
+
370
+ fetch(url, defaultOptions).catch(error => {
371
+ console.warn('SyntropyFront: Error posting to endpoint:', error);
372
+ });
373
+ }
374
+
375
+ // Public API
376
+ addBreadcrumb(category, message, data = {}) {
377
+ if (!this.isActive) return;
368
378
 
369
- // Keep only the last maxEvents
370
- const breadcrumbs = this.breadcrumbManager.getAll();
371
- if (breadcrumbs.length > this.maxEvents) {
372
- this.breadcrumbManager.clear();
373
- breadcrumbs.slice(-this.maxEvents).forEach(b => this.breadcrumbManager.add(b.category, b.message, b.data));
374
- }
379
+ const breadcrumb = this.breadcrumbManager.add(category, message, data);
375
380
 
376
- return breadcrumb;
381
+ // Keep only the last maxEvents
382
+ const breadcrumbs = this.breadcrumbManager.getAll();
383
+ if (breadcrumbs.length > this.maxEvents) {
384
+ this.breadcrumbManager.clear();
385
+ breadcrumbs.slice(-this.maxEvents).forEach(b => this.breadcrumbManager.add(b.category, b.message, b.data));
377
386
  }
387
+
388
+ return breadcrumb;
389
+ }
378
390
 
379
- getBreadcrumbs() {
380
- return this.breadcrumbManager.getAll();
381
- }
391
+ getBreadcrumbs() {
392
+ return this.breadcrumbManager.getAll();
393
+ }
382
394
 
383
- clearBreadcrumbs() {
384
- this.breadcrumbManager.clear();
385
- }
395
+ clearBreadcrumbs() {
396
+ this.breadcrumbManager.clear();
397
+ }
386
398
 
387
- sendError(error, context = {}) {
388
- if (!this.isActive) return;
399
+ sendError(error, context = {}) {
400
+ if (!this.isActive) return;
389
401
 
390
- const errorData = this.errorManager.send(error, context);
391
- const errorPayload = {
392
- ...errorData,
393
- breadcrumbs: this.getBreadcrumbs(),
394
- timestamp: new Date().toISOString()
395
- };
402
+ const errorData = this.errorManager.send(error, context);
403
+ const errorPayload = {
404
+ ...errorData,
405
+ breadcrumbs: this.getBreadcrumbs(),
406
+ timestamp: new Date().toISOString()
407
+ };
396
408
 
397
- this.handleError(errorPayload);
398
- return errorData;
399
- }
400
-
401
- getErrors() {
402
- return this.errorManager.getAll();
403
- }
404
-
405
- clearErrors() {
406
- this.errorManager.clear();
407
- }
408
-
409
- // Utility methods
410
- getStats() {
411
- return {
412
- breadcrumbs: this.breadcrumbManager.getCount(),
413
- errors: this.errorManager.getCount(),
414
- isActive: this.isActive,
415
- maxEvents: this.maxEvents,
416
- hasFetchConfig: !!this.fetchConfig,
417
- hasErrorCallback: !!this.onErrorCallback,
418
- endpoint: this.fetchConfig?.url || 'console'
419
- };
420
- }
409
+ this.handleError(errorPayload);
410
+ return errorData;
411
+ }
412
+
413
+ getErrors() {
414
+ return this.errorManager.getAll();
415
+ }
416
+
417
+ clearErrors() {
418
+ this.errorManager.clear();
419
+ }
420
+
421
+ // Utility methods
422
+ getStats() {
423
+ return {
424
+ breadcrumbs: this.breadcrumbManager.getCount(),
425
+ errors: this.errorManager.getCount(),
426
+ isActive: this.isActive,
427
+ maxEvents: this.maxEvents,
428
+ hasFetchConfig: !!this.fetchConfig,
429
+ hasErrorCallback: !!this.onErrorCallback,
430
+ endpoint: this.fetchConfig?.url || 'console'
431
+ };
432
+ }
421
433
  }
422
434
 
423
435
  // Single instance - auto-initializes