uni-manager 0.1.10 → 0.1.11

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,1184 +1,11 @@
1
- import { map, distinctUntilChanged, BehaviorSubject, switchMap, mergeMap, concatMap, exhaustMap, throwError, of, EMPTY, defer, from, tap, catchError, finalize, timer } from 'rxjs';
2
- import { UniToastManager as UniToastManager$1 } from 'uni-manager/toast';
3
- import isEqual from 'lodash-es/isEqual';
4
- import { UniErrorManager as UniErrorManager$1 } from 'uni-manager/error';
5
- import { UniHttpError, isHttpException } from 'uni-error/http';
6
- import { UniTypeDateManager as UniTypeDateManager$1 } from 'uni-manager/type';
7
- import { UniLocaleManager as UniLocaleManager$1 } from 'uni-manager/locale';
8
-
9
- class UniErrorManager {
10
- /* ------------------------------------------------------------------------------- */
11
- /* --------------------------------- Metodi: get --------------------------------- */
12
- /* ------------------------------------------------------------------------------- */
13
- /** Restituisce se lo store ha chiamate in attesa di risposta o meno */
14
- static get errors$() {
15
- return UniErrorManager.store$.pipe(map((x) => Array.from(x.entries(), ([id, error]) => ({ ...error, id }))), distinctUntilChanged((prev, curr) => {
16
- if (prev.length !== curr.length)
17
- return false;
18
- return prev.every((err, index) => err.id === curr[index].id && err.count === curr[index].count);
19
- }));
20
- }
21
- /* ------------------------------------------------------------------------------- */
22
- /* ------------------------------------ Store ------------------------------------ */
23
- /* ------------------------------------------------------------------------------- */
24
- /** Store privato (Subject) */
25
- static { this.store = new BehaviorSubject(new Map()); }
26
- /** Store pubblico (Observable) */
27
- static { this.store$ = UniErrorManager.store.asObservable(); }
28
- /** Ottiene lo stato attuale della Map senza dover sottoscrivere l'observable */
29
- static get currentValue() {
30
- return UniErrorManager.store.getValue();
31
- }
32
- /* ------------------------------------------------------------------------------- */
33
- /* -------------------------------- Metodi: store -------------------------------- */
34
- /* ------------------------------------------------------------------------------- */
35
- /**
36
- * Aggiunge o aggiorna un errore nello store.
37
- * Se l'errore esiste già (stesso ID), incrementa il contatore 'count' e aggiorna il timestamp.
38
- */
39
- static add(id, error) {
40
- // Recupera l'ultimo stato (Map)
41
- const oldMap = UniErrorManager.currentValue;
42
- // Tenta di recuperare l'oggetto corrente
43
- const oldItem = oldMap.get(id);
44
- // Crea il nuovo oggetto aggiornando contatore e portando il timestamp al momento attuale
45
- const newItemMap = {
46
- ...error,
47
- count: oldItem ? oldItem.count + 1 : 1,
48
- timestamp: new Date(),
49
- };
50
- // Crea una nuova istanza della Map
51
- const newMap = new Map(oldMap);
52
- newMap.set(id, newItemMap);
53
- // Aggiorna il nuovo stato notificando l'observer
54
- UniErrorManager.store.next(newMap);
55
- }
56
- /**
57
- * Rimuove un errore tramite ID
58
- */
59
- static remove(id) {
60
- // Recupera l'ultimo stato (Map)
61
- const oldMap = UniErrorManager.currentValue;
62
- // Controllo: se non è presente l'item allora termina
63
- if (!oldMap.has(id))
64
- return;
65
- // Crea una nuova istanza della Map
66
- const newMap = new Map(oldMap);
67
- newMap.delete(id);
68
- // Aggiorna il nuovo stato notificando l'observer
69
- UniErrorManager.store.next(newMap);
70
- }
71
- /**
72
- * Svuota completamente la lista degli errori
73
- */
74
- static removeAll() {
75
- // Crea una nuova istanza della Map
76
- const newMap = new Map();
77
- // Aggiorna il nuovo stato notificando l'observer
78
- UniErrorManager.store.next(newMap);
79
- }
80
- }
81
-
82
- class UniFileManager {
83
- /**
84
- * Apre un file (es. PDF, immagine) in una nuova scheda del browser
85
- * Il contenuto viene caricato all'interno di un iframe per garantire compatibilità di visualizzazione
86
- */
87
- static openInNewTab(data) {
88
- if (!data || !data.url)
89
- return;
90
- const newWindow = window.open('', '_blank');
91
- if (!newWindow)
92
- return;
93
- newWindow.document.title = data.name;
94
- // Crea l'iframe e lo aggiunge al body della nuova finestra
95
- const iframe = UniFileManager.createIframe(newWindow.document, data.url, '100%', '100%');
96
- newWindow.document.body.style.margin = '0';
97
- newWindow.document.body.append(iframe);
98
- // Rimuove l'oggetto URL dalla memoria dopo un tempo congruo per il caricamento
99
- setTimeout(() => URL.revokeObjectURL(data.url), 3000);
100
- }
101
- /**
102
- * Stampa un file aprendo il dialogo di stampa nativo del sistema
103
- * Utilizza un iframe invisibile per non interrompere la navigazione
104
- */
105
- static openInPrintPreview(data) {
106
- if (!data || !data.url)
107
- return;
108
- // Crea l'iframe come elemento fisso e nascosto (dimensioni zero)
109
- const iframe = UniFileManager.createIframe(document, data.url, '0', '0');
110
- iframe.style.position = 'fixed';
111
- iframe.style.bottom = '0';
112
- document.body.append(iframe);
113
- // Attende il completamento del caricamento del file nell'iframe
114
- iframe.addEventListener('load', () => {
115
- // Attesa supplementare per consentire il rendering del PDF
116
- setTimeout(() => {
117
- iframe.contentWindow?.focus();
118
- iframe.contentWindow?.print();
119
- // Rimozione dell'iframe dal DOM e pulizia della memoria
120
- setTimeout(() => {
121
- iframe.remove();
122
- URL.revokeObjectURL(data.url);
123
- }, 1000);
124
- }, 1000);
125
- });
126
- }
127
- /**
128
- * Scarica un file localmente sul dispositivo dell'utente
129
- */
130
- static download(data) {
131
- if (!data || !data.url)
132
- return;
133
- const link = document.createElement('a');
134
- link.href = data.url;
135
- // Specifica il nome con cui il file verrà salvato
136
- link.download = data.name;
137
- // Opzionale: assicura che il link non sia visibile
138
- link.style.display = 'none';
139
- document.body.append(link);
140
- link.click();
141
- // Pulizia: rimuove l'elemento e revoca l'URL
142
- setTimeout(() => {
143
- link.remove();
144
- URL.revokeObjectURL(data.url);
145
- }, 100);
146
- }
147
- /* ----------------------------- Utils ----------------------------- */
148
- static createIframe(doc, url, width, height) {
149
- const iframe = doc.createElement('iframe');
150
- iframe.src = url;
151
- iframe.style.width = width;
152
- iframe.style.height = height;
153
- iframe.style.border = 'none';
154
- return iframe;
155
- }
156
- }
157
-
158
- var MapOperator;
159
- (function (MapOperator) {
160
- /** Cancella la precedente richiesta, mantiene solo l’ultima */
161
- MapOperator[MapOperator["SWITCH_MAP"] = 0] = "SWITCH_MAP";
162
- /** Ignora nuovi valori finché la corrente non finisce */
163
- MapOperator[MapOperator["EXHAUST_MAP"] = 1] = "EXHAUST_MAP";
164
- /** Mette le richieste in coda, le esegue una alla volta */
165
- MapOperator[MapOperator["CONCAT_MAP"] = 2] = "CONCAT_MAP";
166
- /** Esegue tutte le richieste in parallelo, ordine non garantito */
167
- MapOperator[MapOperator["MERGE_MAP"] = 3] = "MERGE_MAP";
168
- })(MapOperator || (MapOperator = {}));
169
- var PollingErrorMode;
170
- (function (PollingErrorMode) {
171
- /** Salta questa iterazione emettendo un undefined. Il polling continua al tick successivo. */
172
- PollingErrorMode[PollingErrorMode["IGNORE"] = 0] = "IGNORE";
173
- /** Salta questa iterazione senza emettere valori. Il polling continua al tick successivo. */
174
- PollingErrorMode[PollingErrorMode["SKIP"] = 1] = "SKIP";
175
- /** Interrompe il polling propagando l'errore. */
176
- PollingErrorMode[PollingErrorMode["STOP"] = 2] = "STOP";
177
- /** Ignora l'errore, lo salva (es. per UI), e continua il polling. Emette `undefined`. */
178
- PollingErrorMode[PollingErrorMode["IGNORE_WITH_ERROR"] = 3] = "IGNORE_WITH_ERROR";
179
- })(PollingErrorMode || (PollingErrorMode = {}));
180
- var EmitValueMode;
181
- (function (EmitValueMode) {
182
- /** Aggiorna lo stato solo se arrivano dati nuovi (distinct) */
183
- EmitValueMode[EmitValueMode["ON_NEW_DATA"] = 0] = "ON_NEW_DATA";
184
- /** Aggiorna lo stato ad ogni chiamata, anche se i dati sono uguali */
185
- EmitValueMode[EmitValueMode["EVERY_TIME"] = 1] = "EVERY_TIME";
186
- })(EmitValueMode || (EmitValueMode = {}));
187
-
188
- function operatorHandler(operator) {
189
- // Gestione della concorrenza in base al parametro
190
- switch (operator) {
191
- case MapOperator.EXHAUST_MAP: {
192
- return (project) => exhaustMap(project);
193
- }
194
- case MapOperator.CONCAT_MAP: {
195
- return (project) => concatMap(project);
196
- }
197
- case MapOperator.MERGE_MAP: {
198
- return (project) => mergeMap(project);
199
- }
200
- case MapOperator.SWITCH_MAP: {
201
- return (project) => switchMap(project);
202
- }
203
- }
204
- }
205
- function errorHandler(ref, err, errorMode) {
206
- // Controllo: sia effettivamente un errore di tipo 'UniHttpError'
207
- if (err instanceof UniHttpError) {
208
- switch (errorMode) {
209
- case PollingErrorMode.IGNORE: {
210
- return of();
211
- }
212
- case PollingErrorMode.SKIP: {
213
- return EMPTY;
214
- }
215
- case PollingErrorMode.IGNORE_WITH_ERROR: {
216
- UniErrorManager$1.add(ref, err);
217
- return of();
218
- }
219
- case PollingErrorMode.STOP: {
220
- UniErrorManager$1.add(ref, err);
221
- return throwError(() => err);
222
- }
223
- }
224
- }
225
- else {
226
- return throwError(() => err);
227
- }
228
- }
229
-
230
- /* ------------------------------------------------------------------------------- */
231
- /* -------------------------------- Funzioni Core RxJS -------------------------- */
232
- /* ------------------------------------------------------------------------------- */
233
- /**
234
- * Gestisce l'esecuzione e il ciclo di vita di una singola richiesta HTTP.
235
- * Si occupa della registrazione della reference, della gestione dei loader, dei toast di successo e dell'intercettazione degli errori.
236
- */
237
- function http$(url, refType, config, promiseFactory) {
238
- /* Recupero configurazione */
239
- const { ref, toast, hasLoader } = config;
240
- return defer(() => {
241
- const http$ = from(promiseFactory());
242
- return http$.pipe(tap({
243
- subscribe: () => {
244
- /* Creazione reference */
245
- add(ref, url, refType);
246
- /* Incrementa per il loader */
247
- if (hasLoader !== false) {
248
- updateIsLoading(ref, 1);
249
- }
250
- },
251
- unsubscribe: () => {
252
- /* Rimozione reference */
253
- remove(ref);
254
- },
255
- next: (res) => {
256
- /* Mostra toast (se presente) */
257
- if (toast) {
258
- UniToastManager$1.showHttp(toast, res);
259
- }
260
- },
261
- }), catchError((err) => {
262
- // Aggiorna la ref nella map
263
- updateHasError(ref, true);
264
- /* Gestione errore */
265
- return errorHandler(ref, err, PollingErrorMode.STOP);
266
- }), finalize(() => {
267
- /* Decrementa per il loader */
268
- if (hasLoader !== false) {
269
- updateIsLoading(ref, -1);
270
- }
271
- }));
272
- });
273
- }
274
- /**
275
- * Avvia e coordina un ciclo di polling a intervalli regolari.
276
- * Gestisce la concorrenza tramite operatori RxJS configurabili, la rimozione dei popup, di errore nelle iterazioni successive e i comportamenti custom al primo avvio.
277
- */
278
- function httpPolling$(url, config, promiseFactory) {
279
- /* Recupero configurazione */
280
- const { interval, ref, operator = MapOperator.SWITCH_MAP, errorMode = PollingErrorMode.STOP, emitValueMode = EmitValueMode.ON_NEW_DATA, firstIteration, } = config;
281
- const timer$ = timer(0, interval);
282
- const source$ = timer$.pipe(tap({
283
- subscribe: () => {
284
- /* Creazione reference */
285
- add(ref, url, 'polling');
286
- },
287
- unsubscribe: () => {
288
- /* Rimozione reference */
289
- remove(ref);
290
- },
291
- }), operatorHandler(operator)((index) => {
292
- /* Incrementa per il loader */
293
- if (index === 0 && firstIteration?.hasLoader !== false) {
294
- updateIsLoading(ref, 1);
295
- }
296
- return defer(() => {
297
- const http$ = from(promiseFactory());
298
- return http$.pipe(tap((res) => {
299
- /* Meccanismo di ripristino automatico: se il polling torna in salute, cancella l'errore dallo store e nasconde i relativi messaggi a schermo */
300
- if (errorMode === PollingErrorMode.IGNORE_WITH_ERROR) {
301
- updateHasError(ref, false);
302
- UniErrorManager$1.remove(ref);
303
- }
304
- /* Prima risposta */
305
- if (index === 0 && firstIteration?.toast) {
306
- UniToastManager$1.showHttp(firstIteration.toast, res);
307
- }
308
- }), catchError((err) => {
309
- // Aggiorna la ref nella map
310
- updateHasError(ref, true);
311
- /* Gestione errore */
312
- return errorHandler(ref, err, errorMode);
313
- }), finalize(() => {
314
- /* Decrementa per il loader */
315
- if (index === 0 && firstIteration?.hasLoader !== false) {
316
- updateIsLoading(ref, -1);
317
- }
318
- }));
319
- });
320
- }));
321
- return emitValueMode === EmitValueMode.ON_NEW_DATA
322
- ? source$.pipe(distinctUntilChanged((prev, cur) => isEqual(prev, cur)))
323
- : source$;
324
- }
325
- /* ------------------------------------------------------------------------------- */
326
- /* ------------------------------------ Utils ------------------------------------ */
327
- /* ------------------------------------------------------------------------------- */
328
- /**
329
- * Aggiunge una nuova ref nello store solo se non è già presente.
330
- * Se l'ID esiste già, l'operazione viene interrotta per preservare il dato originale.
331
- */
332
- function add(id, url, type) {
333
- // Recupera l'ultimo stato (Map)
334
- const oldMap = UniHttpManager.currentValue;
335
- // Controllo: se è presente l'item allora termina
336
- if (oldMap.get(id))
337
- return;
338
- // Crea il nuovo oggetto
339
- const newItemMap = {
340
- type,
341
- lineId: undefined,
342
- url,
343
- hasError: false,
344
- pendingCount: 0,
345
- };
346
- // Crea una nuova istanza della Map
347
- const newMap = new Map(oldMap);
348
- newMap.set(id, newItemMap);
349
- // Aggiorna il nuovo stato notificando l'observer
350
- UniHttpManager.store.next(newMap);
351
- }
352
- /**
353
- * Rimuove un http ref tramite ID
354
- */
355
- function remove(id) {
356
- // Recupera l'ultimo stato (Map)
357
- const oldMap = UniHttpManager.currentValue;
358
- // Controllo: se non è presente l'item allora termina
359
- if (!oldMap.has(id))
360
- return;
361
- // Aggiorna store
362
- const newMap = new Map(oldMap);
363
- newMap.delete(id);
364
- UniHttpManager.store.next(newMap);
365
- }
366
- /**
367
- * Aggiorna il contatore delle chiamate pendenti per una specifica ref.
368
- * Incrementa o decrementa 'pendingCount' garantendo che non scenda mai sotto lo zero.
369
- * Se la ref non esiste, l'operazione viene ignorata.
370
- */
371
- function updateIsLoading(id, delta) {
372
- // Recupera l'ultimo stato (Map)
373
- const oldMap = UniHttpManager.currentValue;
374
- // Controllo: se non è presente l'item allora termina
375
- const oldItem = oldMap.get(id);
376
- if (!oldItem)
377
- return;
378
- // Aggiorna l'oggetto
379
- const newItemMap = {
380
- ...oldItem,
381
- pendingCount: Math.max(0, oldItem.pendingCount + delta),
382
- };
383
- // Crea una nuova istanza della Map
384
- const newMap = new Map(oldMap);
385
- newMap.set(id, newItemMap);
386
- // Aggiorna il nuovo stato notificando l'observer
387
- UniHttpManager.store.next(newMap);
388
- }
389
- /**
390
- * Aggiorna lo stato di errore per una specifica ref.
391
- * Se il valore di 'hasError' è identico a quello attuale o se la ref non esiste,
392
- * l'operazione viene interrotta per evitare aggiornamenti inutili.
393
- */
394
- function updateHasError(id, hasError) {
395
- // Recupera l'ultimo stato (Map)
396
- const oldMap = UniHttpManager.currentValue;
397
- // Controllo: se non è presente l'item allora termina
398
- const oldItem = oldMap.get(id);
399
- if (!oldItem)
400
- return;
401
- // Controllo: se con lo stesso valore, salta
402
- if (oldItem.hasError === hasError)
403
- return;
404
- // Aggiorna l'oggetto
405
- const newItemMap = { ...oldItem, hasError };
406
- // Crea una nuova istanza della Map
407
- const newMap = new Map(oldMap);
408
- newMap.set(id, newItemMap);
409
- // Aggiorna il nuovo stato notificando l'observer
410
- UniHttpManager.store.next(newMap);
411
- }
412
-
413
- async function execute(url, init) {
414
- try {
415
- /* Esecuzione della richiesta HTTP nativa */
416
- const res = await fetch(url, init);
417
- /* Gestione dello stato 204: assenza di contenuto legittima */
418
- if (res.status === 204) {
419
- return undefined;
420
- }
421
- /*Gestione ok HTTP: risposta ricevuta dal server con stato valido */
422
- if (res.ok) {
423
- return res;
424
- }
425
- /* Gestione Errore HTTP: risposta ricevuta dal server ma con stato non valido */
426
- let httpErrorPayload;
427
- try {
428
- /* Clonazione della risposta per l'ispezione del payload senza consumare lo stream originale */
429
- const resClone = res.clone();
430
- /* Recupero se è un json */
431
- const contentType = res.headers.get('content-type') ?? '';
432
- const isJson = contentType.startsWith('application/json');
433
- /* Estrazione del corpo dell'errore in base al formato rilevato */
434
- httpErrorPayload = isJson ? await resClone.json() : await resClone.text();
435
- }
436
- catch {
437
- /* Fallback in caso di fallimento della clonazione o del parsing del testo */
438
- httpErrorPayload = `Failed to parse error response body (Status: ${res.status})`;
439
- }
440
- // Controllo: se il payload estratto è di tipo HttpException (.NET)
441
- if (isHttpException(httpErrorPayload)) {
442
- // const type = httpErrorPayload.isBe ? 'be' : 'ice';
443
- throw new UniHttpError('be', res.status, url, httpErrorPayload);
444
- }
445
- // Fallback per errori di infrastruttura (es. pagine HTML di IIS/Nginx, 404, 502)
446
- const errorTracker = new Error(res.statusText || `HTTP Error ${res.status}`);
447
- const fallbackException = {
448
- message: typeof httpErrorPayload === 'string' && httpErrorPayload
449
- ? httpErrorPayload
450
- : errorTracker.message,
451
- type: 'InfrastructureError',
452
- stackTrace: errorTracker.stack ?? '',
453
- isBe: true,
454
- };
455
- throw new UniHttpError('server', res.status, url, fallbackException);
456
- }
457
- catch (error) {
458
- // Fallback per errori di rete locale (es. assenza di linea, DNS fallito)
459
- if (error instanceof TypeError) {
460
- const fallbackNetworkException = {
461
- message: error.message,
462
- type: error.name || 'NetworkError',
463
- stackTrace: error.stack ?? '',
464
- isBe: true,
465
- };
466
- throw new UniHttpError('network', -1, url, fallbackNetworkException);
467
- }
468
- // Rilancio diretto per istanze di UniHttpError o eccezioni già gestite
469
- throw error;
470
- }
471
- }
472
- async function executeHttp(url, init) {
473
- /* Esegue la chiamata HTTP e restituisce il corpo decodificato come JSON */
474
- const res = await execute(url, init);
475
- if (!res)
476
- return undefined;
477
- /* Estrae il contenuto come testo */
478
- const text = await res.text();
479
- /* Controllo: se il testo è vuoto (''), ritorna undefined */
480
- if (!text || text.trim().length === 0) {
481
- return undefined;
482
- }
483
- /* Parsa manualmente il testo, dato che lo stream è stato già letto */
484
- return JSON.parse(text);
485
- }
486
- async function executeBlob(url, init) {
487
- /* Esegue la chiamata HTTP */
488
- const res = await execute(url, init);
489
- if (!res)
490
- return undefined;
491
- /* Estrae il contenuto come Blob direttamente */
492
- const blob = await res.blob();
493
- /* Controllo: se il blob è vuoto (0 byte), ritorna undefined */
494
- if (blob.size === 0) {
495
- return undefined;
496
- }
497
- /* Estrae il nome del file dall'header */
498
- const disposition = res.headers.get('Content-Disposition');
499
- let fileName = 'download.pdf'; // Fallback di default
500
- if (disposition) {
501
- const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
502
- if (!!matches && matches[1]) {
503
- fileName = matches[1].replaceAll(/['"]/g, '');
504
- }
505
- }
506
- /* Genera l'URL temporaneo dal blob */
507
- const fileUrl = URL.createObjectURL(blob);
508
- return { url: fileUrl, name: fileName };
509
- }
510
-
511
- /**
512
- * Costruisce un URL completo per l'API partendo dai parametri di configurazione.
513
- */
514
- function getUrl(hostname, port, config) {
515
- const { pathParams, path, hasApiPrefix } = config;
516
- // Costruzione url
517
- const cleanPath = path.replaceAll(/^\/+|\/+$/g, '');
518
- const segments = [hasApiPrefix === false ? '' : 'api', cleanPath].filter(Boolean);
519
- const pathFixed = '/' + segments.join('/');
520
- const url = new URL(pathFixed, `http://${hostname}:${port}`);
521
- if (pathParams) {
522
- // Regex per intercettare stringhe in formato ISO string
523
- const isoDateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$/;
524
- for (const [key, rawValue] of Object.entries(pathParams)) {
525
- if (rawValue === undefined || rawValue === null) {
526
- continue;
527
- }
528
- // Conversione in array per usare un solo ciclo
529
- const valuesArray = Array.isArray(rawValue) ? rawValue : [rawValue];
530
- for (const item of valuesArray) {
531
- if (item === undefined || item === null) {
532
- continue;
533
- }
534
- let formattedValue;
535
- // Caso: Date nativo
536
- if (item instanceof Date) {
537
- formattedValue = UniTypeDateManager$1.toYYYYMMDD(item);
538
- }
539
- // Caso: Date formato ISO string
540
- else if (typeof item === 'string' && isoDateRegex.test(item)) {
541
- const parsedDate = new Date(item);
542
- formattedValue = Number.isNaN(parsedDate.getTime())
543
- ? item
544
- : UniTypeDateManager$1.toYYYYMMDD(parsedDate);
545
- }
546
- // Default
547
- else {
548
- formattedValue = String(item);
549
- }
550
- url.searchParams.append(key, formattedValue);
551
- }
552
- }
553
- }
554
- return url;
555
- }
556
- /**
557
- * Normalizza i valori del body cosi da sistemare le incongruenze tra ui e db
558
- */
559
- function normalizeHttpBody(body, isRoot = true) {
560
- // PRIMITIVI GENERICI
561
- // Tipi gestiti: null, undefined, number, boolean, symbol, bigint, string
562
- if (body === null || typeof body !== 'object') {
563
- // Sotto-gestione specifica per il tipo: string
564
- if (typeof body === 'string') {
565
- return body.trim();
566
- }
567
- return body;
568
- }
569
- // DATE
570
- // Tipi gestiti: Date
571
- // Formattazione esplicita manuale in YYYY-MM-DD
572
- if (body instanceof Date) {
573
- const year = body.getFullYear();
574
- const month = String(body.getMonth() + 1).padStart(2, '0');
575
- const day = String(body.getDate()).padStart(2, '0');
576
- return `${year}-${month}-${day}`;
577
- }
578
- // STRUTTURE DATI ITERABILI
579
- // Tipi gestiti: Array (qualsiasi array, es. string[], number[], Object[])
580
- // Se è un array, viene mappato ricorsivamente ogni singolo elemento al suo interno (passando isRoot = false perché gli elementi dell'array non sono l'oggetto root principale)
581
- if (Array.isArray(body)) {
582
- return body.map((item) => normalizeHttpBody(item, false));
583
- }
584
- // OGGETTI
585
- // Tipi gestiti: Record<string, any>, generici oggetti JavaScript ({ })
586
- // Rimuove chiave 'id' al primo livello e prosegue ricorsivamente
587
- const entries = Object.entries(body)
588
- .filter(([key]) => !(isRoot && key.toLowerCase() === 'id'))
589
- .map(([key, value]) => [key, normalizeHttpBody(value, false)]);
590
- // Ricostruisce l'oggetto normalizzato
591
- return Object.fromEntries(entries);
592
- }
593
-
594
- const CONFIG_TOAST_DEFAULT = {
595
- type: 'success',
596
- label: 'OperationCompleted',
597
- };
598
- class UniHttpManager {
599
- /* ------------------------------------------------------------------------------- */
600
- /* --------------------------------- Metodi: get --------------------------------- */
601
- /* ------------------------------------------------------------------------------- */
602
- /** Restituisce se lo store ha chiamate in attesa di risposta o meno */
603
- static get hasWaitingRequests$() {
604
- return UniHttpManager.store$.pipe(map((requestMap) => {
605
- for (const request of requestMap.values()) {
606
- if (request.pendingCount > 0)
607
- return true;
608
- }
609
- return false;
610
- }), distinctUntilChanged());
611
- }
612
- /* ------------------------------------------------------------------------------- */
613
- /* ------------------------------------ Store ------------------------------------ */
614
- /* ------------------------------------------------------------------------------- */
615
- /** Store privato (Subject) */
616
- static { this.store = new BehaviorSubject(new Map()); }
617
- /** Store pubblico (Observable) */
618
- static { this.store$ = UniHttpManager.store.asObservable(); }
619
- /** Ottiene lo stato attuale della Map senza dover sottoscrivere l'observable */
620
- static get currentValue() {
621
- return UniHttpManager.store.getValue();
622
- }
623
- /* ------------------------------------------------------------------------------- */
624
- /* -------------------------------- Metodi: setup -------------------------------- */
625
- /* ------------------------------------------------------------------------------- */
626
- /**
627
- * Inizializza la configurazione di rete del manager.
628
- * Deve essere chiamato prima di effettuare qualsiasi richiesta HTTP.
629
- */
630
- static setup(hostname, port) {
631
- UniHttpManager.hostname = hostname;
632
- UniHttpManager.port = port;
633
- console.log(UniHttpManager.hostname, UniHttpManager.port);
634
- }
635
- /* ------------------------------------------------------------------------------- */
636
- /* -------------------------------- Metodi: CRUD --------------------------------- */
637
- /* ------------------------------------------------------------------------------- */
638
- /*
639
- * Esegue una singola richiesta HTTP GET.
640
- * Utilizza il path configurato per costruire l'URL completo e restituisce un Observable del risultato.
641
- */
642
- static read$(config) {
643
- /* Config */
644
- const initCustom = {
645
- ...config.init,
646
- method: 'GET',
647
- };
648
- /* API */
649
- console.log('read$', UniHttpManager.hostname, UniHttpManager.port);
650
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
651
- return http$(url, 'one', config, () => executeHttp(url, initCustom)).pipe(tap((res) => {
652
- if (Array.isArray(res) && config.toast === undefined) {
653
- UniToastManager$1.show({
654
- label: 'ItemsFound',
655
- params: { count: res.length },
656
- });
657
- }
658
- }));
659
- }
660
- /**
661
- * Esegue una richiesta HTTP POST inviando un payload JSON nel corpo della richiesta.
662
- * Imposta automaticamente l'header 'Content-Type' come 'application/json'.
663
- */
664
- static create$(config, body) {
665
- /* Config */
666
- const initCustom = {
667
- ...config.init,
668
- method: 'POST',
669
- headers: {
670
- 'Content-Type': 'application/json',
671
- ...config.init?.headers,
672
- },
673
- body: JSON.stringify(normalizeHttpBody(body)),
674
- };
675
- /* Config custom (toast di default) */
676
- const configCustom = {
677
- ...config,
678
- toast: config.hasToast === false ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
679
- };
680
- /* API */
681
- console.log('create$', UniHttpManager.hostname, UniHttpManager.port);
682
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
683
- return http$(url, 'one', configCustom, () => executeHttp(url, initCustom));
684
- }
685
- /**
686
- * Esegue una singola richiesta HTTP PUT per aggiornare una risorsa esistente.
687
- */
688
- static update$(config, body) {
689
- /* Config */
690
- const initCustom = { ...config.init, method: 'PUT' };
691
- if (body !== undefined) {
692
- initCustom.headers = {
693
- 'Content-Type': 'application/json',
694
- ...config.init?.headers,
695
- };
696
- initCustom.body = JSON.stringify(normalizeHttpBody(body));
697
- }
698
- /* Config custom (toast di default) */
699
- const configCustom = {
700
- ...config,
701
- toast: config.hasToast === false ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
702
- };
703
- /* API */
704
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
705
- return http$(url, 'one', configCustom, () => executeHttp(url, initCustom));
706
- }
707
- /**
708
- * Esegue una richiesta HTTP DELETE per rimuovere una risorsa.
709
- * Utilizza il path configurato per costruire l'URL completo e restituisce un Observable del risultato.
710
- */
711
- static delete$(config) {
712
- /* Config */
713
- const initCustom = {
714
- ...config.init,
715
- method: 'DELETE',
716
- };
717
- /* Config custom (toast di default) */
718
- const configCustom = {
719
- ...config,
720
- toast: config.hasToast === false ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
721
- };
722
- /* API */
723
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
724
- return http$(url, 'one', configCustom, () => executeHttp(url, initCustom));
725
- }
726
- /* ------------------------------------------------------------------------------- */
727
- /* -------------------------- Metodi: CRUD file/image ---------------------------- */
728
- /* ------------------------------------------------------------------------------- */
729
- /**
730
- * Recupera un'immagine tramite una richiesta GET e la trasforma in un formato gestibile (es. Blob o Base64).
731
- * Delega la logica di conversione alla funzione executeImage.
732
- */
733
- static readImage$(config) {
734
- /* Config */
735
- const initCustom = {
736
- ...config.init,
737
- method: 'GET',
738
- };
739
- /* API */
740
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
741
- return http$(url, 'image', config, () => executeBlob(url, initCustom));
742
- }
743
- /**
744
- * Recupera un file (es. PDF) tramite una richiesta GET e restituisce un Object URL temporaneo.
745
- * Delega la logica di conversione alla funzione executeFile.
746
- */
747
- static readFile$(config) {
748
- /* Config */
749
- const initCustom = {
750
- ...config.init,
751
- method: 'GET',
752
- };
753
- /* Config custom (toast di default) */
754
- const configCustom = {
755
- ...config,
756
- toast: config.hasToast === false ? undefined : (config.toast ?? CONFIG_TOAST_DEFAULT),
757
- };
758
- /* API */
759
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
760
- return http$(url, 'file', configCustom, () => executeBlob(url, initCustom));
761
- }
762
- /* ------------------------------------------------------------------------------- */
763
- /* ---------------------------- Metodi: CRUD polling ----------------------------- */
764
- /* ------------------------------------------------------------------------------- */
765
- /**
766
- * Avvia un ciclo di polling basato su richieste GET.
767
- * Continua a emettere valori in base alla configurazione di intervallo definita in HttpConfigPolling.
768
- */
769
- static readPolling$(config) {
770
- /* Config */
771
- const initCustom = {
772
- ...config.init,
773
- method: 'GET',
774
- };
775
- /* API */
776
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
777
- return httpPolling$(url, config, () => executeHttp(url, initCustom));
778
- }
779
- /**
780
- * Avvia un ciclo di polling basato su richieste POST.
781
- * Invia il body specificato a ogni iterazione del ciclo.
782
- */
783
- static createPolling$(config, body) {
784
- /* Config */
785
- const initCustom = {
786
- ...config.init,
787
- method: 'POST',
788
- headers: { 'Content-Type': 'application/json' },
789
- body: JSON.stringify(body),
790
- };
791
- /* API */
792
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
793
- return httpPolling$(url, config, () => executeHttp(url, initCustom));
794
- }
795
- /**
796
- * Avvia un ciclo di polling basato su richieste PUT.
797
- */
798
- static updatePolling$(config, body) {
799
- /* Config */
800
- const initCustom = {
801
- ...config.init,
802
- method: 'PUT',
803
- };
804
- if (body !== undefined) {
805
- initCustom.headers = {
806
- ...initCustom.headers,
807
- 'Content-Type': 'application/json',
808
- };
809
- initCustom.body = JSON.stringify(body);
810
- }
811
- /* API */
812
- const url = getUrl(UniHttpManager.hostname, UniHttpManager.port, config);
813
- return httpPolling$(url, config, () => executeHttp(url, initCustom));
814
- }
815
- }
816
-
817
- class UniLocaleManager {
818
- /* ------------------------------------------------------------------------------- */
819
- /* ----------------------------------- Config ------------------------------------ */
820
- /* ------------------------------------------------------------------------------- */
821
- /** Codice lingua corrente (es. 'it-IT', 'en-US') usato per formattare date e numeri */
822
- static { this._locale = navigator.language ?? 'en-US'; }
823
- /** Elenco dei codici lingua supportati dall'applicazione (es. ['it-IT', 'en-US']) */
824
- static { this._localesSupported = []; }
825
- /** Prefisso globale applicato a tutte le chiavi di traduzione (es. 'APP_') */
826
- static { this._prefix = undefined; }
827
- /* ------------------------------------------------------------------------------- */
828
- /* --------------------------------- Metodi: get --------------------------------- */
829
- /* ------------------------------------------------------------------------------- */
830
- /** Restituisce il codice lingua corrente */
831
- static get locale() {
832
- return UniLocaleManager._locale;
833
- }
834
- /** Restituisce l'array contenente tutti i codici locale supportati.*/
835
- static get localesSupported() {
836
- return UniLocaleManager._localesSupported;
837
- }
838
- /** Restituisce solo la lingua (es. 'it') */
839
- static get language() {
840
- return UniLocaleManager._locale.split('-')[0];
841
- }
842
- /** Restituisce solo il paese (es. 'IT') */
843
- static get region() {
844
- const parts = UniLocaleManager._locale.split('-');
845
- return parts.length > 1 ? parts[1] : '';
846
- }
847
- /* ------------------------------------------------------------------------------- */
848
- /* ------------------------------------ Store ------------------------------------ */
849
- /* ------------------------------------------------------------------------------- */
850
- /** Store privato (Subject) */
851
- static { this.store = new BehaviorSubject({}); }
852
- /** Store pubblico (Observable) */
853
- static { this.store$ = UniLocaleManager.store.asObservable(); }
854
- /** Ottiene il dizionario attuale senza sottoscrizione */
855
- static get currentValue() {
856
- return UniLocaleManager.store.getValue();
857
- }
858
- /* ------------------------------------------------------------------------------- */
859
- /* -------------------------------- Metodi: setup -------------------------------- */
860
- /* ------------------------------------------------------------------------------- */
861
- /**
862
- * Inizializza la configurazione di rete del manager.
863
- * Deve essere chiamato prima di effettuare qualsiasi richiesta HTTP.
864
- */
865
- static setup(locale, prefix) {
866
- UniLocaleManager._locale = locale ?? 'en-US';
867
- UniLocaleManager._prefix = prefix;
868
- }
869
- /** Imposta l'elenco dei codici lingua supportati dall'applicazione */
870
- static setLocalesSupported(locales) {
871
- UniLocaleManager._localesSupported = locales ?? [];
872
- }
873
- /**
874
- * Aggiorna lo store locale con il dizionario delle traduzioni fornito.
875
- * Se viene passato undefined, lo store viene inizializzato come oggetto vuoto.
876
- */
877
- static setTranslations(translations) {
878
- UniLocaleManager.store.next(translations ?? {});
879
- }
880
- /* ------------------------------------------------------------------------------- */
881
- /* ----------------------------- Metodi: traduzioni ------------------------------ */
882
- /* ------------------------------------------------------------------------------- */
883
- /**
884
- * Traduce una label in base al dizionario caricato.
885
- * Gestisce la composizione della chiave, i parametri dinamici (interpolazione) e il fallback.
886
- */
887
- static translate(key, prefix = 'lbl', params) {
888
- if (!key)
889
- return '-';
890
- // Costruzione chiave: prefissoGlobale + prefissoLocale + LabelConInizialeMaiuscola
891
- const keyParts = key.trim().split(/(?=[A-Z])/);
892
- const rootKeyParts = keyParts.filter((p) => p.toLowerCase() !== prefix.toLowerCase());
893
- const cleanKey = rootKeyParts.length > 0 ? rootKeyParts.join('') : undefined;
894
- const capitalizedKey = cleanKey ? cleanKey.charAt(0).toUpperCase() + cleanKey.slice(1) : '';
895
- const finalKey = `${UniLocaleManager._prefix ?? ''}${prefix}${capitalizedKey}`;
896
- // Cerca la chiave in minuscolo (standardizzazione)
897
- let translation = UniLocaleManager.currentValue?.[finalKey.toLowerCase()];
898
- // Log e fallback se la traduzione manca
899
- if (!translation) {
900
- console.warn(`Translation missing for key: ${finalKey}`);
901
- return `🔑 ${finalKey}`;
902
- }
903
- // Interpolazione variabili
904
- if (params) {
905
- for (const [key, value] of Object.entries(params)) {
906
- const displayValue = value instanceof Date
907
- ? value.toLocaleDateString(UniLocaleManager._locale)
908
- : String(value);
909
- translation = translation.replaceAll(`{{${key}}}`, displayValue);
910
- }
911
- }
912
- return translation;
913
- }
914
- /**
915
- * Traduce una stringa che contiene parametri separati da un carattere specifico.
916
- * Supporta ora un numero arbitrario di parametri in formato "label/param1/param2"
917
- * o semplicemente "label".
918
- */
919
- static translateInlineParams(keyWithParams, splitChar) {
920
- if (!keyWithParams)
921
- return '-';
922
- const [label, ...params] = keyWithParams.split(splitChar);
923
- // Se non ci sono parametri, esegui una traduzione semplice
924
- if (params.length === 0) {
925
- return UniLocaleManager.translate(label);
926
- }
927
- // Mappa i parametri in un oggetto di interpolazione: { inlineParam0: val, inlineParam1: val, ... }
928
- const interpolationParameters = {};
929
- for (const [index, val] of params.entries()) {
930
- interpolationParameters[`param${index}`] = val;
931
- }
932
- return UniLocaleManager.translate(label, 'lbl', interpolationParameters);
933
- }
934
- /* ------------------------------------------------------------------------------- */
935
- /* ------------------------------- Metodi: numeri -------------------------------- */
936
- /* ------------------------------------------------------------------------------- */
937
- /**
938
- * Converte un valore numerico in una stringa formattata secondo il locale impostato.
939
- * Disabilita i separatori delle migliaia e forza un numero fisso di decimali.
940
- */
941
- static toStringNumber(value, decimal) {
942
- return new Intl.NumberFormat(UniLocaleManager._locale, {
943
- useGrouping: true,
944
- minimumFractionDigits: decimal,
945
- maximumFractionDigits: decimal,
946
- numberingSystem: 'latn',
947
- }).format(value);
948
- }
949
- /* ------------------------------------------------------------------------------- */
950
- /* -------------------------------- Metodi: date --------------------------------- */
951
- /* ------------------------------------------------------------------------------- */
952
- /**
953
- * Formatta una data o una stringa in base al locale corrente.
954
- * Gestisce tre modalità predefinite (date, time, full) e accetta opzioni personalizzate Intl.
955
- */
956
- static toDate(date, mode = 'full', force) {
957
- const dt = new Date(date);
958
- // Controllo: validità data
959
- if (Number.isNaN(dt.getTime())) {
960
- console.error(`[UniLocaleManager] Data non valida fornita a formatDateTime:`, date);
961
- return '';
962
- }
963
- // Controllo: locale da forzare
964
- const locale = force && UniLocaleManager._locale.startsWith(force.oldLang)
965
- ? force.newLocale
966
- : UniLocaleManager._locale;
967
- switch (mode) {
968
- case 'date': {
969
- return dt.toLocaleDateString(locale);
970
- }
971
- case 'time': {
972
- return dt.toLocaleTimeString(locale);
973
- }
974
- case 'full': {
975
- return dt.toLocaleString(locale);
976
- }
977
- }
978
- }
979
- }
980
-
981
- /* eslint-disable @typescript-eslint/no-explicit-any */
982
- class UniToastManager {
983
- /* ------------------------------------------------------------------------------- */
984
- /* -------------------------------- Metodi: setup -------------------------------- */
985
- /* ------------------------------------------------------------------------------- */
986
- /**
987
- * Inizializza il manager con una serie di operazioni
988
- */
989
- static setup(operations) {
990
- UniToastManager.manager = operations;
991
- }
992
- /* ------------------------------------------------------------------------------- */
993
- /* -------------------------------- Metodi: show --------------------------------- */
994
- /* ------------------------------------------------------------------------------- */
995
- /**
996
- * Mostra un toast di successo basato su una risposta HTTP e una label di traduzione.
997
- */
998
- static show(config) {
999
- /* Messaggio tradotto */
1000
- const msg = UniLocaleManager$1.translate(config.label, 'toast', config.params);
1001
- const msgFixed = `${config.prefix ?? ''}${msg}${config.suffix ?? ''}`;
1002
- UniToastManager.manager[config.type ?? 'info'](msgFixed, config);
1003
- }
1004
- /**
1005
- * Mostra un toast di successo basato su una risposta HTTP e una label di traduzione.
1006
- */
1007
- static showHttp(config, res) {
1008
- const { params, resParams, formatters } = config;
1009
- /* Parametri statici di base */
1010
- const allParams = { ...params };
1011
- if (res !== undefined && res !== null) {
1012
- /* Se è un array aggiunge il parametro 'count' con la lunghezza dell'array */
1013
- if (Array.isArray(res)) {
1014
- allParams['count'] = res.length;
1015
- }
1016
- else if (typeof res === 'object' && resParams?.length) {
1017
- // Se la proprietà NON esiste nella risposta, allora salta
1018
- for (const resParam of resParams ?? []) {
1019
- if (!(resParam in res))
1020
- continue;
1021
- // Se esiste un formatter per questa chiave lo si usa, altrimenti valore grezzo
1022
- const rawValue = res[resParam];
1023
- allParams[resParam] = formatters?.[resParam] ? formatters[resParam](rawValue) : rawValue;
1024
- }
1025
- }
1026
- }
1027
- /* Messaggio tradotto */
1028
- UniToastManager.show({ ...config, params: allParams });
1029
- }
1030
- }
1031
-
1032
- /**
1033
- * Classe di utilità per la gestione e formattazione delle date.
1034
- * Fornisce metodi per convertire in modo sicuro valori di tipo Date, stringa o null in formati standardizzati.
1035
- */
1036
- class UniTypeDateManager {
1037
- static toYYYYMMDD(date) {
1038
- if (!date)
1039
- return '';
1040
- const d = new Date(date);
1041
- if (Number.isNaN(d.getTime()))
1042
- return '';
1043
- const year = d.getFullYear();
1044
- const month = String(d.getMonth() + 1).padStart(2, '0');
1045
- const day = String(d.getDate()).padStart(2, '0');
1046
- return `${year}-${month}-${day}`;
1047
- }
1048
- }
1049
-
1050
- /**
1051
- * Utility per la gestione e formattazione di valori numerici.
1052
- */
1053
- class UniTypeNumberManager {
1054
- /** Applica il padding a un numero o una stringa */
1055
- static toPad(value, count, character = '0') {
1056
- // Se il valore è null/undefined, riempiamo l'intero campo con il carattere scelto
1057
- if (value === null || value === undefined) {
1058
- return ''.padStart(count, character);
1059
- }
1060
- // Convertiamo in stringa e applichiamo il padding
1061
- return value.toString().padStart(count, character);
1062
- }
1063
- /** Formatta un numero in una versione leggibile (es: 1000 -> 1K, 1000000 -> 1M) */
1064
- static toTruncateAndAdUdm(value, decimalDigits = 0, maxIntegerDigits = 3) {
1065
- // Nessun limite impostato o numero inferiore alla soglia definita
1066
- if (Math.abs(value) < Math.pow(10, maxIntegerDigits)) {
1067
- return value.toFixed(decimalDigits);
1068
- }
1069
- // Definizione delle scale di riduzione per grandi numeri
1070
- const scales = [
1071
- { threshold: 1e12, suffix: 'T' }, // Trilioni
1072
- { threshold: 1e9, suffix: 'B' }, // Miliardi
1073
- { threshold: 1e6, suffix: 'M' }, // Milioni
1074
- { threshold: 1e3, suffix: 'K' }, // Migliaia
1075
- ];
1076
- // Itera dalla scala più grande alla più piccola per trovare la soglia corretta
1077
- for (const { threshold, suffix } of scales) {
1078
- if (Math.abs(value) >= threshold) {
1079
- const reduced = value / threshold;
1080
- // parseFloat(toFixed()) rimuove gli zeri decimali superflui (es: 1.50 -> 1.5)
1081
- // aggiungendo poi il relativo suffisso (K, M, B, T)
1082
- return Number.parseFloat(reduced.toFixed(decimalDigits)).toString() + suffix;
1083
- }
1084
- }
1085
- // Fallback: se il numero è grande ma non rientra nelle scale (caso raro con la logica attuale)
1086
- return value.toFixed(decimalDigits);
1087
- }
1088
- }
1089
-
1090
- /**
1091
- * Utility per la manipolazione di stringhe
1092
- */
1093
- class UniTypeStringManager {
1094
- /** Converte una stringa in formato lblPascalCase (es. "user_id" -> "lblUserId") */
1095
- static toLabelize(key, prefix = 'lbl') {
1096
- // Se vuoto restituisce vuoto
1097
- if (!key)
1098
- return '';
1099
- // Pulizia chiave
1100
- const fixedKey = key.trim();
1101
- // Unisce il prefisso
1102
- return prefix + UniTypeStringManager.toPascalCase(fixedKey);
1103
- }
1104
- /** Converte una stringa in PascalCase (es. "user_id" -> "UserId") */
1105
- static toPascalCase(key) {
1106
- // Se vuoto restituisce vuoto
1107
- if (!key)
1108
- return '';
1109
- // Pulizia chiave
1110
- const fixedKey = key.trim();
1111
- // Normalizza e pulisce
1112
- const parts = UniTypeStringManager.splitString(fixedKey);
1113
- if (parts.length === 0)
1114
- return '';
1115
- // Converte in PascalCase
1116
- const pascalCased = UniTypeStringManager.toPascalCaseParts(parts);
1117
- return pascalCased;
1118
- }
1119
- /** Converte una stringa in camelCase (es. "user_id" -> "userId") */
1120
- static toCamelCase(key) {
1121
- // Se vuoto restituisce vuoto
1122
- if (!key)
1123
- return '';
1124
- // Pulizia chiave
1125
- const fixedKey = key.trim();
1126
- // Normalizza e pulisce
1127
- const parts = UniTypeStringManager.splitString(fixedKey);
1128
- if (parts.length === 0)
1129
- return '';
1130
- // La prima parola resta minuscola, le successive PascalCase
1131
- const first = parts[0].toLowerCase();
1132
- const rest = parts.slice(1);
1133
- return first + UniTypeStringManager.toPascalCaseParts(rest);
1134
- }
1135
- /** Capitalizza solo la prima lettera della stringa */
1136
- static toCapitalize(key) {
1137
- // Se vuoto restituisce vuoto
1138
- if (!key)
1139
- return '';
1140
- // Pulizia chiave
1141
- const fixedKey = key.trim();
1142
- return fixedKey.charAt(0).toUpperCase() + fixedKey.slice(1);
1143
- }
1144
- /** Sostituisce sotto-stringhe all'interno della stringa */
1145
- static toReplace(key, values) {
1146
- // Se vuoto restituisce vuoto
1147
- if (!key)
1148
- return '';
1149
- // Pulizia chiave
1150
- let fixedKey = key.trim();
1151
- for (const value of values) {
1152
- fixedKey = fixedKey.replaceAll(value.searchValue, value.replaceValue);
1153
- }
1154
- return fixedKey;
1155
- }
1156
- /* ----------------- Utils ----------------- */
1157
- static splitString(key) {
1158
- return (key
1159
- // Pulisce eventuali caratteri di separazione rimasti in testa (es. "_tab_name" -> "tab_name")
1160
- .replace(/^[-_\s]+/, '')
1161
- // Isola il CamelCase inserendo un underscore tra minuscole e maiuscole (es. "userId" -> "user_Id")
1162
- .replaceAll(/([a-z])([A-Z])/g, '$1_$2')
1163
- // Isola gli acronimi attaccati a parole Normali (es. "VARAna" -> "VAR_Ana" grazie alla minuscola "na")
1164
- .replaceAll(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
1165
- // Applica il taglio definitivo usando come riferimento i trattini, gli underscore e gli spazi
1166
- .split(/[-_\s]+/)
1167
- // Rimuove dall'array finale eventuali stringhe vuote generate da separatori consecutivi
1168
- .filter(Boolean));
1169
- }
1170
- static toPascalCaseParts(parts) {
1171
- return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
1172
- }
1173
- }
1174
-
1175
1
  /*
1176
2
  * Public API Surface of uni-manager
1177
3
  */
4
+ const libraryName = 'uni-manager';
1178
5
 
1179
6
  /**
1180
7
  * Generated bundle index. Do not edit.
1181
8
  */
1182
9
 
1183
- export { EmitValueMode, MapOperator, PollingErrorMode, UniErrorManager, UniFileManager, UniHttpManager, UniLocaleManager, UniToastManager, UniTypeDateManager, UniTypeNumberManager, UniTypeStringManager };
10
+ export { libraryName };
1184
11
  //# sourceMappingURL=uni-manager.mjs.map