@factor_ec/utils 5.0.9 → 6.0.0

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,10 +1,33 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
2
+ import { Injectable, inject, PLATFORM_ID, InjectionToken, signal, computed } from '@angular/core';
3
3
  import { isPlatformBrowser } from '@angular/common';
4
- import * as i1 from '@angular/router';
5
- import { NavigationEnd } from '@angular/router';
4
+ import { Router, NavigationEnd } from '@angular/router';
5
+ import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
6
+ import { MatDialog } from '@angular/material/dialog';
7
+ import { BehaviorSubject, switchMap, throwError, catchError, share, finalize, filter, take, lastValueFrom, tap } from 'rxjs';
6
8
 
7
- class ArrayService {
9
+ /**
10
+ * Utility service for array operations.
11
+ *
12
+ * @remarks
13
+ * Provides helper methods for common array manipulation tasks.
14
+ */
15
+ class ArrayUtil {
16
+ /**
17
+ * Merges multiple arrays into a single array, combining objects with the same property value.
18
+ *
19
+ * @param arrays - Array of arrays to merge
20
+ * @param prop - Property name to use as the key for merging objects
21
+ * @returns Merged array with combined objects
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const arr1 = [{ id: 1, name: 'John' }];
26
+ * const arr2 = [{ id: 1, age: 30 }];
27
+ * const result = arrayUtil.merge([arr1, arr2], 'id');
28
+ * // Returns: [{ id: 1, name: 'John', age: 30 }]
29
+ * ```
30
+ */
8
31
  merge(arrays, prop) {
9
32
  const merged = {};
10
33
  arrays.forEach(arr => {
@@ -14,17 +37,31 @@ class ArrayService {
14
37
  });
15
38
  return Object.values(merged);
16
39
  }
17
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ArrayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
18
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ArrayService, providedIn: 'root' });
40
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ArrayUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
41
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ArrayUtil, providedIn: 'root' });
19
42
  }
20
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ArrayService, decorators: [{
43
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ArrayUtil, decorators: [{
21
44
  type: Injectable,
22
45
  args: [{
23
- providedIn: 'root'
46
+ providedIn: 'root',
24
47
  }]
25
48
  }] });
26
49
 
27
- class ColorService {
50
+ /**
51
+ * Utility service for color operations and hash-based color generation.
52
+ *
53
+ * @remarks
54
+ * Provides methods to generate consistent colors from strings using hash algorithms,
55
+ * and convert between different color formats (HSL, RGB, HEX).
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const colorUtil = inject(ColorUtil);
60
+ * const hexColor = colorUtil.hex('username');
61
+ * // Returns: '#a1b2c3' (consistent color for the same string)
62
+ * ```
63
+ */
64
+ class ColorUtil {
28
65
  L;
29
66
  S;
30
67
  hueRanges;
@@ -53,9 +90,13 @@ class ColorService {
53
90
  });
54
91
  }
55
92
  /**
56
- * BKDR Hash (modified version)
93
+ * Generates a hash value from a string using BKDR hash algorithm (modified version).
94
+ *
95
+ * @param str - The string to hash
96
+ * @returns A numeric hash value
57
97
  *
58
- * @param str string to hash
98
+ * @remarks
99
+ * This is a modified BKDR hash algorithm optimized for short strings.
59
100
  */
60
101
  hash(str) {
61
102
  let seed = 131;
@@ -74,10 +115,10 @@ class ColorService {
74
115
  return hash;
75
116
  }
76
117
  /**
77
- * Convert RGB Array to HEX
118
+ * Converts an RGB array to a hexadecimal color string.
78
119
  *
79
- * @param RGBArray - [R, G, B]
80
- * @returns 6 digits hex starting with #
120
+ * @param RGBArray - Array with [R, G, B] values (0-255)
121
+ * @returns Hexadecimal color string starting with # (e.g., '#a1b2c3')
81
122
  */
82
123
  rgb2hex(RGBArray) {
83
124
  let hex = '#';
@@ -90,13 +131,14 @@ class ColorService {
90
131
  return hex;
91
132
  }
92
133
  /**
93
- * Convert HSL to RGB
134
+ * Converts HSL color values to RGB array.
135
+ *
136
+ * @param H - Hue value in range [0, 360)
137
+ * @param S - Saturation value in range [0, 1]
138
+ * @param L - Lightness value in range [0, 1]
139
+ * @returns Array with [R, G, B] values in range [0, 255]
94
140
  *
95
141
  * @see {@link http://zh.wikipedia.org/wiki/HSL和HSV色彩空间} for further information.
96
- * @param H Hue ∈ [0, 360)
97
- * @param S Saturation ∈ [0, 1]
98
- * @param L Lightness ∈ [0, 1]
99
- * @returns R, G, B ∈ [0, 255]
100
142
  */
101
143
  hsl2rgb(H, S, L) {
102
144
  H /= 360;
@@ -125,11 +167,10 @@ class ColorService {
125
167
  });
126
168
  }
127
169
  /**
128
- * Returns the hash in [h, s, l].
129
- * Note that H ∈ [0, 360); S ∈ [0, 1]; L ∈ [0, 1];
170
+ * Generates HSL color values from a string hash.
130
171
  *
131
- * @param str string to hash
132
- * @returns [h, s, l]
172
+ * @param str - The string to generate color from
173
+ * @returns Array with [H, S, L] values where H ∈ [0, 360), S ∈ [0, 1], L ∈ [0, 1]
133
174
  */
134
175
  hsl(str) {
135
176
  let H;
@@ -155,86 +196,135 @@ class ColorService {
155
196
  return [H, S, L];
156
197
  }
157
198
  /**
158
- * Returns the hash in [r, g, b].
159
- * Note that R, G, B ∈ [0, 255]
199
+ * Generates RGB color values from a string hash.
160
200
  *
161
- * @param str string to hash
162
- * @returns [r, g, b]
201
+ * @param str - The string to generate color from
202
+ * @returns Array with [R, G, B] values in range [0, 255]
163
203
  */
164
204
  rgb(str) {
165
205
  let hsl = this.hsl(str);
166
206
  return this.hsl2rgb(hsl[0], hsl[1], hsl[2]);
167
207
  }
168
208
  /**
169
- * Returns the hash in hex
209
+ * Generates a hexadecimal color string from a string hash.
210
+ *
211
+ * @param str - The string to generate color from
212
+ * @returns Hexadecimal color string starting with # (e.g., '#a1b2c3')
170
213
  *
171
- * @param str string to hash
172
- * @returns hex with #
214
+ * @example
215
+ * ```typescript
216
+ * const color = colorUtil.hex('username');
217
+ * // Returns: '#a1b2c3' (same string always returns same color)
218
+ * ```
173
219
  */
174
220
  hex(str) {
175
221
  let rgb = this.rgb(str);
176
222
  return this.rgb2hex(rgb);
177
223
  }
178
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ColorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
179
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ColorService, providedIn: 'root' });
224
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ColorUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
225
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ColorUtil, providedIn: 'root' });
180
226
  }
181
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ColorService, decorators: [{
227
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ColorUtil, decorators: [{
182
228
  type: Injectable,
183
229
  args: [{
184
230
  providedIn: 'root',
185
231
  }]
186
232
  }], ctorParameters: () => [] });
187
233
 
188
- class CsvConfigConsts {
189
- static EOL = '\r\n';
190
- static BOM = '\ufeff';
191
- static DEFAULT_FIELD_SEPARATOR = ',';
192
- static DEFAULT_DECIMAL_SEPARATOR = '.';
193
- static DEFAULT_QUOTE = '"';
194
- static DEFAULT_SHOW_TITLE = false;
195
- static DEFAULT_TITLE = 'My Generated Report';
196
- static DEFAULT_FILENAME = 'generated';
197
- static DEFAULT_SHOW_LABELS = false;
198
- static DEFAULT_USE_TEXT_FILE = false;
199
- static DEFAULT_USE_BOM = true;
200
- static DEFAULT_HEADER = [];
201
- static DEFAULT_KEYS_AS_HEADERS = false;
202
- }
203
- const ConfigDefaults = {
204
- filename: CsvConfigConsts.DEFAULT_FILENAME,
205
- fieldSeparator: CsvConfigConsts.DEFAULT_FIELD_SEPARATOR,
206
- quoteStrings: CsvConfigConsts.DEFAULT_QUOTE,
207
- decimalSeparator: CsvConfigConsts.DEFAULT_DECIMAL_SEPARATOR,
208
- showLabels: CsvConfigConsts.DEFAULT_SHOW_LABELS,
209
- showTitle: CsvConfigConsts.DEFAULT_SHOW_TITLE,
210
- title: CsvConfigConsts.DEFAULT_TITLE,
211
- useTextFile: CsvConfigConsts.DEFAULT_USE_TEXT_FILE,
212
- useBom: CsvConfigConsts.DEFAULT_USE_BOM,
213
- headers: CsvConfigConsts.DEFAULT_HEADER,
214
- useKeysAsHeaders: CsvConfigConsts.DEFAULT_KEYS_AS_HEADERS
234
+ /**
235
+ * Default configuration constants for CSV export operations.
236
+ */
237
+ const CSV_CONFIG = {
238
+ /** End of line character for CSV */
239
+ EOL: '\r\n',
240
+ /** Byte Order Mark for UTF-8 encoding */
241
+ BOM: '\ufeff',
242
+ /** Default field separator character */
243
+ DEFAULT_FIELD_SEPARATOR: ',',
244
+ /** Default decimal separator */
245
+ DEFAULT_DECIMAL_SEPARATOR: '.',
246
+ /** Default quote character for strings */
247
+ DEFAULT_QUOTE: '"',
248
+ /** Default value for showing title */
249
+ DEFAULT_SHOW_TITLE: false,
250
+ /** Default title text */
251
+ DEFAULT_TITLE: 'My Generated Report',
252
+ /** Default filename (without extension) */
253
+ DEFAULT_FILENAME: 'generated',
254
+ /** Default value for showing column labels */
255
+ DEFAULT_SHOW_LABELS: false,
256
+ /** Default value for using text file format */
257
+ DEFAULT_USE_TEXT_FILE: false,
258
+ /** Default value for including BOM */
259
+ DEFAULT_USE_BOM: true,
260
+ /** Default header array (empty) */
261
+ DEFAULT_HEADER: [],
262
+ /** Default value for using object keys as headers */
263
+ DEFAULT_KEYS_AS_HEADERS: false
215
264
  };
216
- class CsvService {
265
+
266
+ /**
267
+ * Service for exporting data to CSV format and parsing CSV content.
268
+ *
269
+ * @remarks
270
+ * Provides functionality to generate CSV files from JSON data with customizable options,
271
+ * and to parse CSV content back into structured data.
272
+ *
273
+ * @example
274
+ * ```typescript
275
+ * const csvExporter = inject(CsvExporter);
276
+ * csvExporter.options = { filename: 'export', showLabels: true };
277
+ * csvExporter.generate([{ name: 'John', age: 30 }]);
278
+ * ```
279
+ */
280
+ class CsvExporter {
217
281
  _data = [];
218
282
  _options;
219
283
  _csv = '';
284
+ configDefaults = {
285
+ filename: CSV_CONFIG.DEFAULT_FILENAME,
286
+ fieldSeparator: CSV_CONFIG.DEFAULT_FIELD_SEPARATOR,
287
+ quoteStrings: CSV_CONFIG.DEFAULT_QUOTE,
288
+ decimalSeparator: CSV_CONFIG.DEFAULT_DECIMAL_SEPARATOR,
289
+ showLabels: CSV_CONFIG.DEFAULT_SHOW_LABELS,
290
+ showTitle: CSV_CONFIG.DEFAULT_SHOW_TITLE,
291
+ title: CSV_CONFIG.DEFAULT_TITLE,
292
+ useTextFile: CSV_CONFIG.DEFAULT_USE_TEXT_FILE,
293
+ useBom: CSV_CONFIG.DEFAULT_USE_BOM,
294
+ headers: [...CSV_CONFIG.DEFAULT_HEADER],
295
+ useKeysAsHeaders: CSV_CONFIG.DEFAULT_KEYS_AS_HEADERS
296
+ };
220
297
  get options() {
221
298
  return this._options;
222
299
  }
223
300
  set options(options) {
224
- this._options = this.objectAssign({}, ConfigDefaults, options);
301
+ this._options = this.objectAssign({}, this.configDefaults, options);
225
302
  }
226
303
  constructor() {
227
- this._options = this.objectAssign({}, ConfigDefaults);
304
+ this._options = this.objectAssign({}, this.configDefaults);
228
305
  }
229
306
  /**
230
- * Generate and Download Csv
307
+ * Generates a CSV file from JSON data and optionally downloads it.
308
+ *
309
+ * @param jsonData - The data to export (array of objects or JSON string)
310
+ * @param shouldReturnCsv - If true, returns the CSV string instead of downloading
311
+ * @returns The CSV string if shouldReturnCsv is true, otherwise void
312
+ *
313
+ * @example
314
+ * ```typescript
315
+ * // Download CSV
316
+ * csvExporter.generate([{ name: 'John', age: 30 }]);
317
+ *
318
+ * // Get CSV string
319
+ * const csvString = csvExporter.generate([{ name: 'John' }], true);
320
+ * ```
231
321
  */
232
322
  generate(jsonData, shouldReturnCsv = false) {
233
323
  // Make sure to reset csv data on each run
234
324
  this._csv = '';
235
325
  this._parseData(jsonData);
236
326
  if (this._options.useBom) {
237
- this._csv += CsvConfigConsts.BOM;
327
+ this._csv += CSV_CONFIG.BOM;
238
328
  }
239
329
  if (this._options.showTitle) {
240
330
  this._csv += this._options.title + '\r\n\n';
@@ -257,8 +347,6 @@ class CsvService {
257
347
  const blob = new Blob([this._csv], {
258
348
  type: 'text/' + FileType + ';charset=utf8;'
259
349
  });
260
- //const attachmentType = this._options.useTextFile ? 'text' : 'csv';
261
- // const uri = 'data:attachment/' + attachmentType + ';charset=utf-8,' + encodeURI(this._csv);
262
350
  const link = document.createElement('a');
263
351
  link.href = URL.createObjectURL(blob);
264
352
  link.setAttribute('visibility', 'hidden');
@@ -268,7 +356,9 @@ class CsvService {
268
356
  document.body.removeChild(link);
269
357
  }
270
358
  /**
271
- * Create Headers
359
+ * Creates CSV headers row based on configuration.
360
+ *
361
+ * @private
272
362
  */
273
363
  _getHeaders() {
274
364
  if (!this._options.showLabels && !this._options.useKeysAsHeaders) {
@@ -284,11 +374,13 @@ class CsvService {
284
374
  row += headers[keyPos] + this._options.fieldSeparator;
285
375
  }
286
376
  row = row.slice(0, -1);
287
- this._csv += row + CsvConfigConsts.EOL;
377
+ this._csv += row + CSV_CONFIG.EOL;
288
378
  }
289
379
  }
290
380
  /**
291
- * Create Body
381
+ * Creates CSV body rows from the data.
382
+ *
383
+ * @private
292
384
  */
293
385
  _getBody() {
294
386
  const keys = Object.keys(this._data[0]);
@@ -300,12 +392,15 @@ class CsvService {
300
392
  this._formatData(this._data[i][key]) + this._options.fieldSeparator;
301
393
  }
302
394
  row = row.slice(0, -1);
303
- this._csv += row + CsvConfigConsts.EOL;
395
+ this._csv += row + CSV_CONFIG.EOL;
304
396
  }
305
397
  }
306
398
  /**
307
- * Format Data
308
- * @param {any} data
399
+ * Formats data for CSV output according to configuration.
400
+ *
401
+ * @param data - The data value to format
402
+ * @returns The formatted data as a string
403
+ * @private
309
404
  */
310
405
  _formatData(data) {
311
406
  if (this._options.decimalSeparator === 'locale' && this._isFloat(data)) {
@@ -330,27 +425,32 @@ class CsvService {
330
425
  return data;
331
426
  }
332
427
  /**
333
- * Check if is Float
334
- * @param {any} input
428
+ * Checks if a value is a floating point number.
429
+ *
430
+ * @param input - The value to check
431
+ * @returns True if the value is a float, false otherwise
432
+ * @private
335
433
  */
336
434
  _isFloat(input) {
337
435
  return +input === input && (!isFinite(input) || Boolean(input % 1));
338
436
  }
339
437
  /**
340
- * Parse the collection given to it
438
+ * Parses JSON data into an array format.
341
439
  *
440
+ * @param jsonData - The JSON data to parse (object, array, or JSON string)
441
+ * @returns Parsed array of data
342
442
  * @private
343
- * @param {*} jsonData
344
- * @returns {any[]}
345
- * @memberof ExportToCsv
346
443
  */
347
444
  _parseData(jsonData) {
348
445
  this._data = typeof jsonData != 'object' ? JSON.parse(jsonData) : jsonData;
349
446
  return this._data;
350
447
  }
351
448
  /**
352
- * Convet to Object
353
- * @param {any} val
449
+ * Converts a value to an object, throwing an error for null or undefined.
450
+ *
451
+ * @param val - The value to convert
452
+ * @returns The value as an object
453
+ * @throws {TypeError} If val is null or undefined
354
454
  */
355
455
  toObject(val) {
356
456
  if (val === null || val === undefined) {
@@ -359,9 +459,11 @@ class CsvService {
359
459
  return Object(val);
360
460
  }
361
461
  /**
362
- * Assign data to new Object
363
- * @param {any} target
364
- * @param {any[]} ...source
462
+ * Assigns properties from source objects to a target object (similar to Object.assign).
463
+ *
464
+ * @param target - The target object to assign properties to
465
+ * @param source - One or more source objects to copy properties from
466
+ * @returns The target object with assigned properties
365
467
  */
366
468
  objectAssign(target, ...source) {
367
469
  let from;
@@ -387,6 +489,18 @@ class CsvService {
387
489
  }
388
490
  return to;
389
491
  }
492
+ /**
493
+ * Parses CSV content into a structured format with headers and content rows.
494
+ *
495
+ * @param csvContent - The CSV string to parse
496
+ * @returns An object containing the header array and content rows array
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * const parsed = csvExporter.read('name,age\nJohn,30\nJane,25');
501
+ * // Returns: { header: ['name', 'age'], content: [['John', '30'], ['Jane', '25']] }
502
+ * ```
503
+ */
390
504
  read(csvContent) {
391
505
  const lines = csvContent.split('\n');
392
506
  let header;
@@ -396,7 +510,7 @@ class CsvService {
396
510
  return this.parseLine(line.trim());
397
511
  });
398
512
  header = csv[0];
399
- // Si encuentra una linea en blanco ya no sigue leyendo las siguientes líneas
513
+ // If a blank line is found, stop reading subsequent lines
400
514
  let breakIndex;
401
515
  csv.some((row, index) => {
402
516
  const isBlankLine = row.every((column) => column.trim() === '');
@@ -417,236 +531,189 @@ class CsvService {
417
531
  content
418
532
  };
419
533
  }
534
+ /**
535
+ * Parses a single CSV line into an array of values.
536
+ *
537
+ * @param csvLine - A single line of CSV content
538
+ * @returns Array of parsed values from the line
539
+ *
540
+ * @remarks
541
+ * Handles quoted values and different quote styles (single and double quotes).
542
+ */
420
543
  parseLine(csvLine) {
421
544
  const values = [];
422
545
  const regex = /(?:"([^"]*)"|'([^']*)'|([^,]+))(?:,|\r?$)/g;
423
546
  let match;
424
547
  while ((match = regex.exec(csvLine)) !== null) {
425
- values.push(match[1] || match[2] || match[3]); // Extraer el valor correcto
548
+ values.push(match[1] || match[2] || match[3]); // Extract the correct value
426
549
  }
427
550
  return values;
428
551
  }
429
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: CsvService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
430
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: CsvService, providedIn: 'root' });
552
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CsvExporter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
553
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CsvExporter, providedIn: 'root' });
431
554
  }
432
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: CsvService, decorators: [{
555
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CsvExporter, decorators: [{
433
556
  type: Injectable,
434
557
  args: [{
435
- providedIn: 'root'
558
+ providedIn: 'root',
436
559
  }]
437
560
  }], ctorParameters: () => [] });
438
561
 
439
- class DateService {
562
+ /**
563
+ * Utility service for date operations.
564
+ *
565
+ * @remarks
566
+ * Provides helper methods for date parsing and manipulation.
567
+ */
568
+ class DateUtil {
569
+ /**
570
+ * Parses a date string in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss) and returns a Date object.
571
+ *
572
+ * @param date - The date string to parse (ISO format)
573
+ * @returns A Date object representing the parsed date
574
+ *
575
+ * @example
576
+ * ```typescript
577
+ * const date = dateUtil.getDate('2025-01-15');
578
+ * // Returns: Date object for January 15, 2025
579
+ * ```
580
+ */
440
581
  getDate(date) {
441
582
  const dateParts = date.split('T')[0].split('-');
442
583
  return new Date(Number(dateParts[0]), Number(dateParts[1]) - 1, Number(dateParts[2]));
443
584
  }
444
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: DateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
445
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: DateService, providedIn: 'root' });
585
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: DateUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
586
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: DateUtil, providedIn: 'root' });
446
587
  }
447
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: DateService, decorators: [{
448
- type: Injectable,
449
- args: [{
450
- providedIn: 'root'
451
- }]
452
- }] });
453
-
454
- class FilesService {
455
- callback;
456
- fileInput;
457
- pickerClosed = false;
458
- constructor() {
459
- this.fileInput = document.createElement('input');
460
- this.fileInput.type = 'file';
461
- this.fileInput.addEventListener('change', (event) => {
462
- this.pickerClosed = true;
463
- this.loadValue(event.currentTarget.files);
464
- });
465
- }
466
- loadValue(files) {
467
- if (files && files.length > 0) {
468
- let data = [];
469
- for (let i = 0; i < files.length; i++) {
470
- const file = files.item(i);
471
- const reader = new FileReader();
472
- reader.readAsDataURL(file);
473
- reader.onload = () => {
474
- data.push(Object.assign(file, {
475
- data: reader.result,
476
- }));
477
- if (data.length == files.length) {
478
- this.callback(data.length > 0 ? data : null);
479
- this.fileInput.value = '';
480
- }
481
- else {
482
- this.callback(null);
483
- }
484
- };
485
- }
486
- }
487
- }
488
- open(callback, options) {
489
- this.pickerClosed = false;
490
- // Detectar cuando el usuario vuelve a la ventana después de abrir el picker
491
- const onFocus = () => {
492
- setTimeout(() => {
493
- if (!this.pickerClosed) {
494
- console.log('El usuario cerró el file picker sin seleccionar archivos.');
495
- this.loadValue(null);
496
- }
497
- window.removeEventListener('focus', onFocus);
498
- }, 500); // Esperamos un poco para asegurarnos de que onchange haya tenido oportunidad de ejecutarse
499
- };
500
- window.addEventListener('focus', onFocus);
501
- this.fileInput.accept = options?.accept ? options.accept : '';
502
- this.fileInput.multiple = options?.multiple || false;
503
- this.fileInput.click();
504
- if (callback) {
505
- this.callback = callback;
506
- }
507
- }
508
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FilesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
509
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FilesService, providedIn: 'root' });
510
- }
511
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FilesService, decorators: [{
588
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: DateUtil, decorators: [{
512
589
  type: Injectable,
513
590
  args: [{
514
591
  providedIn: 'root',
515
592
  }]
516
- }], ctorParameters: () => [] });
593
+ }] });
517
594
 
518
- class FilePickerService {
519
- pickerClosed = false;
595
+ /**
596
+ * Service for opening file picker dialogs and reading file contents.
597
+ *
598
+ * @remarks
599
+ * Provides a programmatic way to open file selection dialogs and read file data as base64.
600
+ */
601
+ class FilePicker {
602
+ /**
603
+ * Opens a file picker dialog and returns the selected files with their data as base64.
604
+ *
605
+ * @param options - Optional configuration for the file picker
606
+ * @param options.accept - File types to accept (e.g., 'image/*', '.pdf')
607
+ * @param options.multiple - Whether to allow multiple file selection
608
+ * @returns Promise that resolves to an array of files with data property, or null if cancelled
609
+ *
610
+ * @example
611
+ * ```typescript
612
+ * const files = await filePicker.open({ accept: 'image/*', multiple: true });
613
+ * if (files) {
614
+ * files.forEach(file => console.log(file.data)); // base64 data
615
+ * }
616
+ * ```
617
+ */
520
618
  async open(options) {
521
619
  return new Promise((resolve, reject) => {
522
620
  const fileInput = document.createElement('input');
523
621
  fileInput.type = 'file';
524
- fileInput.addEventListener('change', (event) => {
525
- this.pickerClosed = true;
526
- const files = event.currentTarget.files;
527
- if (files && files.length > 0) {
528
- let data = [];
529
- for (let i = 0; i < files.length; i++) {
530
- const file = files.item(i);
531
- const reader = new FileReader();
532
- reader.readAsDataURL(file);
533
- reader.onload = () => {
534
- data.push(Object.assign(file, {
535
- data: reader.result
536
- }));
537
- if (data.length == files.length) {
538
- fileInput.value = '';
539
- resolve(data.length > 0 ? data : null);
540
- }
541
- else {
542
- resolve(null);
543
- }
544
- };
545
- }
622
+ fileInput.style.display = 'none';
623
+ if (options?.accept) {
624
+ fileInput.accept = options.accept;
625
+ }
626
+ fileInput.multiple = options?.multiple ?? false;
627
+ document.body.appendChild(fileInput);
628
+ let changeHandled = false;
629
+ const cleanUp = () => {
630
+ window.removeEventListener('focus', onFocus);
631
+ document.removeEventListener('visibilitychange', onVisibilityChange);
632
+ if (fileInput.parentNode) {
633
+ document.body.removeChild(fileInput);
546
634
  }
547
- });
548
- this.pickerClosed = false;
549
- // Detectar cuando el usuario vuelve a la ventana después de abrir el picker
635
+ };
550
636
  const onFocus = () => {
551
637
  setTimeout(() => {
552
- if (!this.pickerClosed) {
553
- reject(null);
638
+ if (!changeHandled) {
639
+ cleanUp();
640
+ resolve(null);
554
641
  }
555
- window.removeEventListener('focus', onFocus);
556
- }, 500); // Esperamos un poco para asegurarnos de que onchange haya tenido oportunidad de ejecutarse
642
+ }, 1500);
557
643
  };
644
+ const onVisibilityChange = () => {
645
+ if (document.visibilityState === 'visible') {
646
+ setTimeout(() => {
647
+ if (!changeHandled) {
648
+ cleanUp();
649
+ resolve(null);
650
+ }
651
+ }, 1500);
652
+ }
653
+ };
654
+ fileInput.addEventListener('change', async (event) => {
655
+ changeHandled = true;
656
+ const files = Array.from(event.target.files || []);
657
+ if (!files.length) {
658
+ cleanUp();
659
+ resolve(null);
660
+ return;
661
+ }
662
+ try {
663
+ const result = await Promise.all(files.map(file => {
664
+ return new Promise((res, rej) => {
665
+ const reader = new FileReader();
666
+ reader.onload = () => {
667
+ res(Object.assign(file, { data: reader.result }));
668
+ };
669
+ reader.onerror = (err) => {
670
+ rej(reader.error);
671
+ };
672
+ reader.readAsDataURL(file);
673
+ });
674
+ }));
675
+ cleanUp();
676
+ resolve(result);
677
+ }
678
+ catch (error) {
679
+ cleanUp();
680
+ reject(error);
681
+ }
682
+ });
558
683
  window.addEventListener('focus', onFocus);
559
- fileInput.accept = options?.accept ? options.accept : '';
560
- fileInput.multiple = options?.multiple || false;
684
+ document.addEventListener('visibilitychange', onVisibilityChange);
561
685
  fileInput.click();
562
686
  });
563
687
  }
564
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FilePickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
565
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FilePickerService, providedIn: 'root' });
688
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilePicker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
689
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilePicker, providedIn: 'root' });
566
690
  }
567
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FilePickerService, decorators: [{
691
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilePicker, decorators: [{
568
692
  type: Injectable,
569
693
  args: [{
570
- providedIn: 'root'
694
+ providedIn: 'root',
571
695
  }]
572
696
  }] });
573
697
 
574
- class GoogleTagManagerService {
575
- platformId;
576
- router;
577
- trackingId;
578
- constructor(platformId, router) {
579
- this.platformId = platformId;
580
- this.router = router;
581
- }
582
- appendTrackingCode(trackingId, options) {
583
- try {
584
- if (isPlatformBrowser(this.platformId) && trackingId) {
585
- this.trackingId = trackingId;
586
- const s1 = document.createElement('script');
587
- s1.innerHTML = `
588
- (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
589
- new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
590
- j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'',p='${options?.environment?.preview
591
- ? '&gtm_auth=' + options?.environment.auth + '&gtm_preview=' + options?.environment.preview
592
- : ''}';j.async=true;j.src=
593
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl+p;f.parentNode.insertBefore(j,f);
594
- })(window,document,'script','dataLayer','${trackingId}');
595
- `;
596
- document.head.appendChild(s1);
597
- const s2 = document.createElement('noscript');
598
- const s3 = document.createElement('iframe');
599
- s3.width = '0';
600
- s3.height = '0';
601
- s3.style.display = 'none';
602
- s3.style.visibility = 'hidden';
603
- s3.src = `//www.googletagmanager.com/ns.html?id=${trackingId}`;
604
- s2.appendChild(s3);
605
- document.body.prepend(s2);
606
- this.initSubscribers();
607
- }
608
- }
609
- catch (ex) {
610
- console.error('Error appending google tag manager');
611
- console.error(ex);
612
- }
613
- }
614
- addVariable(variable) {
615
- if (isPlatformBrowser(this.platformId) && this.trackingId) {
616
- window.dataLayer = window.dataLayer || [];
617
- window.dataLayer.push(variable);
618
- }
619
- }
620
- initSubscribers() {
621
- this.router.events.subscribe(event => {
622
- try {
623
- if (event instanceof NavigationEnd && this.trackingId) {
624
- this.addVariable({
625
- event: 'router.NavigationEnd',
626
- pageTitle: document.title,
627
- pagePath: event.urlAfterRedirects
628
- });
629
- }
630
- }
631
- catch (e) {
632
- console.error(e);
633
- }
634
- });
635
- }
636
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GoogleTagManagerService, deps: [{ token: PLATFORM_ID }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
637
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GoogleTagManagerService, providedIn: 'root' });
638
- }
639
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: GoogleTagManagerService, decorators: [{
640
- type: Injectable,
641
- args: [{
642
- providedIn: 'root'
643
- }]
644
- }], ctorParameters: () => [{ type: Object, decorators: [{
645
- type: Inject,
646
- args: [PLATFORM_ID]
647
- }] }, { type: i1.Router }] });
648
-
649
- class ObjectService {
698
+ /**
699
+ * Utility service for object operations.
700
+ *
701
+ * @remarks
702
+ * Provides helper methods for object manipulation and transformation.
703
+ */
704
+ class ObjectUtil {
705
+ /**
706
+ * Filters out null, undefined, and 'undefined' string properties from an object.
707
+ *
708
+ * @param obj - The object to filter
709
+ * @returns A new object with null/undefined properties removed
710
+ *
711
+ * @example
712
+ * ```typescript
713
+ * const filtered = objectUtil.filterNullProperties({ a: 1, b: null, c: undefined });
714
+ * // Returns: { a: 1 }
715
+ * ```
716
+ */
650
717
  filterNullProperties(obj) {
651
718
  const mappedObj = {};
652
719
  Object.keys(obj).forEach((key) => {
@@ -656,6 +723,22 @@ class ObjectService {
656
723
  });
657
724
  return mappedObj;
658
725
  }
726
+ /**
727
+ * Performs a deep merge of two objects, combining nested properties.
728
+ *
729
+ * @param target - The target object to merge into
730
+ * @param source - The source object to merge from
731
+ * @returns A new object with deeply merged properties
732
+ *
733
+ * @remarks
734
+ * Uses structuredClone to avoid mutating the original objects.
735
+ *
736
+ * @example
737
+ * ```typescript
738
+ * const merged = objectUtil.deepMerge({ a: { b: 1 } }, { a: { c: 2 } });
739
+ * // Returns: { a: { b: 1, c: 2 } }
740
+ * ```
741
+ */
659
742
  deepMerge(target, source) {
660
743
  source = structuredClone(source);
661
744
  target = structuredClone(target);
@@ -666,23 +749,34 @@ class ObjectService {
666
749
  }
667
750
  return { ...target, ...source };
668
751
  }
669
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ObjectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
670
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ObjectService, providedIn: 'root' });
752
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ObjectUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
753
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ObjectUtil, providedIn: 'root' });
671
754
  }
672
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: ObjectService, decorators: [{
755
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ObjectUtil, decorators: [{
673
756
  type: Injectable,
674
757
  args: [{
675
- providedIn: 'root'
758
+ providedIn: 'root',
676
759
  }]
677
760
  }] });
678
761
 
679
- class StorageService {
680
- platformId;
681
- //TODO: Replace with Map object it is more efficient
682
- memoryStorage;
683
- constructor(platformId) {
684
- this.platformId = platformId;
685
- }
762
+ /**
763
+ * Service for managing browser storage (localStorage, sessionStorage, and memory storage).
764
+ *
765
+ * @remarks
766
+ * Provides a unified interface for storing and retrieving data from different storage types.
767
+ * Automatically handles JSON serialization/deserialization and platform detection.
768
+ *
769
+ * @example
770
+ * ```typescript
771
+ * const storage = inject(Storage);
772
+ * storage.set('key', { data: 'value' }, 'local');
773
+ * const value = storage.get('key', 'local');
774
+ * ```
775
+ */
776
+ class Storage {
777
+ // TODO: Replace with Map object it is more efficient
778
+ memoryStorage = {};
779
+ platformId = inject(PLATFORM_ID);
686
780
  getValue(key, storage) {
687
781
  let value;
688
782
  if (!storage || typeof storage == 'string') {
@@ -703,6 +797,12 @@ class StorageService {
703
797
  }
704
798
  return value;
705
799
  }
800
+ /**
801
+ * Deletes a value from the specified storage.
802
+ *
803
+ * @param key - The key of the value to delete
804
+ * @param storage - The storage type ('local', 'session', or 'memory'). Defaults to 'session'
805
+ */
706
806
  delete(key, storage) {
707
807
  if (isPlatformBrowser(this.platformId)) {
708
808
  if (!storage || typeof storage == 'string') {
@@ -723,6 +823,16 @@ class StorageService {
723
823
  }
724
824
  }
725
825
  }
826
+ /**
827
+ * Retrieves a value from the specified storage.
828
+ *
829
+ * @param key - The key of the value to retrieve
830
+ * @param storage - The storage type ('local', 'session', or 'memory'). Defaults to 'session'
831
+ * @returns The parsed value (if JSON) or the raw value, or undefined if not found
832
+ *
833
+ * @remarks
834
+ * Automatically attempts to parse JSON values. If parsing fails, returns the raw string.
835
+ */
726
836
  get(key, storage) {
727
837
  let parsedValue;
728
838
  if (isPlatformBrowser(this.platformId)) {
@@ -735,6 +845,16 @@ class StorageService {
735
845
  }
736
846
  return parsedValue;
737
847
  }
848
+ /**
849
+ * Stores a value in the specified storage.
850
+ *
851
+ * @param key - The key to store the value under
852
+ * @param value - The value to store (will be JSON stringified)
853
+ * @param storage - The storage type ('local', 'session', or 'memory'). Defaults to 'session'
854
+ *
855
+ * @remarks
856
+ * Values are automatically JSON stringified before storage.
857
+ */
738
858
  set(key, value, storage) {
739
859
  if (isPlatformBrowser(this.platformId)) {
740
860
  const valueString = JSON.stringify(value);
@@ -753,20 +873,36 @@ class StorageService {
753
873
  }
754
874
  }
755
875
  }
756
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StorageService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
757
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StorageService, providedIn: 'root' });
876
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Storage, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
877
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Storage, providedIn: 'root' });
758
878
  }
759
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StorageService, decorators: [{
879
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: Storage, decorators: [{
760
880
  type: Injectable,
761
881
  args: [{
762
882
  providedIn: 'root',
763
883
  }]
764
- }], ctorParameters: () => [{ type: Object, decorators: [{
765
- type: Inject,
766
- args: [PLATFORM_ID]
767
- }] }] });
884
+ }] });
768
885
 
769
- class StringService {
886
+ /**
887
+ * Utility service for string operations.
888
+ *
889
+ * @remarks
890
+ * Provides helper methods for common string manipulation and formatting tasks.
891
+ */
892
+ class StringUtil {
893
+ /**
894
+ * Decodes HTML entities in a string.
895
+ *
896
+ * @param text - The string containing HTML entities to decode
897
+ * @returns The decoded string
898
+ *
899
+ * @example
900
+ * ```typescript
901
+ * const encoded = '&lt;div&gt;Hello&lt;/div&gt;';
902
+ * const decoded = stringUtil.decodeHTML(encoded);
903
+ * // Returns: '<div>Hello</div>'
904
+ * ```
905
+ */
770
906
  decodeHTML(text) {
771
907
  const span = document.createElement('span');
772
908
  return text.replace(/&[#A-Za-z0-9]+;/gi, (entity) => {
@@ -775,9 +911,16 @@ class StringService {
775
911
  });
776
912
  }
777
913
  /**
778
- * Normaliza un texto para evitar el Case y los acentos
779
- * @param text texto a normalizar
780
- * @returns texto normalizado
914
+ * Normalizes a text by converting to lowercase and removing accents.
915
+ *
916
+ * @param text - The text to normalize
917
+ * @returns The normalized text without accents and in lowercase
918
+ *
919
+ * @example
920
+ * ```typescript
921
+ * const normalized = stringUtil.normalize('Café');
922
+ * // Returns: 'cafe'
923
+ * ```
781
924
  */
782
925
  normalize(text) {
783
926
  if (!text)
@@ -787,37 +930,179 @@ class StringService {
787
930
  .normalize("NFD")
788
931
  .replace(/[\u0300-\u036f]/g, "");
789
932
  }
933
+ /**
934
+ * Normalizes a name by trimming whitespace and capitalizing the first letter.
935
+ *
936
+ * @param text - The name text to normalize
937
+ * @returns The normalized name with first letter capitalized
938
+ *
939
+ * @example
940
+ * ```typescript
941
+ * const normalized = stringUtil.normalizeName(' john doe ');
942
+ * // Returns: 'John doe'
943
+ * ```
944
+ */
790
945
  normalizeName(text) {
791
946
  text = text.trim();
792
947
  text = text.charAt(0).toUpperCase() + text.slice(1);
793
948
  return text;
794
949
  }
795
950
  /**
796
- * Te da el tiempo en milisegundos que toma leer un texto dado
797
- * @param text texto a leer
798
- * @return number Tiempo en milisegundos
951
+ * Calculates the estimated reading time in milliseconds for a given text.
952
+ *
953
+ * @param text - The text to calculate reading time for
954
+ * @returns The estimated reading time in milliseconds
955
+ *
956
+ * @remarks
957
+ * Uses a reading speed of 1200 characters per minute.
958
+ *
959
+ * @example
960
+ * ```typescript
961
+ * const readingTime = stringUtil.calculateReadingTime('Long text here...');
962
+ * // Returns: estimated milliseconds
963
+ * ```
799
964
  */
800
965
  calculateReadingTime(text) {
801
- // Caracteres por minuto
966
+ // Characters per minute
802
967
  const cpm = 1200;
803
- // Calcular el número de caracteres en el texto
968
+ // Calculate the number of characters in the text
804
969
  const numCharacters = text.length;
805
- // Calcular el tiempo de lectura en minutos
970
+ // Calculate reading time in minutes
806
971
  const readingTimeMinutes = numCharacters / cpm;
807
- // Convertir el tiempo de lectura de minutos a milisegundos
972
+ // Convert reading time from minutes to milliseconds
808
973
  const readingTimeMilliseconds = readingTimeMinutes * 60 * 1000;
809
974
  return readingTimeMilliseconds;
810
975
  }
811
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StringService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
812
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StringService, providedIn: 'root' });
976
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: StringUtil, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
977
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: StringUtil, providedIn: 'root' });
813
978
  }
814
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: StringService, decorators: [{
979
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: StringUtil, decorators: [{
815
980
  type: Injectable,
816
981
  args: [{
817
- providedIn: 'root'
982
+ providedIn: 'root',
983
+ }]
984
+ }] });
985
+
986
+ /**
987
+ * Service for integrating Google Tag Manager (GTM) tracking.
988
+ *
989
+ * @remarks
990
+ * Handles GTM script injection, variable tracking, and automatic route change tracking.
991
+ *
992
+ * @example
993
+ * ```typescript
994
+ * const gtm = inject(GoogleTagManager);
995
+ * gtm.appendTrackingCode('GTM-XXXXXXX');
996
+ * gtm.addVariable({ event: 'customEvent', data: 'value' });
997
+ * ```
998
+ */
999
+ class GoogleTagManager {
1000
+ trackingId;
1001
+ platformId = inject(PLATFORM_ID);
1002
+ router = inject(Router);
1003
+ /**
1004
+ * Appends Google Tag Manager tracking code to the page.
1005
+ *
1006
+ * @param trackingId - The GTM container ID (e.g., 'GTM-XXXXXXX')
1007
+ * @param options - Optional configuration for GTM environment
1008
+ * @param options.environment - Environment configuration for preview mode
1009
+ * @param options.environment.auth - Authentication token for preview
1010
+ * @param options.environment.preview - Preview container ID
1011
+ *
1012
+ * @remarks
1013
+ * Automatically subscribes to router navigation events for page view tracking.
1014
+ */
1015
+ appendTrackingCode(trackingId, options) {
1016
+ try {
1017
+ if (isPlatformBrowser(this.platformId) && trackingId) {
1018
+ this.trackingId = trackingId;
1019
+ const s1 = document.createElement('script');
1020
+ s1.innerHTML = `
1021
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
1022
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
1023
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'',p='${options?.environment?.preview
1024
+ ? '&gtm_auth=' + options?.environment.auth + '&gtm_preview=' + options?.environment.preview
1025
+ : ''}';j.async=true;j.src=
1026
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl+p;f.parentNode.insertBefore(j,f);
1027
+ })(window,document,'script','dataLayer','${trackingId}');
1028
+ `;
1029
+ document.head.appendChild(s1);
1030
+ const s2 = document.createElement('noscript');
1031
+ const s3 = document.createElement('iframe');
1032
+ s3.width = '0';
1033
+ s3.height = '0';
1034
+ s3.style.display = 'none';
1035
+ s3.style.visibility = 'hidden';
1036
+ s3.src = `//www.googletagmanager.com/ns.html?id=${trackingId}`;
1037
+ s2.appendChild(s3);
1038
+ document.body.prepend(s2);
1039
+ this.initSubscribers();
1040
+ }
1041
+ }
1042
+ catch (ex) {
1043
+ console.error('Error appending google tag manager');
1044
+ console.error(ex);
1045
+ }
1046
+ }
1047
+ /**
1048
+ * Pushes a variable or event to the GTM dataLayer.
1049
+ *
1050
+ * @param variable - The variable or event object to push to dataLayer
1051
+ *
1052
+ * @example
1053
+ * ```typescript
1054
+ * gtm.addVariable({ event: 'purchase', value: 100 });
1055
+ * ```
1056
+ */
1057
+ addVariable(variable) {
1058
+ if (isPlatformBrowser(this.platformId) && this.trackingId) {
1059
+ window.dataLayer = window.dataLayer || [];
1060
+ window.dataLayer.push(variable);
1061
+ }
1062
+ }
1063
+ /**
1064
+ * Initializes router event subscribers for automatic page view tracking.
1065
+ *
1066
+ * @private
1067
+ */
1068
+ initSubscribers() {
1069
+ this.router.events.subscribe(event => {
1070
+ try {
1071
+ if (event instanceof NavigationEnd && this.trackingId) {
1072
+ this.addVariable({
1073
+ event: 'router.NavigationEnd',
1074
+ pageTitle: document.title,
1075
+ pagePath: event.urlAfterRedirects
1076
+ });
1077
+ }
1078
+ }
1079
+ catch (e) {
1080
+ console.error(e);
1081
+ }
1082
+ });
1083
+ }
1084
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GoogleTagManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1085
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GoogleTagManager, providedIn: 'root' });
1086
+ }
1087
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GoogleTagManager, decorators: [{
1088
+ type: Injectable,
1089
+ args: [{
1090
+ providedIn: 'root',
818
1091
  }]
819
1092
  }] });
820
1093
 
1094
+ /**
1095
+ * Constants for CRUD operation types.
1096
+ */
1097
+ const OPERATION_TYPE = {
1098
+ /** Create operation */
1099
+ CREATE: 'create',
1100
+ /** Update operation */
1101
+ UPDATE: 'update',
1102
+ /** Delete operation */
1103
+ DELETE: 'delete'
1104
+ };
1105
+
821
1106
  function lengthValidator(number, digits) {
822
1107
  let value = true;
823
1108
  if (number.trim() === '') { // No puede estar vacio
@@ -938,6 +1223,18 @@ function storeCodeValidator(number) {
938
1223
  }
939
1224
  return value;
940
1225
  }
1226
+ /**
1227
+ * Determines the type of Ecuadorian identification number.
1228
+ *
1229
+ * @param number - The identification number to analyze
1230
+ * @returns The identification type ('cedula', 'ruc_natural', 'ruc_privada', 'ruc_publica') or null if invalid
1231
+ *
1232
+ * @example
1233
+ * ```typescript
1234
+ * const type = getIdentificationType('1234567890');
1235
+ * // Returns: 'cedula' or null
1236
+ * ```
1237
+ */
941
1238
  function getIdentificationType(number) {
942
1239
  let type = null;
943
1240
  if (lengthValidator(number, 10) &&
@@ -972,6 +1269,23 @@ function getIdentificationType(number) {
972
1269
  return type;
973
1270
  }
974
1271
 
1272
+ /**
1273
+ * Creates a validator function for Ecuadorian identification numbers (cédula or RUC).
1274
+ *
1275
+ * @param type - The type of identification to validate
1276
+ * - 'cedula': Validates cédula (10 digits)
1277
+ * - 'ruc_natural': Validates RUC for natural persons (13 digits)
1278
+ * - 'ruc_privada': Validates RUC for private companies (13 digits)
1279
+ * - 'ruc_publica': Validates RUC for public companies (13 digits)
1280
+ * - 'ruc': Validates any type of RUC
1281
+ * - 'id': Validates any valid identification type
1282
+ * @returns A validator function that returns an error object if validation fails
1283
+ *
1284
+ * @example
1285
+ * ```typescript
1286
+ * const control = new FormControl('', [identificationValidator('cedula')]);
1287
+ * ```
1288
+ */
975
1289
  function identificationValidator(type) {
976
1290
  return (control) => {
977
1291
  const number = String(control.value);
@@ -1001,9 +1315,440 @@ function identificationValidator(type) {
1001
1315
  };
1002
1316
  }
1003
1317
 
1318
+ class AuthProvider {
1319
+ }
1320
+
1321
+ /**
1322
+ * Injection token for auth configuration.
1323
+ * Provide this in app.config with values from environment.
1324
+ *
1325
+ * @example
1326
+ * ```typescript
1327
+ * import { AUTH_CONFIG } from 'auth-core';
1328
+ * import { environment } from '@/environments/environment';
1329
+ *
1330
+ * providers: [
1331
+ * { provide: AUTH_CONFIG, useValue: environment }
1332
+ * ]
1333
+ * ```
1334
+ */
1335
+ const AUTH_CONFIG = new InjectionToken('AUTH_CONFIG');
1336
+
1337
+ /**
1338
+ * Concrete authentication provider responsible for handling session lifecycle,
1339
+ * secure token refresh, and profile management concerns across the app.
1340
+ *
1341
+ * @remarks
1342
+ * The service extends {@link AuthProvider} to leverage core session helpers while
1343
+ * adding stateful logic for dialogs, social login, and settings synchronization.
1344
+ */
1345
+ class AuthService extends AuthProvider {
1346
+ // Dependency injection
1347
+ config = inject(AUTH_CONFIG);
1348
+ dialog = inject(MatDialog);
1349
+ httpClient = inject(HttpClient);
1350
+ _user = signal(null, ...(ngDevMode ? [{ debugName: "_user" }] : /* istanbul ignore next */ []));
1351
+ /** Session token key */
1352
+ TOKEN_KEY = `${this.config.sessionPrefix}_sess`;
1353
+ user = computed(() => this._user(), ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
1354
+ constructor() {
1355
+ super();
1356
+ if (this.isLoggedIn()) {
1357
+ this._user.set(this.extractUserFromToken(this.getToken()?.token ?? ''));
1358
+ }
1359
+ }
1360
+ /**
1361
+ * Flag indicating whether the access token is being refreshed
1362
+ */
1363
+ refreshTokenInProgress = false;
1364
+ /**
1365
+ * Manages the access token refresh flow
1366
+ */
1367
+ refreshTokenSubject = new BehaviorSubject(null);
1368
+ /**
1369
+ * Sends the authentication token to the server
1370
+ * @param request HTTP request
1371
+ * @returns
1372
+ */
1373
+ addAuthenticationToken(request) {
1374
+ const token = this.getToken();
1375
+ // If the access token is null, the user is not logged in; return the original request
1376
+ if (!token ||
1377
+ request.url.includes(this.config.auth.signinUrl) ||
1378
+ (this.config.auth.refreshTokenUrl && request.url.includes(this.config.auth.refreshTokenUrl))) {
1379
+ return request;
1380
+ }
1381
+ // Clone the request, because the original request is immutable
1382
+ return request.clone({
1383
+ setHeaders: {
1384
+ Authorization: `Bearer ${token.token}`
1385
+ }
1386
+ });
1387
+ }
1388
+ async connect(client) {
1389
+ const isChrome = navigator.userAgentData?.brands?.some((b) => b.brand === 'Google Chrome') ?? false;
1390
+ if (this.config.fedcm &&
1391
+ isChrome &&
1392
+ 'credentials' in navigator &&
1393
+ navigator.credentials &&
1394
+ 'get' in navigator.credentials) {
1395
+ try {
1396
+ const fedcm = this.config.fedcm[client];
1397
+ if (!fedcm) {
1398
+ throw new Error('No auth client exists');
1399
+ }
1400
+ const credential = await navigator.credentials.get({
1401
+ identity: {
1402
+ mode: 'active',
1403
+ providers: [
1404
+ {
1405
+ configURL: fedcm.configURL,
1406
+ clientId: fedcm.clientId,
1407
+ fields: ['name', 'email', 'picture'],
1408
+ params: {
1409
+ fetch_basic_profile: true,
1410
+ response_type: 'permission id_token',
1411
+ scope: 'email profile openid',
1412
+ include_granted_scopes: true,
1413
+ nonce: 'notprovided'
1414
+ }
1415
+ }
1416
+ ]
1417
+ },
1418
+ mediation: 'required'
1419
+ });
1420
+ if (!credential) {
1421
+ throw new Error('No credential obtained');
1422
+ }
1423
+ const fedcmCredential = credential;
1424
+ // Send the ID token to the backend
1425
+ const response = await fetch(fedcm.tokenUrl, {
1426
+ method: 'POST',
1427
+ headers: { 'Content-Type': 'application/json' },
1428
+ body: JSON.stringify({
1429
+ id_token: JSON.parse(fedcmCredential.token).id_token
1430
+ })
1431
+ });
1432
+ const data = await response.json();
1433
+ this.setToken(data.token);
1434
+ this._user.set(data.user);
1435
+ location.href = this.config.appPath;
1436
+ }
1437
+ catch (e) {
1438
+ console.error('FedCM error: ', e);
1439
+ return false;
1440
+ }
1441
+ }
1442
+ else {
1443
+ const url = this.config.auth.clients[client];
1444
+ if (url) {
1445
+ location.href = url;
1446
+ }
1447
+ }
1448
+ return true;
1449
+ }
1450
+ /**
1451
+ * Extracts user claims from a JWT token payload.
1452
+ * Maps common JWT claims (sub, email, given_name, family_name, etc.) to the User model.
1453
+ * @param jwtToken The JWT token string
1454
+ * @returns User object or null if parsing fails
1455
+ */
1456
+ extractUserFromToken(jwtToken) {
1457
+ if (!jwtToken)
1458
+ return null;
1459
+ try {
1460
+ const jwtParts = jwtToken.split('.');
1461
+ if (jwtParts.length !== 3)
1462
+ return null;
1463
+ const payload = JSON.parse(window.atob(jwtParts[1]));
1464
+ const roles = Array.isArray(payload['roles'])
1465
+ ? payload['roles']
1466
+ : typeof payload['role'] === 'string'
1467
+ ? [payload['role']]
1468
+ : [];
1469
+ return {
1470
+ username: (payload['sub'] ??
1471
+ payload['preferred_username'] ??
1472
+ payload['username'] ??
1473
+ ''),
1474
+ roles
1475
+ };
1476
+ }
1477
+ catch {
1478
+ return null;
1479
+ }
1480
+ }
1481
+ /**
1482
+ * Extracts the expiration timestamp from a JWT token
1483
+ * @param jwtToken The JWT token string
1484
+ * @returns The expiration timestamp in seconds (JWT exp format) or undefined if not found/invalid
1485
+ */
1486
+ extractExpirationFromToken(jwtToken) {
1487
+ if (!jwtToken) {
1488
+ return undefined;
1489
+ }
1490
+ try {
1491
+ const jwtParts = jwtToken.split('.');
1492
+ if (jwtParts.length === 3) {
1493
+ const payload = JSON.parse(window.atob(jwtParts[1]));
1494
+ if (payload.exp) {
1495
+ // Store timestamp in seconds (JWT exp format)
1496
+ return payload.exp;
1497
+ }
1498
+ }
1499
+ }
1500
+ catch {
1501
+ // If JWT parsing fails, return undefined
1502
+ }
1503
+ return undefined;
1504
+ }
1505
+ /**
1506
+ * Handles the flow of refreshing the access token or redirecting to sign-in
1507
+ * @param err HTTP error
1508
+ * @param request HTTP request sent
1509
+ * @param next HTTP handler
1510
+ */
1511
+ handle401Error(err, request, next) {
1512
+ const token = this.getToken();
1513
+ if (token && token.refresh_token && this.config.auth.refreshTokenUrl) {
1514
+ if (!this.refreshTokenInProgress) {
1515
+ this.refreshTokenInProgress = true;
1516
+ this.refreshTokenSubject.next(null);
1517
+ return this.refreshToken().pipe(switchMap((newToken) => {
1518
+ if (newToken) {
1519
+ this.refreshTokenSubject.next(newToken);
1520
+ return next(this.addAuthenticationToken(request));
1521
+ }
1522
+ // If we don't get a new token, logout.
1523
+ this.logout();
1524
+ return throwError(() => new HttpErrorResponse({
1525
+ error: {},
1526
+ headers: new HttpHeaders(),
1527
+ status: 401,
1528
+ statusText: '',
1529
+ url: undefined
1530
+ }));
1531
+ }), catchError((error) => {
1532
+ // It can't replace the access token; set error status 401 to continue flow
1533
+ return throwError(() => new HttpErrorResponse({
1534
+ error: error.error,
1535
+ headers: error.headers,
1536
+ status: 401,
1537
+ statusText: error.statusText,
1538
+ url: error.url || undefined
1539
+ }));
1540
+ }), share(), finalize(() => {
1541
+ this.refreshTokenInProgress = false;
1542
+ }));
1543
+ }
1544
+ else {
1545
+ return this.refreshTokenSubject.pipe(filter((token) => token != null), take(1), switchMap(() => {
1546
+ return next(this.addAuthenticationToken(request));
1547
+ }));
1548
+ }
1549
+ }
1550
+ else {
1551
+ // No refresh token flow
1552
+ if (this.isLoggedIn()) {
1553
+ this.logout();
1554
+ }
1555
+ return throwError(() => err);
1556
+ }
1557
+ }
1558
+ /**
1559
+ * Sends sign-in to the server and obtains the authentication token
1560
+ * @param data Authentication data
1561
+ * @returns
1562
+ */
1563
+ async login(data) {
1564
+ const token = await lastValueFrom(this.httpClient.post(this.config.auth.signinUrl, data));
1565
+ this.setToken(token);
1566
+ return true;
1567
+ }
1568
+ /**
1569
+ * Logs out the user
1570
+ */
1571
+ async logout() {
1572
+ this.dialog.closeAll();
1573
+ this.clearToken();
1574
+ this.clearUser();
1575
+ location.href =
1576
+ window.innerWidth < 1000 ? `${this.config.appPath}/auth` : `${this.config.appPath}/signin`;
1577
+ return true;
1578
+ }
1579
+ async signup(data, options) {
1580
+ return lastValueFrom(this.httpClient.post(this.config.auth.signupUrl, data, options));
1581
+ }
1582
+ /**
1583
+ * If a refresh token is implemented, send it to obtain a new access token
1584
+ * @returns Access token
1585
+ */
1586
+ refreshToken() {
1587
+ const token = this.getToken();
1588
+ return this.httpClient
1589
+ .post(this.config.auth.refreshTokenUrl, { refresh_token: token?.refresh_token })
1590
+ .pipe(tap((token) => {
1591
+ this.setToken(token);
1592
+ }), catchError((error) => {
1593
+ this.logout();
1594
+ return throwError(() => new Error(error));
1595
+ }));
1596
+ }
1597
+ /**
1598
+ * Reads the session token from the Secure cookie (client-side storage).
1599
+ * @internal
1600
+ */
1601
+ getToken() {
1602
+ if (typeof document === 'undefined' || !document.cookie)
1603
+ return null;
1604
+ const match = document.cookie.match(new RegExp('(?:^|; )' + this.TOKEN_KEY.replace(/([.*+?^${}()|[\]\\])/g, '\\$1') + '=([^;]*)'));
1605
+ const raw = match ? decodeURIComponent(match[1]) : null;
1606
+ if (!raw)
1607
+ return null;
1608
+ try {
1609
+ const data = JSON.parse(raw);
1610
+ return data?.token ? data : null;
1611
+ }
1612
+ catch {
1613
+ return null;
1614
+ }
1615
+ }
1616
+ /**
1617
+ * Sets the authentication token in session state and in a Secure cookie (client-side).
1618
+ * Cookie: Path=/; SameSite=Strict; Secure on HTTPS; Max-Age from expiresAt or 1 day.
1619
+ *
1620
+ * @param token - The authentication token object to store
1621
+ */
1622
+ setToken(token) {
1623
+ const copy = token ? { ...token } : null;
1624
+ if (copy && typeof document !== 'undefined') {
1625
+ const expiresAt = this.extractExpirationFromToken(copy.token);
1626
+ const value = encodeURIComponent(JSON.stringify(copy));
1627
+ const maxAge = expiresAt
1628
+ ? Math.max(0, expiresAt - Math.round(Date.now() / 1000))
1629
+ : 24 * 60 * 60;
1630
+ let cookie = `${this.TOKEN_KEY}=${value}; Path=/; Max-Age=${maxAge}; SameSite=Strict`;
1631
+ if (typeof location !== 'undefined' && location.protocol === 'https:')
1632
+ cookie += '; Secure';
1633
+ document.cookie = cookie;
1634
+ this._user.set(this.extractUserFromToken(copy.token) ?? null);
1635
+ }
1636
+ }
1637
+ /**
1638
+ * Clears the authentication token from session state and removes the cookie.
1639
+ */
1640
+ clearToken() {
1641
+ if (typeof document !== 'undefined') {
1642
+ document.cookie = `${this.TOKEN_KEY}=; Path=/; Max-Age=0`;
1643
+ }
1644
+ this.clearUser();
1645
+ }
1646
+ /**
1647
+ * Clears the current user from session state
1648
+ */
1649
+ clearUser() {
1650
+ this._user.set(null);
1651
+ }
1652
+ /**
1653
+ * Computed signal indicating whether a user is currently logged in
1654
+ * Based on the presence and validity of the authentication token
1655
+ *
1656
+ * For JWT tokens, validates expiration. For other token types, only checks existence.
1657
+ *
1658
+ * @returns true if a valid token exists, false otherwise
1659
+ */
1660
+ isLoggedIn = computed(() => {
1661
+ // Depend on _user so computed re-evaluates when setToken/clearToken updates state
1662
+ this._user();
1663
+ const token = this.getToken();
1664
+ if (!token)
1665
+ return false;
1666
+ const expiresAt = this.extractExpirationFromToken(token.token);
1667
+ if (expiresAt) {
1668
+ const currentTimestamp = Math.round(Date.now() / 1000);
1669
+ return expiresAt > currentTimestamp;
1670
+ }
1671
+ return true;
1672
+ }, ...(ngDevMode ? [{ debugName: "isLoggedIn" }] : /* istanbul ignore next */ []));
1673
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1674
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthService, providedIn: 'root' });
1675
+ }
1676
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthService, decorators: [{
1677
+ type: Injectable,
1678
+ args: [{
1679
+ providedIn: 'root'
1680
+ }]
1681
+ }], ctorParameters: () => [] });
1682
+
1683
+ const authGuard = async (route, state) => {
1684
+ const config = inject(AUTH_CONFIG);
1685
+ const router = inject(Router);
1686
+ const authProvider = inject(AuthProvider);
1687
+ const storageService = inject(Storage);
1688
+ if (!authProvider.isLoggedIn()) {
1689
+ storageService.set(`${config.sessionPrefix}_rdi`, state.url);
1690
+ return router.createUrlTree([window.innerWidth < 1000 ? '/auth' : '/signin']);
1691
+ }
1692
+ return true;
1693
+ };
1694
+ const loginGuard = () => {
1695
+ const authProvider = inject(AuthProvider);
1696
+ const router = inject(Router);
1697
+ return authProvider.isLoggedIn() ? router.createUrlTree(['/']) : true;
1698
+ };
1699
+ const resetGuard = (route) => {
1700
+ const authProvider = inject(AuthProvider);
1701
+ const router = inject(Router);
1702
+ const token = route.queryParamMap.get('token');
1703
+ if (token && !authProvider.isLoggedIn()) {
1704
+ return true;
1705
+ }
1706
+ else if (authProvider.isLoggedIn()) {
1707
+ return router.createUrlTree(['/']);
1708
+ }
1709
+ else {
1710
+ router.navigateByUrl(`/error/403`, { skipLocationChange: true });
1711
+ return false;
1712
+ }
1713
+ };
1714
+
1715
+ const authInterceptor = (req, next) => {
1716
+ const authConfig = inject(AUTH_CONFIG);
1717
+ const authProvider = inject(AuthProvider);
1718
+ const authReq = authProvider.addAuthenticationToken(req);
1719
+ return next(authReq).pipe(catchError((error) => {
1720
+ if (error instanceof HttpErrorResponse) {
1721
+ switch (error.status) {
1722
+ case 401:
1723
+ if (!req.url.includes(authConfig.auth.refreshTokenUrl)) {
1724
+ return authProvider.handle401Error(error, req, next);
1725
+ }
1726
+ else {
1727
+ authProvider.logout();
1728
+ return throwError(() => error);
1729
+ }
1730
+ default:
1731
+ return throwError(() => error);
1732
+ }
1733
+ }
1734
+ else {
1735
+ return throwError(() => error);
1736
+ }
1737
+ }));
1738
+ };
1739
+
1740
+ /*
1741
+ * Public API exports for utils library
1742
+ */
1743
+ // Services/Utils
1744
+
1745
+ /*
1746
+ * Public API Surface of utils
1747
+ */
1748
+
1004
1749
  /**
1005
1750
  * Generated bundle index. Do not edit.
1006
1751
  */
1007
1752
 
1008
- export { ArrayService, ColorService, ConfigDefaults, CsvConfigConsts, CsvService, DateService, FilePickerService, FilesService, GoogleTagManagerService, ObjectService, StorageService, StringService, getIdentificationType, identificationValidator };
1753
+ export { AUTH_CONFIG, ArrayUtil, AuthProvider, AuthService, CSV_CONFIG, ColorUtil, CsvExporter, DateUtil, FilePicker, GoogleTagManager, OPERATION_TYPE, ObjectUtil, Storage, StringUtil, authGuard, authInterceptor, getIdentificationType, identificationValidator, loginGuard, resetGuard };
1009
1754
  //# sourceMappingURL=factor_ec-utils.mjs.map