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