@quanticdigit/web-errors 1.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.
package/LICENSE.txt ADDED
@@ -0,0 +1,47 @@
1
+ COMMERCIAL SOFTWARE LICENSE AGREEMENT
2
+ Quantic Digit S.R.L. — Restricted Use License
3
+ Version 1.0 — © 2025 Quantic Digit S.R.L. All rights reserved.
4
+
5
+ IMPORTANT – READ CAREFULLY:
6
+ This Software (the “Software”) is provided under license, not sold.
7
+ By installing, copying or using the Software, you agree to the following terms.
8
+
9
+ 1. GRANT OF LICENSE
10
+ Quantic Digit S.R.L. (“the Author”) grants you a non-exclusive, non-transferable license to use the Software exclusively for:
11
+ - personal learning and experimentation,
12
+ - educational or training activities,
13
+ - software development, prototyping, or testing not intended for commercial release.
14
+
15
+ 2. PROHIBITED USES
16
+ Unless expressly authorized in writing by Quantic Digit S.R.L., it is strictly forbidden to:
17
+ - use the Software in any commercial activity;
18
+ - integrate the Software into products or services intended for end customers;
19
+ - deploy or distribute the Software within a company, organization, or enterprise environment;
20
+ - use the Software in applications, systems or processes with any revenue, profit, or commercial purpose;
21
+ - redistribute, sublicense, sell, rent, lease, or otherwise transfer the Software to third parties.
22
+
23
+ “Commercial use” includes, but is not limited to:
24
+ - internal business use by companies, organizations or professionals,
25
+ - inclusion in paid services or products,
26
+ - use within customer projects, consultancy, or software delivered to clients,
27
+ - any use that yields direct or indirect economic advantage.
28
+
29
+ 3. OWNERSHIP
30
+ The Software is licensed, not sold.
31
+ All intellectual property rights remain the sole property of Quantic Digit S.R.L.
32
+
33
+ 4. WARRANTY DISCLAIMER
34
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT ANY WARRANTY OF ANY KIND.
35
+ The Author disclaims all implied warranties, including merchantability, fitness for a particular purpose and non-infringement.
36
+
37
+ 5. LIMITATION OF LIABILITY
38
+ In no event shall Quantic Digit S.R.L. be liable for any damages, including direct, indirect, incidental, special or consequential damages arising from the use of the Software.
39
+
40
+ 6. TERMINATION
41
+ Any violation of this License immediately terminates your right to use the Software.
42
+ Upon termination, you must cease all use and destroy all copies of the Software.
43
+
44
+ 7. GOVERNING LAW
45
+ This Agreement is governed by the laws of Italy, unless otherwise agreed in a written contract with Quantic Digit S.R.L.
46
+
47
+ © 2025 Quantic Digit S.R.L. — All rights reserved.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # WebErrors
2
+
3
+ This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
8
+
9
+ ```bash
10
+ ng generate component component-name
11
+ ```
12
+
13
+ For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
+
15
+ ```bash
16
+ ng generate --help
17
+ ```
18
+
19
+ ## Building
20
+
21
+ To build the library, run:
22
+
23
+ ```bash
24
+ ng build web-errors
25
+ ```
26
+
27
+ This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
28
+
29
+ ### Publishing the Library
30
+
31
+ Once the project is built, you can publish your library by following these steps:
32
+
33
+ 1. Navigate to the `dist` directory:
34
+
35
+ ```bash
36
+ cd dist/web-errors
37
+ ```
38
+
39
+ 2. Run the `npm publish` command to publish your library to the npm registry:
40
+ ```bash
41
+ npm publish
42
+ ```
43
+
44
+ ## Running unit tests
45
+
46
+ To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
47
+
48
+ ```bash
49
+ ng test
50
+ ```
51
+
52
+ ## Running end-to-end tests
53
+
54
+ For end-to-end (e2e) testing, run:
55
+
56
+ ```bash
57
+ ng e2e
58
+ ```
59
+
60
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
61
+
62
+ ## Additional Resources
63
+
64
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
@@ -0,0 +1,391 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, Injectable } from '@angular/core';
3
+ import { custom } from 'devextreme/ui/dialog';
4
+ import { formatMessage, locale } from 'devextreme/localization';
5
+ import { LogLevel } from '@quanticdigit/web-services';
6
+ export { LogLevel } from '@quanticdigit/web-services';
7
+
8
+ /**
9
+ * Le chiavi di testo che questo pacchetto usa.
10
+ *
11
+ * @remarks
12
+ * <b>Sono il contratto.</b> Il pacchetto porta con se' i testi predefiniti in piu' lingue — vedi
13
+ * {@link DEFAULT_ERROR_MESSAGES} — e un prodotto ne sovrascrive una qualsiasi caricandola nel
14
+ * proprio catalogo di DevExtreme con `loadMessages`: quello del prodotto vince sempre.
15
+ *
16
+ * Stanno qui e non accanto a chi le risolve per rompere un ciclo fra moduli: i testi hanno bisogno
17
+ * delle chiavi, e chi risolve ha bisogno di entrambi.
18
+ */
19
+ const ErrorCopyKeys = {
20
+ titleInfo: 'dialogTitleInfo',
21
+ titleWarning: 'dialogTitleWarning',
22
+ titleError: 'dialogTitleError',
23
+ titleFatal: 'dialogTitleFatal',
24
+ messageWarning: 'dialogMessageWarning',
25
+ messageError: 'dialogMessageError',
26
+ messageFatal: 'dialogMessageFatal',
27
+ buttonClose: 'dialogLabelbtnClose',
28
+ };
29
+ /** Comodita' interna: compone le otto voci di una lingua nell'ordine dichiarato. */
30
+ const voci = (titleInfo, titleWarning, titleError, titleFatal, messageWarning, messageError, messageFatal, buttonClose) => ({
31
+ [ErrorCopyKeys.titleInfo]: titleInfo,
32
+ [ErrorCopyKeys.titleWarning]: titleWarning,
33
+ [ErrorCopyKeys.titleError]: titleError,
34
+ [ErrorCopyKeys.titleFatal]: titleFatal,
35
+ [ErrorCopyKeys.messageWarning]: messageWarning,
36
+ [ErrorCopyKeys.messageError]: messageError,
37
+ [ErrorCopyKeys.messageFatal]: messageFatal,
38
+ [ErrorCopyKeys.buttonClose]: buttonClose,
39
+ });
40
+ /**
41
+ * I testi predefiniti della finestra degli errori.
42
+ *
43
+ * @remarks
44
+ * <b>Perche' stanno nel pacchetto.</b> E' il modello dei componenti DevExtreme: un calendario porta
45
+ * dentro di se' i nomi dei mesi in tutte le lingue, e chi non li vuole si rifa' il componente con un
46
+ * template. Qui e' lo stesso — se questi testi non vanno bene si dichiara `ERROR_PRESENTER` e la
47
+ * finestra la disegna il prodotto.
48
+ *
49
+ * <b>Perche' non si caricano nel catalogo di DevExtreme.</b> Caricarli con `loadMessages` sarebbe
50
+ * scrivere in uno stato globale, e l'esito dipenderebbe da chi scrive per ultimo: se un prodotto
51
+ * carica i propri testi all'avvio e noi carichiamo i nostri dopo, i suoi sparirebbero. Restando qui,
52
+ * l'ordine di risoluzione e' fisso e non dipende da nessuna sequenza:
53
+ *
54
+ * 1. il catalogo del prodotto, se ha la chiave;
55
+ * 2. questi testi, per la lingua completa (`zh-tw`);
56
+ * 3. questi testi, per la sola lingua (`zh`);
57
+ * 4. questi testi in inglese;
58
+ * 5. la chiave stessa.
59
+ *
60
+ * Un prodotto sovrascrive quindi <b>una singola voce</b> caricandola nel proprio catalogo, senza
61
+ * dover ridichiarare le altre sette.
62
+ *
63
+ * <b>Le lingue sono le stesse trentuno di DevExtreme</b>, cosi' che i testi della finestra non
64
+ * risultino in una lingua diversa da quella dei componenti che la circondano. Delle trentuno, le
65
+ * cinque che i prodotti dichiarano davvero — `de`, `en`, `es`, `fr`, `it` — sono quelle su cui vale
66
+ * la pena investire una revisione; le altre ventisei sono un ripiego utile, non una traduzione
67
+ * professionale, e vanno riviste da chi la lingua la parla prima di distribuire in quel mercato.
68
+ */
69
+ const DEFAULT_ERROR_MESSAGES = {
70
+ ar: voci('معلومات', 'تحذير', 'خطأ', 'خطأ في التطبيق', 'يرجى مراجعة التحذيرات التالية.', 'لم تكتمل العملية.', 'حدث خطأ غير متوقع.', 'إغلاق'),
71
+ bg: voci('Информация', 'Предупреждение', 'Грешка', 'Грешка в приложението', 'Моля, прегледайте следните предупреждения.', 'Операцията не беше завършена.', 'Възникна неочаквана грешка.', 'Затвори'),
72
+ ca: voci('Informació', 'Avís', 'Error', 'Error d’aplicació', 'Reviseu els avisos següents.', 'L’operació no s’ha completat.', 'S’ha produït un error inesperat.', 'Tanca'),
73
+ cs: voci('Informace', 'Upozornění', 'Chyba', 'Chyba aplikace', 'Zkontrolujte následující upozornění.', 'Operace nebyla dokončena.', 'Došlo k neočekávané chybě.', 'Zavřít'),
74
+ da: voci('Information', 'Advarsel', 'Fejl', 'Programfejl', 'Gennemgå følgende advarsler.', 'Handlingen blev ikke gennemført.', 'Der opstod en uventet fejl.', 'Luk'),
75
+ de: voci('Information', 'Warnung', 'Fehler', 'Anwendungsfehler', 'Bitte beachten Sie die folgenden Warnungen.', 'Der Vorgang wurde nicht abgeschlossen.', 'Ein unerwarteter Fehler ist aufgetreten.', 'Schließen'),
76
+ el: voci('Πληροφορίες', 'Προειδοποίηση', 'Σφάλμα', 'Σφάλμα εφαρμογής', 'Ελέγξτε τις παρακάτω προειδοποιήσεις.', 'Η ενέργεια δεν ολοκληρώθηκε.', 'Παρουσιάστηκε απροσδόκητο σφάλμα.', 'Κλείσιμο'),
77
+ en: voci('Information', 'Warning', 'Error', 'Application error', 'Please review the following warnings.', 'The operation was not completed.', 'An unexpected error occurred.', 'Close'),
78
+ es: voci('Información', 'Aviso', 'Error', 'Error de aplicación', 'Revise las siguientes advertencias.', 'La operación no se ha completado.', 'Se ha producido un error inesperado.', 'Cerrar'),
79
+ fa: voci('اطلاعات', 'هشدار', 'خطا', 'خطای برنامه', 'لطفاً هشدارهای زیر را بررسی کنید.', 'عملیات کامل نشد.', 'خطای غیرمنتظره‌ای رخ داد.', 'بستن'),
80
+ fi: voci('Tiedot', 'Varoitus', 'Virhe', 'Sovellusvirhe', 'Tarkista seuraavat varoitukset.', 'Toimintoa ei suoritettu loppuun.', 'Tapahtui odottamaton virhe.', 'Sulje'),
81
+ fr: voci('Informations', 'Avertissement', 'Erreur', 'Erreur applicative', 'Veuillez prendre connaissance des avertissements suivants.', 'L’opération n’a pas été effectuée.', 'Une erreur inattendue est survenue.', 'Fermer'),
82
+ hu: voci('Információ', 'Figyelmeztetés', 'Hiba', 'Alkalmazáshiba', 'Tekintse át az alábbi figyelmeztetéseket.', 'A művelet nem fejeződött be.', 'Váratlan hiba történt.', 'Bezárás'),
83
+ it: voci('Informazioni', 'Avviso', 'Errore', 'Errore applicativo', 'Prendere visione dei seguenti avvisi.', 'L’operazione non è stata completata.', 'Si è verificato un errore imprevisto.', 'Chiudi'),
84
+ ja: voci('情報', '警告', 'エラー', 'アプリケーションエラー', '次の警告をご確認ください。', '操作は完了しませんでした。', '予期しないエラーが発生しました。', '閉じる'),
85
+ ko: voci('정보', '경고', '오류', '애플리케이션 오류', '다음 경고를 확인하십시오.', '작업이 완료되지 않았습니다.', '예기치 않은 오류가 발생했습니다.', '닫기'),
86
+ lt: voci('Informacija', 'Įspėjimas', 'Klaida', 'Programos klaida', 'Peržiūrėkite šiuos įspėjimus.', 'Operacija nebuvo baigta.', 'Įvyko netikėta klaida.', 'Uždaryti'),
87
+ lv: voci('Informācija', 'Brīdinājums', 'Kļūda', 'Lietojumprogrammas kļūda', 'Lūdzu, pārskatiet tālāk norādītos brīdinājumus.', 'Darbība netika pabeigta.', 'Radās neparedzēta kļūda.', 'Aizvērt'),
88
+ nb: voci('Informasjon', 'Advarsel', 'Feil', 'Programfeil', 'Se gjennom følgende advarsler.', 'Handlingen ble ikke fullført.', 'Det oppstod en uventet feil.', 'Lukk'),
89
+ nl: voci('Informatie', 'Waarschuwing', 'Fout', 'Toepassingsfout', 'Bekijk de volgende waarschuwingen.', 'De bewerking is niet voltooid.', 'Er is een onverwachte fout opgetreden.', 'Sluiten'),
90
+ pl: voci('Informacja', 'Ostrzeżenie', 'Błąd', 'Błąd aplikacji', 'Zapoznaj się z poniższymi ostrzeżeniami.', 'Operacja nie została ukończona.', 'Wystąpił nieoczekiwany błąd.', 'Zamknij'),
91
+ pt: voci('Informação', 'Aviso', 'Erro', 'Erro da aplicação', 'Consulte os avisos seguintes.', 'A operação não foi concluída.', 'Ocorreu um erro inesperado.', 'Fechar'),
92
+ ro: voci('Informații', 'Avertisment', 'Eroare', 'Eroare de aplicație', 'Consultați următoarele avertismente.', 'Operațiunea nu a fost finalizată.', 'A apărut o eroare neașteptată.', 'Închide'),
93
+ ru: voci('Информация', 'Предупреждение', 'Ошибка', 'Ошибка приложения', 'Ознакомьтесь со следующими предупреждениями.', 'Операция не была завершена.', 'Произошла непредвиденная ошибка.', 'Закрыть'),
94
+ sl: voci('Informacije', 'Opozorilo', 'Napaka', 'Napaka aplikacije', 'Preglejte naslednja opozorila.', 'Dejanje ni bilo dokončano.', 'Prišlo je do nepričakovane napake.', 'Zapri'),
95
+ sv: voci('Information', 'Varning', 'Fel', 'Programfel', 'Granska följande varningar.', 'Åtgärden slutfördes inte.', 'Ett oväntat fel inträffade.', 'Stäng'),
96
+ tr: voci('Bilgi', 'Uyarı', 'Hata', 'Uygulama hatası', 'Lütfen aşağıdaki uyarıları inceleyin.', 'İşlem tamamlanmadı.', 'Beklenmeyen bir hata oluştu.', 'Kapat'),
97
+ uk: voci('Інформація', 'Попередження', 'Помилка', 'Помилка застосунку', 'Перегляньте наведені нижче попередження.', 'Операцію не завершено.', 'Сталася неочікувана помилка.', 'Закрити'),
98
+ vi: voci('Thông tin', 'Cảnh báo', 'Lỗi', 'Lỗi ứng dụng', 'Vui lòng xem các cảnh báo sau.', 'Thao tác chưa được hoàn tất.', 'Đã xảy ra lỗi không mong muốn.', 'Đóng'),
99
+ zh: voci('信息', '警告', '错误', '应用程序错误', '请查看以下警告。', '操作未完成。', '发生意外错误。', '关闭'),
100
+ 'zh-tw': voci('資訊', '警告', '錯誤', '應用程式錯誤', '請檢視下列警告。', '作業未完成。', '發生未預期的錯誤。', '關閉'),
101
+ };
102
+ /** La lingua di ripiego quando quella corrente non e' fra quelle previste. */
103
+ const FALLBACK_LANGUAGE = 'en';
104
+
105
+ /**
106
+ * L'icona che accompagna un livello.
107
+ *
108
+ * Portata da `global-functions.ts` di Jupiter con lo stesso nome e lo stesso esito: i fogli di stile
109
+ * che vi si appoggiano non cambiano.
110
+ */
111
+ function manageFormCssClassIcons(level) {
112
+ switch (level) {
113
+ case LogLevel.Info:
114
+ return 'dx-icon-info';
115
+ case LogLevel.Warning:
116
+ return 'dx-icon-warning';
117
+ case LogLevel.Error:
118
+ return 'dx-icon-clear';
119
+ default:
120
+ return 'dx-icon-warning';
121
+ }
122
+ }
123
+ /**
124
+ * Risolve una chiave, in quattro passi.
125
+ *
126
+ * @remarks
127
+ * L'ordine e' fisso e non dipende da chi ha caricato cosa per primo:
128
+ *
129
+ * 1. il <b>catalogo del prodotto</b>, se ha la chiave — cosi' sovrascrivere una singola voce e'
130
+ * sufficiente a farla vincere, senza ridichiarare le altre;
131
+ * 2. i testi del pacchetto per la <b>lingua completa</b> (`zh-tw`);
132
+ * 3. i testi del pacchetto per la <b>sola lingua</b> (`zh`, e quindi anche `it-IT` su `it`);
133
+ * 4. gli stessi in inglese, per le lingue che non abbiamo previsto;
134
+ * 5. la <b>chiave</b>, come ultima risorsa.
135
+ *
136
+ * Il quarto passo esiste perche' `formatMessage` restituisce la <b>stringa vuota</b> su una chiave
137
+ * assente, non la chiave: lasciarlo passare produceva una finestra con titolo e testo vuoti — e'
138
+ * andato storto qualcosa e non c'e' scritto niente da nessuna parte. Una chiave visibile e' una
139
+ * mancanza che si nota.
140
+ */
141
+ function resolve(key) {
142
+ const dalProdotto = formatMessage(key);
143
+ if (dalProdotto != null && dalProdotto !== '') {
144
+ return dalProdotto;
145
+ }
146
+ // Prima la lingua COMPLETA, poi la sola parte di lingua. L'ordine conta: `zh-tw` e `zh` sono
147
+ // due dizionari diversi — cinese tradizionale e semplificato — e guardare subito la sola parte di
148
+ // lingua renderebbe il primo irraggiungibile. `it-IT` invece non ha un dizionario proprio e
149
+ // ricade su `it`, che e' il comportamento voluto.
150
+ const completa = (locale() ?? FALLBACK_LANGUAGE).toLowerCase();
151
+ const lingua = completa.split('-')[0];
152
+ return (DEFAULT_ERROR_MESSAGES[completa]?.[key] ??
153
+ DEFAULT_ERROR_MESSAGES[lingua]?.[key] ??
154
+ DEFAULT_ERROR_MESSAGES[FALLBACK_LANGUAGE]?.[key] ??
155
+ key);
156
+ }
157
+ /** Il titolo della finestra per un livello, risolto sul catalogo del prodotto. */
158
+ function levelTitle(level) {
159
+ switch (level) {
160
+ case LogLevel.Info:
161
+ return resolve(ErrorCopyKeys.titleInfo);
162
+ case LogLevel.Warning:
163
+ return resolve(ErrorCopyKeys.titleWarning);
164
+ case LogLevel.Error:
165
+ return resolve(ErrorCopyKeys.titleError);
166
+ case LogLevel.Fatal:
167
+ return resolve(ErrorCopyKeys.titleFatal);
168
+ default:
169
+ return resolve(ErrorCopyKeys.titleInfo);
170
+ }
171
+ }
172
+ /** L'introduzione della finestra per un livello, risolta sul catalogo del prodotto. */
173
+ function levelMessage(level) {
174
+ switch (level) {
175
+ case LogLevel.Warning:
176
+ return resolve(ErrorCopyKeys.messageWarning);
177
+ case LogLevel.Error:
178
+ return resolve(ErrorCopyKeys.messageError);
179
+ case LogLevel.Fatal:
180
+ return resolve(ErrorCopyKeys.messageFatal);
181
+ default:
182
+ return resolve(ErrorCopyKeys.titleInfo);
183
+ }
184
+ }
185
+ /**
186
+ * Traduce il tipo dichiarato da `IErrorInfoAppender` nel livello corrispondente.
187
+ *
188
+ * I quattro caratteri sono quelli dell'envelope: `W` avviso, `E` rifiuto di dominio, `F` guasto.
189
+ */
190
+ function levelOf(type) {
191
+ switch (type) {
192
+ case 'W':
193
+ return LogLevel.Warning;
194
+ case 'E':
195
+ return LogLevel.Error;
196
+ case 'F':
197
+ return LogLevel.Fatal;
198
+ default:
199
+ return LogLevel.Info;
200
+ }
201
+ }
202
+ /**
203
+ * Come mostrare gli errori, quando la finestra predefinita non va bene.
204
+ *
205
+ * @remarks
206
+ * <b>Non ha una fabbrica predefinita, e non e' una dimenticanza.</b> Assente — o fornito come
207
+ * `null` — significa «va bene il modo base», e {@link ErrorCollector} apre la propria finestra
208
+ * DevExtreme. Si dichiara soltanto quando si vuole qualcos'altro:
209
+ *
210
+ * ```ts
211
+ * { provide: ERROR_PRESENTER, useValue: (p) => miaFinestra(p.title, p.message, p.errors) }
212
+ * ```
213
+ *
214
+ * Chi vuole tenere la finestra e aggiungerci qualcosa avvolge {@link devExtremeDialogPresenter},
215
+ * che e' esportata apposta.
216
+ */
217
+ const ERROR_PRESENTER = new InjectionToken('QuanticDigit.ErrorPresenter');
218
+ /**
219
+ * Risolve una chiave qualsiasi con lo stesso ripiego dei titoli.
220
+ *
221
+ * Serve a chi realizza una finestra propria e vuole le stesse regole di risoluzione.
222
+ */
223
+ function buttonText(key) {
224
+ return resolve(key);
225
+ }
226
+
227
+ /**
228
+ * La finestra predefinita: un elenco degli errori raccolti, con l'icona del proprio livello.
229
+ *
230
+ * @remarks
231
+ * E' cio' che {@link ErrorCollector} apre quando nessuno ha dichiarato `ERROR_PRESENTER`. E'
232
+ * esportata perche' chi ne vuole una diversa possa comunque riusarla — per esempio per mandare
233
+ * l'errore alla telemetria e poi mostrarlo come prima:
234
+ *
235
+ * ```ts
236
+ * { provide: ERROR_PRESENTER, useValue: (p) => { telemetria(p); devExtremeDialogPresenter(p); } }
237
+ * ```
238
+ */
239
+ const devExtremeDialogPresenter = (presentation) => {
240
+ const righe = presentation.errors
241
+ .map((e) => `<li class="${manageFormCssClassIcons(e.level)}">${testo(e.message, e.id)}</li>`)
242
+ .join('');
243
+ const finestra = custom({
244
+ title: presentation.title,
245
+ messageHtml: `<p>${presentation.message}</p><ul>${righe}</ul>`,
246
+ buttons: [{ text: buttonText(ErrorCopyKeys.buttonClose) }],
247
+ });
248
+ void finestra.show();
249
+ };
250
+ /**
251
+ * Il testo di una riga.
252
+ *
253
+ * @remarks
254
+ * `message` porta cio' che il server ha messo. In DeepWood e' una <b>chiave di copy</b> del catalogo
255
+ * (`error.<mnemonico>`), non un testo: quel prodotto deve risolverla prima di consegnarla qui,
256
+ * oppure mostrera' la chiave. La chiave mostrata e' una mancanza visibile, preferibile a un testo
257
+ * inventato dal pacchetto.
258
+ */
259
+ function testo(message, id) {
260
+ const identificativo = id != null && id !== 0 ? ` [${id}]` : '';
261
+ return `${message ?? ''}${identificativo}`;
262
+ }
263
+
264
+ /**
265
+ * Raccoglie gli errori di una chiamata e li mostra all'utente.
266
+ *
267
+ * @remarks
268
+ * <b>E' la realizzazione di `IErrorInfoAppender`</b> che `@quanticdigit/web-services` dichiara e non
269
+ * fornisce. La separazione e' voluta: il livello di servizio sa <i>che</i> qualcosa e' andato storto,
270
+ * questo sa <i>come</i> dirlo, e un'applicazione senza interfaccia grafica sostituisce il secondo
271
+ * senza toccare il primo.
272
+ *
273
+ * <b>Non e' un portaggio.</b> In Jupiter questo lavoro sta dentro `LogService`, 376 righe che sono
274
+ * insieme accumulatore, presentatore, registro e client HTTP verso una WebApi: quel file non e'
275
+ * separabile senza riscriverlo. Qui c'e' la sola presentazione, e cio' che manca — spedire l'evento
276
+ * di errore al proprio backend — resta al prodotto, che lo fa con i propri servizi.
277
+ *
278
+ * <b>Gli errori si accumulano.</b> Una schermata che ne produce tre in sequenza deve mostrarne una
279
+ * finestra sola: aprirne tre significa che l'utente chiude le prime due senza leggerle.
280
+ */
281
+ class ErrorCollector {
282
+ /** Quanto si aspetta prima di mostrare, per raccogliere cio' che arriva nello stesso giro. */
283
+ static ATTESA_MS = 0;
284
+ /**
285
+ * Come mostrare, quando il modo base non va bene.
286
+ *
287
+ * @remarks
288
+ * Assente significa <b>va bene il modo base</b>: si apre la finestra di
289
+ * {@link devExtremeDialogPresenter}. `ERROR_PRESENTER` si dichiara soltanto per sostituirla, e in
290
+ * quel caso la nostra non viene nemmeno costruita.
291
+ */
292
+ presenter = inject(ERROR_PRESENTER, { optional: true });
293
+ accumulati = [];
294
+ livello = LogLevel.Info;
295
+ programmata = false;
296
+ /** Gli errori non ancora mostrati. */
297
+ get messages() {
298
+ return this.accumulati;
299
+ }
300
+ /**
301
+ * Prende in carico gli errori di una chiamata.
302
+ *
303
+ * @remarks
304
+ * E' scritta come proprieta' di tipo funzione, ma ora e' una scelta e non un obbligo:
305
+ * `BaseService` chiama attraverso l'oggetto, quindi anche un metodo normale funzionerebbe. Resta
306
+ * cosi' perche' regge anche se qualcuno la consegna come riferimento a un altro raccoglitore.
307
+ */
308
+ appendErrorInfo = (errors, type, url, post) => {
309
+ if (errors == null || errors.length === 0) {
310
+ return;
311
+ }
312
+ const livello = levelOf(type);
313
+ if (livello > this.livello) {
314
+ this.livello = livello;
315
+ }
316
+ for (const errore of errors) {
317
+ this.accumulati.push(errore);
318
+ }
319
+ this.programma();
320
+ };
321
+ /** Dimentica cio' che non e' stato mostrato. */
322
+ clear() {
323
+ this.accumulati.length = 0;
324
+ this.livello = LogLevel.Info;
325
+ }
326
+ /**
327
+ * Programma la presentazione per la fine del giro corrente.
328
+ *
329
+ * Tre chiamate fallite nello stesso istante producono una finestra sola con tre righe, non tre
330
+ * finestre: e' la ragione per cui la presentazione non e' immediata.
331
+ */
332
+ programma() {
333
+ if (this.programmata) {
334
+ return;
335
+ }
336
+ this.programmata = true;
337
+ setTimeout(() => this.mostra(), ErrorCollector.ATTESA_MS);
338
+ }
339
+ /** Mostra quanto accumulato e svuota. */
340
+ mostra() {
341
+ this.programmata = false;
342
+ if (this.accumulati.length === 0) {
343
+ return;
344
+ }
345
+ const presentazione = {
346
+ level: this.livello,
347
+ title: levelTitle(this.livello),
348
+ message: levelMessage(this.livello),
349
+ // Una copia: si svuota subito dopo, e chi presenta deve poter leggere con calma.
350
+ errors: [...this.accumulati],
351
+ };
352
+ this.clear();
353
+ // Se qualcuno ha dichiarato come mostrare, la nostra finestra non viene nemmeno costruita.
354
+ if (this.presenter != null) {
355
+ this.presenter(presentazione);
356
+ return;
357
+ }
358
+ devExtremeDialogPresenter(presentazione);
359
+ }
360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ErrorCollector, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
361
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ErrorCollector, providedIn: 'root' });
362
+ }
363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: ErrorCollector, decorators: [{
364
+ type: Injectable,
365
+ args: [{ providedIn: 'root' }]
366
+ }] });
367
+
368
+ /**
369
+ * Superficie pubblica di `@quanticdigit/web-errors`.
370
+ *
371
+ * Raccoglie gli errori che il livello di servizio consegna e li mostra: accumulo per livello, icone,
372
+ * una finestra sola per giro, e i testi gia' dentro nelle lingue principali.
373
+ *
374
+ * E' la realizzazione di `IErrorInfoAppender`, che `@quanticdigit/web-services` dichiara e non
375
+ * fornisce: chi non vuole una finestra sostituisce questo pacchetto e non tocca quello.
376
+ *
377
+ * Tre livelli di personalizzazione, dal piu' leggero al piu' invasivo. Nessuno e' obbligatorio: senza
378
+ * fare niente si ha una finestra che parla la lingua corrente.
379
+ *
380
+ * 1. **un testo** — si carica quella chiave di `ErrorCopyKeys` nel catalogo di DevExtreme, e vince
381
+ * sul testo predefinito senza doverne ridichiarare altri;
382
+ * 2. **la finestra** — si dichiara `ERROR_PRESENTER`; senza, resta quella predefinita;
383
+ * 3. **tutto** — si realizza `IErrorInfoAppender` per conto proprio e questo pacchetto non serve.
384
+ */
385
+
386
+ /**
387
+ * Generated bundle index. Do not edit.
388
+ */
389
+
390
+ export { DEFAULT_ERROR_MESSAGES, ERROR_PRESENTER, ErrorCollector, ErrorCopyKeys, FALLBACK_LANGUAGE, buttonText, devExtremeDialogPresenter, levelMessage, levelOf, levelTitle, manageFormCssClassIcons };
391
+ //# sourceMappingURL=quanticdigit-web-errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quanticdigit-web-errors.mjs","sources":["../../../projects/web-errors/src/lib/error-messages.ts","../../../projects/web-errors/src/lib/error-presentation.ts","../../../projects/web-errors/src/lib/error-dialog.ts","../../../projects/web-errors/src/lib/error-collector.ts","../../../projects/web-errors/src/public-api.ts","../../../projects/web-errors/src/quanticdigit-web-errors.ts"],"sourcesContent":["/**\n * Le chiavi di testo che questo pacchetto usa.\n *\n * @remarks\n * <b>Sono il contratto.</b> Il pacchetto porta con se' i testi predefiniti in piu' lingue — vedi\n * {@link DEFAULT_ERROR_MESSAGES} — e un prodotto ne sovrascrive una qualsiasi caricandola nel\n * proprio catalogo di DevExtreme con `loadMessages`: quello del prodotto vince sempre.\n *\n * Stanno qui e non accanto a chi le risolve per rompere un ciclo fra moduli: i testi hanno bisogno\n * delle chiavi, e chi risolve ha bisogno di entrambi.\n */\nexport const ErrorCopyKeys = {\n titleInfo: 'dialogTitleInfo',\n titleWarning: 'dialogTitleWarning',\n titleError: 'dialogTitleError',\n titleFatal: 'dialogTitleFatal',\n messageWarning: 'dialogMessageWarning',\n messageError: 'dialogMessageError',\n messageFatal: 'dialogMessageFatal',\n buttonClose: 'dialogLabelbtnClose',\n} as const;\n\n/** Comodita' interna: compone le otto voci di una lingua nell'ordine dichiarato. */\nconst voci = (\n titleInfo: string,\n titleWarning: string,\n titleError: string,\n titleFatal: string,\n messageWarning: string,\n messageError: string,\n messageFatal: string,\n buttonClose: string,\n): Readonly<Record<string, string>> => ({\n [ErrorCopyKeys.titleInfo]: titleInfo,\n [ErrorCopyKeys.titleWarning]: titleWarning,\n [ErrorCopyKeys.titleError]: titleError,\n [ErrorCopyKeys.titleFatal]: titleFatal,\n [ErrorCopyKeys.messageWarning]: messageWarning,\n [ErrorCopyKeys.messageError]: messageError,\n [ErrorCopyKeys.messageFatal]: messageFatal,\n [ErrorCopyKeys.buttonClose]: buttonClose,\n});\n\n/**\n * I testi predefiniti della finestra degli errori.\n *\n * @remarks\n * <b>Perche' stanno nel pacchetto.</b> E' il modello dei componenti DevExtreme: un calendario porta\n * dentro di se' i nomi dei mesi in tutte le lingue, e chi non li vuole si rifa' il componente con un\n * template. Qui e' lo stesso — se questi testi non vanno bene si dichiara `ERROR_PRESENTER` e la\n * finestra la disegna il prodotto.\n *\n * <b>Perche' non si caricano nel catalogo di DevExtreme.</b> Caricarli con `loadMessages` sarebbe\n * scrivere in uno stato globale, e l'esito dipenderebbe da chi scrive per ultimo: se un prodotto\n * carica i propri testi all'avvio e noi carichiamo i nostri dopo, i suoi sparirebbero. Restando qui,\n * l'ordine di risoluzione e' fisso e non dipende da nessuna sequenza:\n *\n * 1. il catalogo del prodotto, se ha la chiave;\n * 2. questi testi, per la lingua completa (`zh-tw`);\n * 3. questi testi, per la sola lingua (`zh`);\n * 4. questi testi in inglese;\n * 5. la chiave stessa.\n *\n * Un prodotto sovrascrive quindi <b>una singola voce</b> caricandola nel proprio catalogo, senza\n * dover ridichiarare le altre sette.\n *\n * <b>Le lingue sono le stesse trentuno di DevExtreme</b>, cosi' che i testi della finestra non\n * risultino in una lingua diversa da quella dei componenti che la circondano. Delle trentuno, le\n * cinque che i prodotti dichiarano davvero — `de`, `en`, `es`, `fr`, `it` — sono quelle su cui vale\n * la pena investire una revisione; le altre ventisei sono un ripiego utile, non una traduzione\n * professionale, e vanno riviste da chi la lingua la parla prima di distribuire in quel mercato.\n */\nexport const DEFAULT_ERROR_MESSAGES: Readonly<Record<string, Readonly<Record<string, string>>>> = {\n ar: voci(\n 'معلومات',\n 'تحذير',\n 'خطأ',\n 'خطأ في التطبيق',\n 'يرجى مراجعة التحذيرات التالية.',\n 'لم تكتمل العملية.',\n 'حدث خطأ غير متوقع.',\n 'إغلاق',\n ),\n bg: voci(\n 'Информация',\n 'Предупреждение',\n 'Грешка',\n 'Грешка в приложението',\n 'Моля, прегледайте следните предупреждения.',\n 'Операцията не беше завършена.',\n 'Възникна неочаквана грешка.',\n 'Затвори',\n ),\n ca: voci(\n 'Informació',\n 'Avís',\n 'Error',\n 'Error d’aplicació',\n 'Reviseu els avisos següents.',\n 'L’operació no s’ha completat.',\n 'S’ha produït un error inesperat.',\n 'Tanca',\n ),\n cs: voci(\n 'Informace',\n 'Upozornění',\n 'Chyba',\n 'Chyba aplikace',\n 'Zkontrolujte následující upozornění.',\n 'Operace nebyla dokončena.',\n 'Došlo k neočekávané chybě.',\n 'Zavřít',\n ),\n da: voci(\n 'Information',\n 'Advarsel',\n 'Fejl',\n 'Programfejl',\n 'Gennemgå følgende advarsler.',\n 'Handlingen blev ikke gennemført.',\n 'Der opstod en uventet fejl.',\n 'Luk',\n ),\n de: voci(\n 'Information',\n 'Warnung',\n 'Fehler',\n 'Anwendungsfehler',\n 'Bitte beachten Sie die folgenden Warnungen.',\n 'Der Vorgang wurde nicht abgeschlossen.',\n 'Ein unerwarteter Fehler ist aufgetreten.',\n 'Schließen',\n ),\n el: voci(\n 'Πληροφορίες',\n 'Προειδοποίηση',\n 'Σφάλμα',\n 'Σφάλμα εφαρμογής',\n 'Ελέγξτε τις παρακάτω προειδοποιήσεις.',\n 'Η ενέργεια δεν ολοκληρώθηκε.',\n 'Παρουσιάστηκε απροσδόκητο σφάλμα.',\n 'Κλείσιμο',\n ),\n en: voci(\n 'Information',\n 'Warning',\n 'Error',\n 'Application error',\n 'Please review the following warnings.',\n 'The operation was not completed.',\n 'An unexpected error occurred.',\n 'Close',\n ),\n es: voci(\n 'Información',\n 'Aviso',\n 'Error',\n 'Error de aplicación',\n 'Revise las siguientes advertencias.',\n 'La operación no se ha completado.',\n 'Se ha producido un error inesperado.',\n 'Cerrar',\n ),\n fa: voci(\n 'اطلاعات',\n 'هشدار',\n 'خطا',\n 'خطای برنامه',\n 'لطفاً هشدارهای زیر را بررسی کنید.',\n 'عملیات کامل نشد.',\n 'خطای غیرمنتظره‌ای رخ داد.',\n 'بستن',\n ),\n fi: voci(\n 'Tiedot',\n 'Varoitus',\n 'Virhe',\n 'Sovellusvirhe',\n 'Tarkista seuraavat varoitukset.',\n 'Toimintoa ei suoritettu loppuun.',\n 'Tapahtui odottamaton virhe.',\n 'Sulje',\n ),\n fr: voci(\n 'Informations',\n 'Avertissement',\n 'Erreur',\n 'Erreur applicative',\n 'Veuillez prendre connaissance des avertissements suivants.',\n 'L’opération n’a pas été effectuée.',\n 'Une erreur inattendue est survenue.',\n 'Fermer',\n ),\n hu: voci(\n 'Információ',\n 'Figyelmeztetés',\n 'Hiba',\n 'Alkalmazáshiba',\n 'Tekintse át az alábbi figyelmeztetéseket.',\n 'A művelet nem fejeződött be.',\n 'Váratlan hiba történt.',\n 'Bezárás',\n ),\n it: voci(\n 'Informazioni',\n 'Avviso',\n 'Errore',\n 'Errore applicativo',\n 'Prendere visione dei seguenti avvisi.',\n 'L’operazione non è stata completata.',\n 'Si è verificato un errore imprevisto.',\n 'Chiudi',\n ),\n ja: voci(\n '情報',\n '警告',\n 'エラー',\n 'アプリケーションエラー',\n '次の警告をご確認ください。',\n '操作は完了しませんでした。',\n '予期しないエラーが発生しました。',\n '閉じる',\n ),\n ko: voci(\n '정보',\n '경고',\n '오류',\n '애플리케이션 오류',\n '다음 경고를 확인하십시오.',\n '작업이 완료되지 않았습니다.',\n '예기치 않은 오류가 발생했습니다.',\n '닫기',\n ),\n lt: voci(\n 'Informacija',\n 'Įspėjimas',\n 'Klaida',\n 'Programos klaida',\n 'Peržiūrėkite šiuos įspėjimus.',\n 'Operacija nebuvo baigta.',\n 'Įvyko netikėta klaida.',\n 'Uždaryti',\n ),\n lv: voci(\n 'Informācija',\n 'Brīdinājums',\n 'Kļūda',\n 'Lietojumprogrammas kļūda',\n 'Lūdzu, pārskatiet tālāk norādītos brīdinājumus.',\n 'Darbība netika pabeigta.',\n 'Radās neparedzēta kļūda.',\n 'Aizvērt',\n ),\n nb: voci(\n 'Informasjon',\n 'Advarsel',\n 'Feil',\n 'Programfeil',\n 'Se gjennom følgende advarsler.',\n 'Handlingen ble ikke fullført.',\n 'Det oppstod en uventet feil.',\n 'Lukk',\n ),\n nl: voci(\n 'Informatie',\n 'Waarschuwing',\n 'Fout',\n 'Toepassingsfout',\n 'Bekijk de volgende waarschuwingen.',\n 'De bewerking is niet voltooid.',\n 'Er is een onverwachte fout opgetreden.',\n 'Sluiten',\n ),\n pl: voci(\n 'Informacja',\n 'Ostrzeżenie',\n 'Błąd',\n 'Błąd aplikacji',\n 'Zapoznaj się z poniższymi ostrzeżeniami.',\n 'Operacja nie została ukończona.',\n 'Wystąpił nieoczekiwany błąd.',\n 'Zamknij',\n ),\n pt: voci(\n 'Informação',\n 'Aviso',\n 'Erro',\n 'Erro da aplicação',\n 'Consulte os avisos seguintes.',\n 'A operação não foi concluída.',\n 'Ocorreu um erro inesperado.',\n 'Fechar',\n ),\n ro: voci(\n 'Informații',\n 'Avertisment',\n 'Eroare',\n 'Eroare de aplicație',\n 'Consultați următoarele avertismente.',\n 'Operațiunea nu a fost finalizată.',\n 'A apărut o eroare neașteptată.',\n 'Închide',\n ),\n ru: voci(\n 'Информация',\n 'Предупреждение',\n 'Ошибка',\n 'Ошибка приложения',\n 'Ознакомьтесь со следующими предупреждениями.',\n 'Операция не была завершена.',\n 'Произошла непредвиденная ошибка.',\n 'Закрыть',\n ),\n sl: voci(\n 'Informacije',\n 'Opozorilo',\n 'Napaka',\n 'Napaka aplikacije',\n 'Preglejte naslednja opozorila.',\n 'Dejanje ni bilo dokončano.',\n 'Prišlo je do nepričakovane napake.',\n 'Zapri',\n ),\n sv: voci(\n 'Information',\n 'Varning',\n 'Fel',\n 'Programfel',\n 'Granska följande varningar.',\n 'Åtgärden slutfördes inte.',\n 'Ett oväntat fel inträffade.',\n 'Stäng',\n ),\n tr: voci(\n 'Bilgi',\n 'Uyarı',\n 'Hata',\n 'Uygulama hatası',\n 'Lütfen aşağıdaki uyarıları inceleyin.',\n 'İşlem tamamlanmadı.',\n 'Beklenmeyen bir hata oluştu.',\n 'Kapat',\n ),\n uk: voci(\n 'Інформація',\n 'Попередження',\n 'Помилка',\n 'Помилка застосунку',\n 'Перегляньте наведені нижче попередження.',\n 'Операцію не завершено.',\n 'Сталася неочікувана помилка.',\n 'Закрити',\n ),\n vi: voci(\n 'Thông tin',\n 'Cảnh báo',\n 'Lỗi',\n 'Lỗi ứng dụng',\n 'Vui lòng xem các cảnh báo sau.',\n 'Thao tác chưa được hoàn tất.',\n 'Đã xảy ra lỗi không mong muốn.',\n 'Đóng',\n ),\n zh: voci(\n '信息',\n '警告',\n '错误',\n '应用程序错误',\n '请查看以下警告。',\n '操作未完成。',\n '发生意外错误。',\n '关闭',\n ),\n 'zh-tw': voci(\n '資訊',\n '警告',\n '錯誤',\n '應用程式錯誤',\n '請檢視下列警告。',\n '作業未完成。',\n '發生未預期的錯誤。',\n '關閉',\n ),\n};\n\n/** La lingua di ripiego quando quella corrente non e' fra quelle previste. */\nexport const FALLBACK_LANGUAGE = 'en';\n","import { InjectionToken } from '@angular/core';\nimport { formatMessage, locale } from 'devextreme/localization';\nimport { DEFAULT_ERROR_MESSAGES, ErrorCopyKeys, FALLBACK_LANGUAGE } from './error-messages';\nimport { IErrorInfoModel, LogLevel } from '@quanticdigit/web-services';\n\n// L'enum della gravita' vive nel contratto, non nella presentazione: si riesporta perche' chi lo\n// prendeva da questo pacchetto continui a poterlo fare.\nexport { LogLevel } from '@quanticdigit/web-services';\n\n// Le chiavi vivono accanto ai testi: si riesportano perche' chi le prendeva da qui continui a farlo.\nexport { ErrorCopyKeys } from './error-messages';\n\n/**\n * L'icona che accompagna un livello.\n *\n * Portata da `global-functions.ts` di Jupiter con lo stesso nome e lo stesso esito: i fogli di stile\n * che vi si appoggiano non cambiano.\n */\nexport function manageFormCssClassIcons(level: number): string {\n switch (level) {\n case LogLevel.Info:\n return 'dx-icon-info';\n case LogLevel.Warning:\n return 'dx-icon-warning';\n case LogLevel.Error:\n return 'dx-icon-clear';\n default:\n return 'dx-icon-warning';\n }\n}\n\n/**\n * Risolve una chiave, in quattro passi.\n *\n * @remarks\n * L'ordine e' fisso e non dipende da chi ha caricato cosa per primo:\n *\n * 1. il <b>catalogo del prodotto</b>, se ha la chiave — cosi' sovrascrivere una singola voce e'\n * sufficiente a farla vincere, senza ridichiarare le altre;\n * 2. i testi del pacchetto per la <b>lingua completa</b> (`zh-tw`);\n * 3. i testi del pacchetto per la <b>sola lingua</b> (`zh`, e quindi anche `it-IT` su `it`);\n * 4. gli stessi in inglese, per le lingue che non abbiamo previsto;\n * 5. la <b>chiave</b>, come ultima risorsa.\n *\n * Il quarto passo esiste perche' `formatMessage` restituisce la <b>stringa vuota</b> su una chiave\n * assente, non la chiave: lasciarlo passare produceva una finestra con titolo e testo vuoti — e'\n * andato storto qualcosa e non c'e' scritto niente da nessuna parte. Una chiave visibile e' una\n * mancanza che si nota.\n */\nfunction resolve(key: string): string {\n const dalProdotto = formatMessage(key);\n if (dalProdotto != null && dalProdotto !== '') {\n return dalProdotto;\n }\n\n // Prima la lingua COMPLETA, poi la sola parte di lingua. L'ordine conta: `zh-tw` e `zh` sono\n // due dizionari diversi — cinese tradizionale e semplificato — e guardare subito la sola parte di\n // lingua renderebbe il primo irraggiungibile. `it-IT` invece non ha un dizionario proprio e\n // ricade su `it`, che e' il comportamento voluto.\n const completa = (locale() ?? FALLBACK_LANGUAGE).toLowerCase();\n const lingua = completa.split('-')[0];\n\n return (\n DEFAULT_ERROR_MESSAGES[completa]?.[key] ??\n DEFAULT_ERROR_MESSAGES[lingua]?.[key] ??\n DEFAULT_ERROR_MESSAGES[FALLBACK_LANGUAGE]?.[key] ??\n key\n );\n}\n\n/** Il titolo della finestra per un livello, risolto sul catalogo del prodotto. */\nexport function levelTitle(level: number): string {\n switch (level) {\n case LogLevel.Info:\n return resolve(ErrorCopyKeys.titleInfo);\n case LogLevel.Warning:\n return resolve(ErrorCopyKeys.titleWarning);\n case LogLevel.Error:\n return resolve(ErrorCopyKeys.titleError);\n case LogLevel.Fatal:\n return resolve(ErrorCopyKeys.titleFatal);\n default:\n return resolve(ErrorCopyKeys.titleInfo);\n }\n}\n\n/** L'introduzione della finestra per un livello, risolta sul catalogo del prodotto. */\nexport function levelMessage(level: number): string {\n switch (level) {\n case LogLevel.Warning:\n return resolve(ErrorCopyKeys.messageWarning);\n case LogLevel.Error:\n return resolve(ErrorCopyKeys.messageError);\n case LogLevel.Fatal:\n return resolve(ErrorCopyKeys.messageFatal);\n default:\n return resolve(ErrorCopyKeys.titleInfo);\n }\n}\n\n/**\n * Traduce il tipo dichiarato da `IErrorInfoAppender` nel livello corrispondente.\n *\n * I quattro caratteri sono quelli dell'envelope: `W` avviso, `E` rifiuto di dominio, `F` guasto.\n */\nexport function levelOf(type: string): LogLevel {\n switch (type) {\n case 'W':\n return LogLevel.Warning;\n case 'E':\n return LogLevel.Error;\n case 'F':\n return LogLevel.Fatal;\n default:\n return LogLevel.Info;\n }\n}\n\n/**\n * Cio' che va mostrato all'utente: gia' aggregato, con il livello massimo e i testi risolti.\n *\n * @remarks\n * I testi arrivano gia' risolti perche' il caso comune sia banale. Chi vuole ricalcolarli ha\n * {@link levelTitle}, {@link levelMessage} e {@link manageFormCssClassIcons}, che restano pubbliche.\n */\nexport interface ErrorPresentation {\n readonly level: LogLevel;\n readonly title: string;\n readonly message: string;\n readonly errors: readonly IErrorInfoModel[];\n}\n\n/** Chi mostra gli errori raccolti. */\nexport type ErrorPresenter = (presentation: ErrorPresentation) => void;\n\n/**\n * Come mostrare gli errori, quando la finestra predefinita non va bene.\n *\n * @remarks\n * <b>Non ha una fabbrica predefinita, e non e' una dimenticanza.</b> Assente — o fornito come\n * `null` — significa «va bene il modo base», e {@link ErrorCollector} apre la propria finestra\n * DevExtreme. Si dichiara soltanto quando si vuole qualcos'altro:\n *\n * ```ts\n * { provide: ERROR_PRESENTER, useValue: (p) => miaFinestra(p.title, p.message, p.errors) }\n * ```\n *\n * Chi vuole tenere la finestra e aggiungerci qualcosa avvolge {@link devExtremeDialogPresenter},\n * che e' esportata apposta.\n */\nexport const ERROR_PRESENTER = new InjectionToken<ErrorPresenter>('QuanticDigit.ErrorPresenter');\n\n/**\n * Risolve una chiave qualsiasi con lo stesso ripiego dei titoli.\n *\n * Serve a chi realizza una finestra propria e vuole le stesse regole di risoluzione.\n */\nexport function buttonText(key: string): string {\n return resolve(key);\n}\n","import { custom } from 'devextreme/ui/dialog';\nimport {\n ErrorCopyKeys,\n ErrorPresentation,\n ErrorPresenter,\n buttonText,\n manageFormCssClassIcons,\n} from './error-presentation';\n\n/**\n * La finestra predefinita: un elenco degli errori raccolti, con l'icona del proprio livello.\n *\n * @remarks\n * E' cio' che {@link ErrorCollector} apre quando nessuno ha dichiarato `ERROR_PRESENTER`. E'\n * esportata perche' chi ne vuole una diversa possa comunque riusarla — per esempio per mandare\n * l'errore alla telemetria e poi mostrarlo come prima:\n *\n * ```ts\n * { provide: ERROR_PRESENTER, useValue: (p) => { telemetria(p); devExtremeDialogPresenter(p); } }\n * ```\n */\nexport const devExtremeDialogPresenter: ErrorPresenter = (presentation: ErrorPresentation): void => {\n const righe = presentation.errors\n .map((e) => `<li class=\"${manageFormCssClassIcons(e.level)}\">${testo(e.message, e.id)}</li>`)\n .join('');\n\n const finestra = custom({\n title: presentation.title,\n messageHtml: `<p>${presentation.message}</p><ul>${righe}</ul>`,\n buttons: [{ text: buttonText(ErrorCopyKeys.buttonClose) }],\n });\n\n void finestra.show();\n};\n\n/**\n * Il testo di una riga.\n *\n * @remarks\n * `message` porta cio' che il server ha messo. In DeepWood e' una <b>chiave di copy</b> del catalogo\n * (`error.<mnemonico>`), non un testo: quel prodotto deve risolverla prima di consegnarla qui,\n * oppure mostrera' la chiave. La chiave mostrata e' una mancanza visibile, preferibile a un testo\n * inventato dal pacchetto.\n */\nfunction testo(message: string, id: number): string {\n const identificativo = id != null && id !== 0 ? ` [${id}]` : '';\n return `${message ?? ''}${identificativo}`;\n}\n","import { Injectable, inject } from '@angular/core';\nimport { IErrorInfoAppender, IErrorInfoModel } from '@quanticdigit/web-services';\nimport { devExtremeDialogPresenter } from './error-dialog';\nimport { ERROR_PRESENTER, ErrorPresentation, LogLevel, levelMessage, levelOf, levelTitle } from './error-presentation';\n\n/**\n * Raccoglie gli errori di una chiamata e li mostra all'utente.\n *\n * @remarks\n * <b>E' la realizzazione di `IErrorInfoAppender`</b> che `@quanticdigit/web-services` dichiara e non\n * fornisce. La separazione e' voluta: il livello di servizio sa <i>che</i> qualcosa e' andato storto,\n * questo sa <i>come</i> dirlo, e un'applicazione senza interfaccia grafica sostituisce il secondo\n * senza toccare il primo.\n *\n * <b>Non e' un portaggio.</b> In Jupiter questo lavoro sta dentro `LogService`, 376 righe che sono\n * insieme accumulatore, presentatore, registro e client HTTP verso una WebApi: quel file non e'\n * separabile senza riscriverlo. Qui c'e' la sola presentazione, e cio' che manca — spedire l'evento\n * di errore al proprio backend — resta al prodotto, che lo fa con i propri servizi.\n *\n * <b>Gli errori si accumulano.</b> Una schermata che ne produce tre in sequenza deve mostrarne una\n * finestra sola: aprirne tre significa che l'utente chiude le prime due senza leggerle.\n */\n@Injectable({ providedIn: 'root' })\nexport class ErrorCollector implements IErrorInfoAppender {\n /** Quanto si aspetta prima di mostrare, per raccogliere cio' che arriva nello stesso giro. */\n private static readonly ATTESA_MS = 0;\n\n /**\n * Come mostrare, quando il modo base non va bene.\n *\n * @remarks\n * Assente significa <b>va bene il modo base</b>: si apre la finestra di\n * {@link devExtremeDialogPresenter}. `ERROR_PRESENTER` si dichiara soltanto per sostituirla, e in\n * quel caso la nostra non viene nemmeno costruita.\n */\n private readonly presenter = inject(ERROR_PRESENTER, { optional: true });\n\n private readonly accumulati: IErrorInfoModel[] = [];\n private livello: LogLevel = LogLevel.Info;\n private programmata = false;\n\n /** Gli errori non ancora mostrati. */\n get messages(): readonly IErrorInfoModel[] {\n return this.accumulati;\n }\n\n /**\n * Prende in carico gli errori di una chiamata.\n *\n * @remarks\n * E' scritta come proprieta' di tipo funzione, ma ora e' una scelta e non un obbligo:\n * `BaseService` chiama attraverso l'oggetto, quindi anche un metodo normale funzionerebbe. Resta\n * cosi' perche' regge anche se qualcuno la consegna come riferimento a un altro raccoglitore.\n */\n appendErrorInfo = (errors: IErrorInfoModel[], type: string, url: string, post: unknown): void => {\n if (errors == null || errors.length === 0) {\n return;\n }\n\n const livello = levelOf(type);\n if (livello > this.livello) {\n this.livello = livello;\n }\n\n for (const errore of errors) {\n this.accumulati.push(errore);\n }\n\n this.programma();\n };\n\n /** Dimentica cio' che non e' stato mostrato. */\n clear(): void {\n this.accumulati.length = 0;\n this.livello = LogLevel.Info;\n }\n\n /**\n * Programma la presentazione per la fine del giro corrente.\n *\n * Tre chiamate fallite nello stesso istante producono una finestra sola con tre righe, non tre\n * finestre: e' la ragione per cui la presentazione non e' immediata.\n */\n private programma(): void {\n if (this.programmata) {\n return;\n }\n this.programmata = true;\n setTimeout(() => this.mostra(), ErrorCollector.ATTESA_MS);\n }\n\n /** Mostra quanto accumulato e svuota. */\n private mostra(): void {\n this.programmata = false;\n\n if (this.accumulati.length === 0) {\n return;\n }\n\n const presentazione: ErrorPresentation = {\n level: this.livello,\n title: levelTitle(this.livello),\n message: levelMessage(this.livello),\n // Una copia: si svuota subito dopo, e chi presenta deve poter leggere con calma.\n errors: [...this.accumulati],\n };\n\n this.clear();\n\n // Se qualcuno ha dichiarato come mostrare, la nostra finestra non viene nemmeno costruita.\n if (this.presenter != null) {\n this.presenter(presentazione);\n return;\n }\n\n devExtremeDialogPresenter(presentazione);\n }\n}\n","/**\n * Superficie pubblica di `@quanticdigit/web-errors`.\n *\n * Raccoglie gli errori che il livello di servizio consegna e li mostra: accumulo per livello, icone,\n * una finestra sola per giro, e i testi gia' dentro nelle lingue principali.\n *\n * E' la realizzazione di `IErrorInfoAppender`, che `@quanticdigit/web-services` dichiara e non\n * fornisce: chi non vuole una finestra sostituisce questo pacchetto e non tocca quello.\n *\n * Tre livelli di personalizzazione, dal piu' leggero al piu' invasivo. Nessuno e' obbligatorio: senza\n * fare niente si ha una finestra che parla la lingua corrente.\n *\n * 1. **un testo** — si carica quella chiave di `ErrorCopyKeys` nel catalogo di DevExtreme, e vince\n * sul testo predefinito senza doverne ridichiarare altri;\n * 2. **la finestra** — si dichiara `ERROR_PRESENTER`; senza, resta quella predefinita;\n * 3. **tutto** — si realizza `IErrorInfoAppender` per conto proprio e questo pacchetto non serve.\n */\n\nexport * from './lib/error-collector';\nexport * from './lib/error-dialog';\nexport * from './lib/error-messages';\nexport * from './lib/error-presentation';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@quanticdigit/web-errors",
3
+ "version": "1.0.0",
4
+ "description": "Raccolta e presentazione degli errori applicativi: accumulo per livello e dialogo.",
5
+ "author": "Quantic Digit S.R.L.",
6
+ "license": "SEE LICENSE IN LICENSE.txt",
7
+ "homepage": "https://www.quanticdigit.it/",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://gitlab.com/quanticdigit/library.git"
11
+ },
12
+ "sideEffects": false,
13
+ "peerDependencies": {
14
+ "@angular/core": "^21.2.0",
15
+ "devextreme": "^25.1.3",
16
+ "@quanticdigit/web-services": "1.0.0"
17
+ },
18
+ "dependencies": {
19
+ "tslib": "^2.3.0"
20
+ },
21
+ "module": "fesm2022/quanticdigit-web-errors.mjs",
22
+ "typings": "types/quanticdigit-web-errors.d.ts",
23
+ "exports": {
24
+ "./package.json": {
25
+ "default": "./package.json"
26
+ },
27
+ ".": {
28
+ "types": "./types/quanticdigit-web-errors.d.ts",
29
+ "default": "./fesm2022/quanticdigit-web-errors.mjs"
30
+ }
31
+ },
32
+ "type": "module"
33
+ }
@@ -0,0 +1,188 @@
1
+ import { IErrorInfoAppender, IErrorInfoModel, LogLevel } from '@quanticdigit/web-services';
2
+ export { LogLevel } from '@quanticdigit/web-services';
3
+ import * as i0 from '@angular/core';
4
+ import { InjectionToken } from '@angular/core';
5
+
6
+ /**
7
+ * Raccoglie gli errori di una chiamata e li mostra all'utente.
8
+ *
9
+ * @remarks
10
+ * <b>E' la realizzazione di `IErrorInfoAppender`</b> che `@quanticdigit/web-services` dichiara e non
11
+ * fornisce. La separazione e' voluta: il livello di servizio sa <i>che</i> qualcosa e' andato storto,
12
+ * questo sa <i>come</i> dirlo, e un'applicazione senza interfaccia grafica sostituisce il secondo
13
+ * senza toccare il primo.
14
+ *
15
+ * <b>Non e' un portaggio.</b> In Jupiter questo lavoro sta dentro `LogService`, 376 righe che sono
16
+ * insieme accumulatore, presentatore, registro e client HTTP verso una WebApi: quel file non e'
17
+ * separabile senza riscriverlo. Qui c'e' la sola presentazione, e cio' che manca — spedire l'evento
18
+ * di errore al proprio backend — resta al prodotto, che lo fa con i propri servizi.
19
+ *
20
+ * <b>Gli errori si accumulano.</b> Una schermata che ne produce tre in sequenza deve mostrarne una
21
+ * finestra sola: aprirne tre significa che l'utente chiude le prime due senza leggerle.
22
+ */
23
+ declare class ErrorCollector implements IErrorInfoAppender {
24
+ /** Quanto si aspetta prima di mostrare, per raccogliere cio' che arriva nello stesso giro. */
25
+ private static readonly ATTESA_MS;
26
+ /**
27
+ * Come mostrare, quando il modo base non va bene.
28
+ *
29
+ * @remarks
30
+ * Assente significa <b>va bene il modo base</b>: si apre la finestra di
31
+ * {@link devExtremeDialogPresenter}. `ERROR_PRESENTER` si dichiara soltanto per sostituirla, e in
32
+ * quel caso la nostra non viene nemmeno costruita.
33
+ */
34
+ private readonly presenter;
35
+ private readonly accumulati;
36
+ private livello;
37
+ private programmata;
38
+ /** Gli errori non ancora mostrati. */
39
+ get messages(): readonly IErrorInfoModel[];
40
+ /**
41
+ * Prende in carico gli errori di una chiamata.
42
+ *
43
+ * @remarks
44
+ * E' scritta come proprieta' di tipo funzione, ma ora e' una scelta e non un obbligo:
45
+ * `BaseService` chiama attraverso l'oggetto, quindi anche un metodo normale funzionerebbe. Resta
46
+ * cosi' perche' regge anche se qualcuno la consegna come riferimento a un altro raccoglitore.
47
+ */
48
+ appendErrorInfo: (errors: IErrorInfoModel[], type: string, url: string, post: unknown) => void;
49
+ /** Dimentica cio' che non e' stato mostrato. */
50
+ clear(): void;
51
+ /**
52
+ * Programma la presentazione per la fine del giro corrente.
53
+ *
54
+ * Tre chiamate fallite nello stesso istante producono una finestra sola con tre righe, non tre
55
+ * finestre: e' la ragione per cui la presentazione non e' immediata.
56
+ */
57
+ private programma;
58
+ /** Mostra quanto accumulato e svuota. */
59
+ private mostra;
60
+ static ɵfac: i0.ɵɵFactoryDeclaration<ErrorCollector, never>;
61
+ static ɵprov: i0.ɵɵInjectableDeclaration<ErrorCollector>;
62
+ }
63
+
64
+ /**
65
+ * Le chiavi di testo che questo pacchetto usa.
66
+ *
67
+ * @remarks
68
+ * <b>Sono il contratto.</b> Il pacchetto porta con se' i testi predefiniti in piu' lingue — vedi
69
+ * {@link DEFAULT_ERROR_MESSAGES} — e un prodotto ne sovrascrive una qualsiasi caricandola nel
70
+ * proprio catalogo di DevExtreme con `loadMessages`: quello del prodotto vince sempre.
71
+ *
72
+ * Stanno qui e non accanto a chi le risolve per rompere un ciclo fra moduli: i testi hanno bisogno
73
+ * delle chiavi, e chi risolve ha bisogno di entrambi.
74
+ */
75
+ declare const ErrorCopyKeys: {
76
+ readonly titleInfo: "dialogTitleInfo";
77
+ readonly titleWarning: "dialogTitleWarning";
78
+ readonly titleError: "dialogTitleError";
79
+ readonly titleFatal: "dialogTitleFatal";
80
+ readonly messageWarning: "dialogMessageWarning";
81
+ readonly messageError: "dialogMessageError";
82
+ readonly messageFatal: "dialogMessageFatal";
83
+ readonly buttonClose: "dialogLabelbtnClose";
84
+ };
85
+ /**
86
+ * I testi predefiniti della finestra degli errori.
87
+ *
88
+ * @remarks
89
+ * <b>Perche' stanno nel pacchetto.</b> E' il modello dei componenti DevExtreme: un calendario porta
90
+ * dentro di se' i nomi dei mesi in tutte le lingue, e chi non li vuole si rifa' il componente con un
91
+ * template. Qui e' lo stesso — se questi testi non vanno bene si dichiara `ERROR_PRESENTER` e la
92
+ * finestra la disegna il prodotto.
93
+ *
94
+ * <b>Perche' non si caricano nel catalogo di DevExtreme.</b> Caricarli con `loadMessages` sarebbe
95
+ * scrivere in uno stato globale, e l'esito dipenderebbe da chi scrive per ultimo: se un prodotto
96
+ * carica i propri testi all'avvio e noi carichiamo i nostri dopo, i suoi sparirebbero. Restando qui,
97
+ * l'ordine di risoluzione e' fisso e non dipende da nessuna sequenza:
98
+ *
99
+ * 1. il catalogo del prodotto, se ha la chiave;
100
+ * 2. questi testi, per la lingua completa (`zh-tw`);
101
+ * 3. questi testi, per la sola lingua (`zh`);
102
+ * 4. questi testi in inglese;
103
+ * 5. la chiave stessa.
104
+ *
105
+ * Un prodotto sovrascrive quindi <b>una singola voce</b> caricandola nel proprio catalogo, senza
106
+ * dover ridichiarare le altre sette.
107
+ *
108
+ * <b>Le lingue sono le stesse trentuno di DevExtreme</b>, cosi' che i testi della finestra non
109
+ * risultino in una lingua diversa da quella dei componenti che la circondano. Delle trentuno, le
110
+ * cinque che i prodotti dichiarano davvero — `de`, `en`, `es`, `fr`, `it` — sono quelle su cui vale
111
+ * la pena investire una revisione; le altre ventisei sono un ripiego utile, non una traduzione
112
+ * professionale, e vanno riviste da chi la lingua la parla prima di distribuire in quel mercato.
113
+ */
114
+ declare const DEFAULT_ERROR_MESSAGES: Readonly<Record<string, Readonly<Record<string, string>>>>;
115
+ /** La lingua di ripiego quando quella corrente non e' fra quelle previste. */
116
+ declare const FALLBACK_LANGUAGE = "en";
117
+
118
+ /**
119
+ * L'icona che accompagna un livello.
120
+ *
121
+ * Portata da `global-functions.ts` di Jupiter con lo stesso nome e lo stesso esito: i fogli di stile
122
+ * che vi si appoggiano non cambiano.
123
+ */
124
+ declare function manageFormCssClassIcons(level: number): string;
125
+ /** Il titolo della finestra per un livello, risolto sul catalogo del prodotto. */
126
+ declare function levelTitle(level: number): string;
127
+ /** L'introduzione della finestra per un livello, risolta sul catalogo del prodotto. */
128
+ declare function levelMessage(level: number): string;
129
+ /**
130
+ * Traduce il tipo dichiarato da `IErrorInfoAppender` nel livello corrispondente.
131
+ *
132
+ * I quattro caratteri sono quelli dell'envelope: `W` avviso, `E` rifiuto di dominio, `F` guasto.
133
+ */
134
+ declare function levelOf(type: string): LogLevel;
135
+ /**
136
+ * Cio' che va mostrato all'utente: gia' aggregato, con il livello massimo e i testi risolti.
137
+ *
138
+ * @remarks
139
+ * I testi arrivano gia' risolti perche' il caso comune sia banale. Chi vuole ricalcolarli ha
140
+ * {@link levelTitle}, {@link levelMessage} e {@link manageFormCssClassIcons}, che restano pubbliche.
141
+ */
142
+ interface ErrorPresentation {
143
+ readonly level: LogLevel;
144
+ readonly title: string;
145
+ readonly message: string;
146
+ readonly errors: readonly IErrorInfoModel[];
147
+ }
148
+ /** Chi mostra gli errori raccolti. */
149
+ type ErrorPresenter = (presentation: ErrorPresentation) => void;
150
+ /**
151
+ * Come mostrare gli errori, quando la finestra predefinita non va bene.
152
+ *
153
+ * @remarks
154
+ * <b>Non ha una fabbrica predefinita, e non e' una dimenticanza.</b> Assente — o fornito come
155
+ * `null` — significa «va bene il modo base», e {@link ErrorCollector} apre la propria finestra
156
+ * DevExtreme. Si dichiara soltanto quando si vuole qualcos'altro:
157
+ *
158
+ * ```ts
159
+ * { provide: ERROR_PRESENTER, useValue: (p) => miaFinestra(p.title, p.message, p.errors) }
160
+ * ```
161
+ *
162
+ * Chi vuole tenere la finestra e aggiungerci qualcosa avvolge {@link devExtremeDialogPresenter},
163
+ * che e' esportata apposta.
164
+ */
165
+ declare const ERROR_PRESENTER: InjectionToken<ErrorPresenter>;
166
+ /**
167
+ * Risolve una chiave qualsiasi con lo stesso ripiego dei titoli.
168
+ *
169
+ * Serve a chi realizza una finestra propria e vuole le stesse regole di risoluzione.
170
+ */
171
+ declare function buttonText(key: string): string;
172
+
173
+ /**
174
+ * La finestra predefinita: un elenco degli errori raccolti, con l'icona del proprio livello.
175
+ *
176
+ * @remarks
177
+ * E' cio' che {@link ErrorCollector} apre quando nessuno ha dichiarato `ERROR_PRESENTER`. E'
178
+ * esportata perche' chi ne vuole una diversa possa comunque riusarla — per esempio per mandare
179
+ * l'errore alla telemetria e poi mostrarlo come prima:
180
+ *
181
+ * ```ts
182
+ * { provide: ERROR_PRESENTER, useValue: (p) => { telemetria(p); devExtremeDialogPresenter(p); } }
183
+ * ```
184
+ */
185
+ declare const devExtremeDialogPresenter: ErrorPresenter;
186
+
187
+ export { DEFAULT_ERROR_MESSAGES, ERROR_PRESENTER, ErrorCollector, ErrorCopyKeys, FALLBACK_LANGUAGE, buttonText, devExtremeDialogPresenter, levelMessage, levelOf, levelTitle, manageFormCssClassIcons };
188
+ export type { ErrorPresentation, ErrorPresenter };