emilsoftware-utilities 2.0.0-dev.24 → 2.0.0-dev.26

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.
Files changed (112) hide show
  1. package/README.MD +185 -182
  2. package/dist/ContractGenerator.js.map +1 -1
  3. package/dist/DailyFileTransport.js.map +1 -1
  4. package/dist/DatabaseUpdater.js +24 -24
  5. package/dist/DatabaseUpdater.js.map +1 -1
  6. package/dist/EmilsoftwareModule.js.map +1 -1
  7. package/dist/ExecutionTimeLogger.js.map +1 -1
  8. package/dist/Orm.js.map +1 -1
  9. package/dist/RoutesLoggerMiddleware.js.map +1 -1
  10. package/dist/Utilities.js.map +1 -1
  11. package/dist/accessi-module/AccessiModule.js.map +1 -1
  12. package/dist/accessi-module/Console/console.js.map +1 -1
  13. package/dist/accessi-module/Controllers/ConfiguratorController.js.map +1 -1
  14. package/dist/accessi-module/Controllers/FiltriController.js.map +1 -1
  15. package/dist/accessi-module/Controllers/PermissionController.js.map +1 -1
  16. package/dist/accessi-module/Controllers/UserController.js.map +1 -1
  17. package/dist/accessi-module/Dtos/Abilitazione.js.map +1 -1
  18. package/dist/accessi-module/Dtos/AbilitazioneMenu.js.map +1 -1
  19. package/dist/accessi-module/Dtos/AssignPermissionsToUserRequest.js.map +1 -1
  20. package/dist/accessi-module/Dtos/AssignRolesToUserRequest.js.map +1 -1
  21. package/dist/accessi-module/Dtos/BaseResponse.js.map +1 -1
  22. package/dist/accessi-module/Dtos/FiltriUtente.js.map +1 -1
  23. package/dist/accessi-module/Dtos/GetGroupsWithMenusResponse.js.map +1 -1
  24. package/dist/accessi-module/Dtos/GetMenusResponse.js.map +1 -1
  25. package/dist/accessi-module/Dtos/GetRolesResponse.js.map +1 -1
  26. package/dist/accessi-module/Dtos/GetUsersResponse.js.map +1 -1
  27. package/dist/accessi-module/Dtos/LoginRequest.js.map +1 -1
  28. package/dist/accessi-module/Dtos/LoginResponse.js.map +1 -1
  29. package/dist/accessi-module/Dtos/Permission.js.map +1 -1
  30. package/dist/accessi-module/Dtos/RegisterRequest.js.map +1 -1
  31. package/dist/accessi-module/Dtos/Role.js.map +1 -1
  32. package/dist/accessi-module/Dtos/RuoliMenu.js.map +1 -1
  33. package/dist/accessi-module/Dtos/SetStatoRegistrazione.js.map +1 -1
  34. package/dist/accessi-module/Dtos/StatoRegistrazione.js.map +1 -1
  35. package/dist/accessi-module/Dtos/TipoAbilitazione.js.map +1 -1
  36. package/dist/accessi-module/Dtos/TipoFiltro.js.map +1 -1
  37. package/dist/accessi-module/Dtos/TokenResult.js.map +1 -1
  38. package/dist/accessi-module/Dtos/UpdateEnabledStatusRequest.js.map +1 -1
  39. package/dist/accessi-module/Dtos/UserDto.js.map +1 -1
  40. package/dist/accessi-module/Dtos/UserGrantsDto.js.map +1 -1
  41. package/dist/accessi-module/Dtos/index.js.map +1 -1
  42. package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
  43. package/dist/accessi-module/Services/ConfiguratorService/ConfiguratorService.js.map +1 -1
  44. package/dist/accessi-module/Services/EmailService/EmailService.js +11 -11
  45. package/dist/accessi-module/Services/EmailService/EmailService.js.map +1 -1
  46. package/dist/accessi-module/Services/FiltriService/FiltriService.js.map +1 -1
  47. package/dist/accessi-module/Services/PermissionService/PermissionService.js +118 -118
  48. package/dist/accessi-module/Services/PermissionService/PermissionService.js.map +1 -1
  49. package/dist/accessi-module/Services/ServiceTokenService/ServiceTokenService.js +4 -4
  50. package/dist/accessi-module/Services/ServiceTokenService/ServiceTokenService.js.map +1 -1
  51. package/dist/accessi-module/Services/UserService/UserService.js +72 -72
  52. package/dist/accessi-module/Services/UserService/UserService.js.map +1 -1
  53. package/dist/accessi-module/Views/reset-password.html +213 -213
  54. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.d.ts +5 -0
  55. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js +82 -46
  56. package/dist/accessi-module/database-updates/AccessiDatabaseUpdater.js.map +1 -1
  57. package/dist/accessi-module/database-updates/runAccessiDbUpdate.js +9 -1
  58. package/dist/accessi-module/database-updates/runAccessiDbUpdate.js.map +1 -1
  59. package/dist/accessi-module/index.js.map +1 -1
  60. package/dist/accessi-module/jwt/jwt.strategy.js.map +1 -1
  61. package/dist/accessi-module/middleware/accessiRequirements.js.map +1 -1
  62. package/dist/accessi-module/middleware/authenticateGen.js.map +1 -1
  63. package/dist/accessi-module/security/accessControl.js.map +1 -1
  64. package/dist/accessi-module/security/authenticatedToken.js.map +1 -1
  65. package/dist/accessi-module/security/passwordResetToken.js.map +1 -1
  66. package/dist/accessi-module/security/publicAuthRateLimit.js.map +1 -1
  67. package/dist/allegati-module/AllegatiModule.js.map +1 -1
  68. package/dist/allegati-module/Controllers/AllegatiController.js.map +1 -1
  69. package/dist/allegati-module/Dtos/AllegatoDto.js.map +1 -1
  70. package/dist/allegati-module/Dtos/BaseResponse.js.map +1 -1
  71. package/dist/allegati-module/Dtos/UploadSingleFileRequest.js.map +1 -1
  72. package/dist/allegati-module/Dtos/index.js.map +1 -1
  73. package/dist/allegati-module/Dtos/responses/DownloadAllegatoResponseDto.js.map +1 -1
  74. package/dist/allegati-module/Dtos/responses/GetListResponse.js.map +1 -1
  75. package/dist/allegati-module/Dtos/responses/UploadAllegatoResponseDto.js.map +1 -1
  76. package/dist/allegati-module/Services/AllegatiService/AllegatiService.js +49 -49
  77. package/dist/allegati-module/Services/AllegatiService/AllegatiService.js.map +1 -1
  78. package/dist/allegati-module/index.js.map +1 -1
  79. package/dist/autobind.js.map +1 -1
  80. package/dist/emilsoftware-utilities-2.0.0-dev.26.tgz +0 -0
  81. package/dist/firebird-compat.js.map +1 -1
  82. package/dist/index.js.map +1 -1
  83. package/dist/initEmilsoftwareModule.js.map +1 -1
  84. package/dist/swagger/SwaggerConfig.js.map +1 -1
  85. package/package.json +103 -103
  86. package/dist/accessi-module/Console/generated/model/abilitazioneMenuIcona.d.ts +0 -12
  87. package/dist/accessi-module/Console/generated/model/abilitazioneMenuIcona.js +0 -9
  88. package/dist/accessi-module/Console/generated/model/abilitazioneMenuIcona.js.map +0 -1
  89. package/dist/accessi-module/Console/generated/model/abilitazioneMenuNote.d.ts +0 -12
  90. package/dist/accessi-module/Console/generated/model/abilitazioneMenuNote.js +0 -9
  91. package/dist/accessi-module/Console/generated/model/abilitazioneMenuNote.js.map +0 -1
  92. package/dist/accessi-module/Console/generated/model/abilitazioneMenuPagina.d.ts +0 -12
  93. package/dist/accessi-module/Console/generated/model/abilitazioneMenuPagina.js +0 -9
  94. package/dist/accessi-module/Console/generated/model/abilitazioneMenuPagina.js.map +0 -1
  95. package/dist/accessi-module/Console/generated/model/abilitazioneMenuTipo.d.ts +0 -12
  96. package/dist/accessi-module/Console/generated/model/abilitazioneMenuTipo.js +0 -9
  97. package/dist/accessi-module/Console/generated/model/abilitazioneMenuTipo.js.map +0 -1
  98. package/dist/accessi-module/Console/generated/model/publicRegisterRequestCellulare.d.ts +0 -13
  99. package/dist/accessi-module/Console/generated/model/publicRegisterRequestCellulare.js +0 -9
  100. package/dist/accessi-module/Console/generated/model/publicRegisterRequestCellulare.js.map +0 -1
  101. package/dist/accessi-module/Console/generated/model/registerRequestCellulare.d.ts +0 -13
  102. package/dist/accessi-module/Console/generated/model/registerRequestCellulare.js +0 -9
  103. package/dist/accessi-module/Console/generated/model/registerRequestCellulare.js.map +0 -1
  104. package/dist/accessi-module/Console/generated/model/userDtoAvatar.d.ts +0 -13
  105. package/dist/accessi-module/Console/generated/model/userDtoAvatar.js +0 -9
  106. package/dist/accessi-module/Console/generated/model/userDtoAvatar.js.map +0 -1
  107. package/dist/accessi-module/Console/generated/model/userDtoCellulare.d.ts +0 -13
  108. package/dist/accessi-module/Console/generated/model/userDtoCellulare.js +0 -9
  109. package/dist/accessi-module/Console/generated/model/userDtoCellulare.js.map +0 -1
  110. package/dist/accessi-module/Console/openApiClient.d.ts +0 -34
  111. package/dist/accessi-module/Console/openApiClient.js +0 -111
  112. package/dist/accessi-module/Console/openApiClient.js.map +0 -1
package/README.MD CHANGED
@@ -1,182 +1,185 @@
1
- # Emilsoftware Utilities
2
-
3
- Documentazione completa del modulo Accessi, generata da `src/accessi-module/Console/wiki.ts` (stessa fonte della Wiki nella console):
4
-
5
- - **[WIKI.md](WIKI.md)** — guida completa con indice, esempi corretti e antipattern.
6
- - **[WIKI.ai.md](WIKI.ai.md)** — versione compatta da fornire a un'IA che costruisce il backend.
7
-
8
- Rigenera entrambi con `npm run generate:accessi-wiki` (incluso in `npm run build`).
9
-
10
- ---
11
-
12
- Convenzioni per i Commit Message
13
-
14
- 1. feat:
15
- Descrizione: Nuova feature
16
- Esempio: feat: aggiunta endpoint per notifiche
17
- Trigger: MINOR release (es. 1.3.0 -> 1.4.0)
18
- Su dev: 1.4.0-dev.0
19
-
20
- 2. fix:
21
- Descrizione: Correzione di bug
22
- Esempio: fix: gestione errore null in login
23
- Trigger: PATCH release (es. 1.3.1)
24
- Su dev: 1.3.1-dev.0
25
-
26
- 3. BREAKING CHANGE:
27
- Descrizione: Cambiamento che rompe la compatibilità all'indietro
28
- Esempio:
29
- feat!: rimozione supporto legacy
30
- BREAKING CHANGE: cambia lo schema JWT
31
- Trigger: MAJOR release (es. 1.0.0 -> 2.0.0)
32
-
33
- 4. Altri tipi di commit
34
- Esempi: chore:, docs:, test:, refactor:
35
- Descrizione: Non triggerano una release di default, a meno che non siano configurati esplicitamente nel commit-analyzer.
36
- Esempio: chore: aggiorna dipendenze
37
-
38
- 5. Forzare una Release
39
- Descrizione: Utile in caso di merge o altre operazioni.
40
- Comandi:
41
- git commit --allow-empty -m "feat: trigger test release"
42
- Poi:
43
- git push origin dev
44
- Oppure:
45
- git push origin main
46
-
47
- ## Accessi
48
-
49
- La documentazione operativa del modulo accessi si trova in [src/accessi-module/README.md](src/accessi-module/README.md).
50
-
51
- ## Test e qualita
52
-
53
- | Comando | Descrizione |
54
- | --- | --- |
55
- | `npm run typecheck` | Typecheck TypeScript in modalita `strict` (piu flag di igiene) |
56
- | `npm run lint` | Lint dependency-free (no `any`, no `@ts-ignore`, no `console.*` fuori dal logger) |
57
- | `npm test` | Typecheck + lint + test unitari |
58
- | `npm run test:unit` | Solo test unitari (`node --test`) |
59
- | `npm run test:coverage` | Test unitari con report e **soglie minime** di copertura |
60
- | `npm run test:integration` | Integration test su Firebird reale (default: porta 3055) |
61
- | `npm run test:integration:fb5` / `:fb3` | Integration test su Firebird 5 / 3 |
62
- | `npm run test:accessi-console` | Test end-to-end della console (richiede un browser Chromium e la build) |
63
-
64
- ### Integration test su Firebird reale
65
-
66
- Avvia i database di test (Firebird 5 sulla 3055, Firebird 3 sulla 3056) e lancia la suite:
67
-
68
- ```bash
69
- docker compose -f docker-compose.test.yml up -d
70
- npm run test:integration:fb5
71
- npm run test:integration:fb3
72
- docker compose -f docker-compose.test.yml down -v
73
- ```
74
-
75
- Il database viene creato dal test stesso (`attachOrCreate`) e lo schema costruito dal migrator
76
- della libreria (`AccessiDatabaseUpdater`), quindi gli integration test validano anche migrazione e
77
- compatibilita dello schema. Senza un Firebird raggiungibile i test vengono marcati come `skip`; con
78
- `ACCESSI_TEST_REQUIRE_DB=1` falliscono invece di essere saltati (usato dalla CI).
79
-
80
- Variabili: `ACCESSI_TEST_DB_HOST`, `ACCESSI_TEST_DB_PORT`, `ACCESSI_TEST_DB_DATABASE`,
81
- `ACCESSI_TEST_DB_USER`, `ACCESSI_TEST_DB_PASSWORD`, `ACCESSI_TEST_DB_WIRE_CRYPT`,
82
- `ACCESSI_TEST_DB_AUTH_PLUGIN`.
83
-
84
- I container usano le porte host **3055** e **3056** per non entrare in conflitto con un Firebird
85
- locale spesso in ascolto sulla 3050. Per cambiare porta/percorso, modifica i mapping in
86
- `docker-compose.test.yml` e passa `ACCESSI_TEST_DB_PORT` / `ACCESSI_TEST_DB_DATABASE`.
87
-
88
- La CI (`.github/workflows/ci.yml`) esegue typecheck, test con coverage e build, piu un job di
89
- integration test in matrice su Firebird 5 e 3. Il workflow di release (`publish.yml`) esegue
90
- typecheck e test prima di build e pubblicazione.
91
-
92
- ## Service token (macchina-a-macchina)
93
-
94
- Credenziali di servizio per chiamate tecniche, **non legate a un utente** e senza login giornaliero.
95
- Sostituiscono l'uso di un `secret` nel body: si inviano con `Authorization: Bearer <token>`.
96
-
97
- - Formato: `st_<id>.<segreto>`. Il segreto e mostrato **una sola volta** alla creazione/rotazione.
98
- - Storage: tabella `ACCESSI_SERVICE_TOKEN` (schema Accessi, migrazione automatica `1.7.0`). Il segreto e salvato solo come hash SHA-256 e confrontato a tempo costante.
99
- - Metadati e audit: descrizione, scope, creazione/scadenza, ultimo utilizzo con **IP** (`lastUsedIp`), revoca con **autore** (`revokedBy`).
100
- - Rate limit anti-abuso per IP sulla verifica (HTTP 429).
101
- - API amministrative (solo superutente): `POST /api/accessi/service-token`, `GET /api/accessi/service-token` (`includeRevoked=true`), `DELETE /api/accessi/service-token/:tokenId`, `POST /api/accessi/service-token/:tokenId/rotate`.
102
- - Console: sezione **Token di servizio** (crea, ruota, revoca; copia il token una sola volta).
103
- - Utilizzo lato consumer: guard `ServiceTokenGuard` con `@RequireServiceTokenScopes('ia')`; il token verificato e disponibile con `getAccessiServiceToken(req)`.
104
- - Opzione: `serviceTokens: { enabled?: boolean; defaultTtlDays?: number }` in `AccessiOptions`.
105
-
106
- ```ts
107
- import { Module } from '@nestjs/common';
108
- import { ServiceTokenGuard, RequireServiceTokenScopes } from 'emilsoftware-utilities';
109
-
110
- @UseGuards(ServiceTokenGuard)
111
- @RequireServiceTokenScopes('ia')
112
- @Controller('ia')
113
- export class IaController {}
114
- ```
115
-
116
- ## Hardening e opzioni
117
-
118
- - I log delle query **non includono i valori dei parametri** (segreti, credenziali, PII). Per riattivare esplicitamente i valori: `logQueryParameters: true` nelle `databaseOptions`.
119
- - Pool di connessioni **attivo di default** (`poolSize` default `5`); `poolSize: 0` disabilita e torna a una connessione per query. Gli script di breve durata devono chiamare `await Orm.closePools()` prima di uscire.
120
- - I JWT sono verificati con algoritmo fissato a `HS256` ed e obbligatoria una scadenza (`expiresIn`) valida.
121
- - **Chiavi JWT separate per scopo** (`access`, `reset`, `2fa`), derivate dal segreto configurato; il claim `typ: 'access'` e **obbligatorio** per gli access token.
122
- - La backdoor mock `admin`/`demo` non e mai attiva quando `NODE_ENV=production`.
123
- - Gli allegati sono **protetti di default**: senza `allegatiOptions.authorize` le richieste rispondono 403. Per la migrazione da versioni precedenti impostare `requireAuthorization: false` (sconsigliato in produzione).
124
- - Upload allegati: limite di 1 file / 90 MB applicato da Multer e validazione dei magic byte oltre al mimetype dichiarato.
125
-
126
- ## Integrazione
127
-
128
- | Scenario | API |
129
- | --- | --- |
130
- | Backend Express esistente | `await initializeAccessiModule(app, accessiOptions)` monta le rotte `/api/accessi` su un host Nest isolato |
131
- | Applicazione NestJS | importa `AccessiModule.forRoot(accessiOptions)` (e/o `AllegatiModule.forRoot(allegatiOptions)`) |
132
- | Accessi + Allegati insieme | `EmilsoftwareModule.forRoot({ accessiOptions, allegatiOptions })` |
133
-
134
- Tutta la configurazione e riservata al backend: segreti JWT/SMTP/DB non devono mai raggiungere il browser.
135
-
136
- ## Riferimento `AccessiOptions`
137
-
138
- | Campo | Tipo | Note |
139
- | --- | --- | --- |
140
- | `databaseOptions` | `Options` | Connessione Firebird (`DatabaseUtilities.createOption`). Supporta `logQueryParameters`, `poolSize`, `wireCrypt`, `pluginName`. |
141
- | `confirmationEmailUrl` / `confirmationEmailReturnUrl` | `string` | URL per le email di reset. |
142
- | `customResetPage` / `confirmationEmailPrefix` | `string?` | Pagina di reset personalizzata. |
143
- | `encryptionKey` | `string` | Solo compatibilita/migrazione password legacy cifrate. |
144
- | `mockDemoUser` | `boolean` | Utenti fittizi `admin`/`demo`; **mai** attivi in produzione. |
145
- | `passwordExpiration` / `passwordExpirationDays` | `boolean?` / `number?` | Scadenza password (default 90 giorni). |
146
- | `legacyPasswordMigrationOnStartup` | `boolean?` | Migrazione password legacy al bootstrap (default `true`). |
147
- | `autoUpdateDatabase` | `boolean?` | Riconcilia lo schema al bootstrap (default `true`); con `false` verifica in sola lettura. |
148
- | `jwtOptions` | `{ secret: string; expiresIn: string }` | HS256 con scadenza obbligatoria. |
149
- | `emailOptions` | `EmailOptions` | SMTP per reset e codici 2FA. |
150
- | `publicAuthRateLimit` | `PublicAuthRateLimitOptions?` | Limiti degli endpoint pubblici (default attivi). |
151
- | `publicRegistration` | `{ enabled?: boolean }?` | Registrazione pubblica opt-in. |
152
- | `federatedAuthentication` | `{ enabled?; allowSelfRegistration? }?` | SSO provider-agnostico. |
153
- | `extensionFieldsOptions` | `ExtensionFieldsOptions[]?` | Campi applicativi estesi. |
154
- | `serviceTokens` | `{ enabled?: boolean; defaultTtlDays?: number }?` | Token macchina-a-macchina (default abilitati). |
155
-
156
- ## Riferimento `AllegatiOptions`
157
-
158
- | Campo | Tipo | Note |
159
- | --- | --- | --- |
160
- | `databaseOptions` | `Options` | Connessione Firebird. |
161
- | `authorize` | `(req) => boolean \| Promise<boolean>?` | Controllo di autorizzazione applicato a ogni richiesta; `false` → 403. |
162
- | `requireAuthorization` | `boolean?` | **Default `true`**: senza `authorize` le richieste sono rifiutate con 403 (secure by default). `false` ripristina il comportamento storico (endpoint aperti) durante la migrazione. |
163
-
164
- ## Contratto risposte ed errori
165
-
166
- - Successo generico: `RestUtilities.sendOKMessage` → `{ severity, status, statusCode, message }`.
167
- - Payload: `RestUtilities.sendBaseResponse` → `{ Status: { errorCode, errorDescription }, Result, Message }`.
168
- - Errore: `{ severity: 'error', status, statusCode, code, message, error, details? }`, con `code` stabile (es. `ACCESSI_UNAUTHORIZED`, `ACCESSI_INVALID_CREDENTIALS`, `PASSWORD_EXPIRED`, `ACCESSI_DATABASE_SCHEMA_OUTDATED`, `ACCESSI_SERVICE_TOKEN_RATE_LIMITED`). Lo status deriva dalla `HttpException` (validazione → 400); errori di schema DB → 503. SQL, stack e dati sensibili non vengono mai esposti.
169
-
170
- ## Migrazioni e schema
171
-
172
- - Lo schema Accessi e versionato in `accessiSchema.ts` (`ACCESSI_SCHEMA_VERSION`) e riconciliato in modo **idempotente e ripartibile** da `AccessiDatabaseUpdater` (nessun DDL distruttivo, FK/indici/trigger verificati, sequence avanzate solo in avanti).
173
- - La versione viene aggiornata **solo dopo** `assertCompatible`; su errore la migrazione si puo rieseguire.
174
- - Firebird non rende utilizzabili gli oggetti creati nella stessa transazione, quindi il DDL non e racchiuso in un'unica transazione: la robustezza e data da idempotenza + verifica finale.
175
- - Con `autoUpdateDatabase: false` la libreria verifica lo schema in sola lettura e blocca l'avvio se incompatibile (`AccessiDatabaseUpdater.inspectSchema`).
176
-
177
- ## Osservabilita
178
-
179
- - `Logger` strutturato (console colorata + file JSON giornaliero), condiviso per directory.
180
- - Livello minimo configurabile per istanza (`new Logger(tag, { level: 'info' })`) o globalmente con la variabile `LOG_LEVEL` (default `database` = tutto).
181
- - Redazione automatica dei campi sensibili (`password`, `token`, `secret`, ...) e serializzazione degli `Error` con stack.
182
- - I log delle query non contengono i valori dei parametri (salvo `logQueryParameters: true`).
1
+ # Emilsoftware Utilities
2
+
3
+ Documentazione completa del modulo Accessi, generata da `src/accessi-module/Console/wiki.ts` (stessa fonte della Wiki nella console):
4
+
5
+ - **[WIKI.md](WIKI.md)** — guida completa con indice, esempi corretti e antipattern.
6
+ - **[WIKI.ai.md](WIKI.ai.md)** — versione compatta da fornire a un'IA che costruisce il backend.
7
+
8
+ Rigenera entrambi con `npm run generate:accessi-wiki` (incluso in `npm run build`).
9
+
10
+ ---
11
+
12
+ Convenzioni per i Commit Message
13
+
14
+ 1. feat:
15
+ Descrizione: Nuova feature
16
+ Esempio: feat: aggiunta endpoint per notifiche
17
+ Trigger: MINOR release (es. 1.3.0 -> 1.4.0)
18
+ Su dev: 1.4.0-dev.0
19
+
20
+ 2. fix:
21
+ Descrizione: Correzione di bug
22
+ Esempio: fix: gestione errore null in login
23
+ Trigger: PATCH release (es. 1.3.1)
24
+ Su dev: 1.3.1-dev.0
25
+
26
+ 3. BREAKING CHANGE:
27
+ Descrizione: Cambiamento che rompe la compatibilità all'indietro
28
+ Esempio:
29
+ feat!: rimozione supporto legacy
30
+ BREAKING CHANGE: cambia lo schema JWT
31
+ Trigger: MAJOR release (es. 1.0.0 -> 2.0.0)
32
+
33
+ 4. Altri tipi di commit
34
+ Esempi: chore:, docs:, test:, refactor:
35
+ Descrizione: Non triggerano una release di default, a meno che non siano configurati esplicitamente nel commit-analyzer.
36
+ Esempio: chore: aggiorna dipendenze
37
+
38
+ 5. Forzare una Release
39
+ Descrizione: Utile in caso di merge o altre operazioni.
40
+ Comandi:
41
+ git commit --allow-empty -m "feat: trigger test release"
42
+ Poi:
43
+ git push origin dev
44
+ Oppure:
45
+ git push origin main
46
+
47
+ ## Accessi
48
+
49
+ La documentazione operativa del modulo accessi si trova in [src/accessi-module/README.md](src/accessi-module/README.md).
50
+
51
+ ## Test e qualita
52
+
53
+ | Comando | Descrizione |
54
+ | --- | --- |
55
+ | `npm run typecheck` | Typecheck TypeScript in modalita `strict` (piu flag di igiene) |
56
+ | `npm run lint` | Lint dependency-free (no `any`, no `@ts-ignore`, no `console.*` fuori dal logger) |
57
+ | `npm test` | Typecheck + lint + test unitari |
58
+ | `npm run test:unit` | Solo test unitari (`node --test`) |
59
+ | `npm run test:coverage` | Test unitari con report e **soglie minime** di copertura |
60
+ | `npm run test:integration` | Integration test su Firebird reale (default: porta 3055) |
61
+ | `npm run test:integration:fb5` / `:fb3` / `:fb25` | Integration test su Firebird 5 / 3 / 2.5 |
62
+ | `npm run test:accessi-console` | Test end-to-end della console (richiede un browser Chromium e la build) |
63
+
64
+ ### Integration test su Firebird reale
65
+
66
+ Avvia i database di test (Firebird 5 sulla 3055, Firebird 3 sulla 3056, Firebird 2.5 sulla 3057) e lancia la suite:
67
+
68
+ ```bash
69
+ docker compose -f docker-compose.test.yml up -d
70
+ npm run test:integration:fb5
71
+ npm run test:integration:fb3
72
+ npm run test:integration:fb25
73
+ docker compose -f docker-compose.test.yml down -v
74
+ ```
75
+
76
+ Firebird 2.5 usa l'immagine `jacobalberty/firebird:v2.5.9-ss` (l'immagine ufficiale non copre la 2.5) con `Legacy_Auth` e wire crypt disabilitato: lo script `test:integration:fb25` imposta da solo plugin e wireCrypt.
77
+
78
+ Il database viene creato dal test stesso (`attachOrCreate`) e lo schema costruito dal migrator
79
+ della libreria (`AccessiDatabaseUpdater`), quindi gli integration test validano anche migrazione e
80
+ compatibilita dello schema. Senza un Firebird raggiungibile i test vengono marcati come `skip`; con
81
+ `ACCESSI_TEST_REQUIRE_DB=1` falliscono invece di essere saltati (usato dalla CI).
82
+
83
+ Variabili: `ACCESSI_TEST_DB_HOST`, `ACCESSI_TEST_DB_PORT`, `ACCESSI_TEST_DB_DATABASE`,
84
+ `ACCESSI_TEST_DB_USER`, `ACCESSI_TEST_DB_PASSWORD`, `ACCESSI_TEST_DB_WIRE_CRYPT`,
85
+ `ACCESSI_TEST_DB_AUTH_PLUGIN`.
86
+
87
+ I container usano le porte host **3055** e **3056** per non entrare in conflitto con un Firebird
88
+ locale spesso in ascolto sulla 3050. Per cambiare porta/percorso, modifica i mapping in
89
+ `docker-compose.test.yml` e passa `ACCESSI_TEST_DB_PORT` / `ACCESSI_TEST_DB_DATABASE`.
90
+
91
+ La CI (`.github/workflows/ci.yml`) esegue typecheck, test con coverage e build, piu un job di
92
+ integration test in matrice su Firebird 5 e 3. Il workflow di release (`publish.yml`) esegue
93
+ typecheck e test prima di build e pubblicazione.
94
+
95
+ ## Service token (macchina-a-macchina)
96
+
97
+ Credenziali di servizio per chiamate tecniche, **non legate a un utente** e senza login giornaliero.
98
+ Sostituiscono l'uso di un `secret` nel body: si inviano con `Authorization: Bearer <token>`.
99
+
100
+ - Formato: `st_<id>.<segreto>`. Il segreto e mostrato **una sola volta** alla creazione/rotazione.
101
+ - Storage: tabella `ACCESSI_SERVICE_TOKEN` (schema Accessi, migrazione automatica `1.7.0`). Il segreto e salvato solo come hash SHA-256 e confrontato a tempo costante.
102
+ - Metadati e audit: descrizione, scope, creazione/scadenza, ultimo utilizzo con **IP** (`lastUsedIp`), revoca con **autore** (`revokedBy`).
103
+ - Rate limit anti-abuso per IP sulla verifica (HTTP 429).
104
+ - API amministrative (solo superutente): `POST /api/accessi/service-token`, `GET /api/accessi/service-token` (`includeRevoked=true`), `DELETE /api/accessi/service-token/:tokenId`, `POST /api/accessi/service-token/:tokenId/rotate`.
105
+ - Console: sezione **Token di servizio** (crea, ruota, revoca; copia il token una sola volta).
106
+ - Utilizzo lato consumer: guard `ServiceTokenGuard` con `@RequireServiceTokenScopes('ia')`; il token verificato e disponibile con `getAccessiServiceToken(req)`.
107
+ - Opzione: `serviceTokens: { enabled?: boolean; defaultTtlDays?: number }` in `AccessiOptions`.
108
+
109
+ ```ts
110
+ import { Module } from '@nestjs/common';
111
+ import { ServiceTokenGuard, RequireServiceTokenScopes } from 'emilsoftware-utilities';
112
+
113
+ @UseGuards(ServiceTokenGuard)
114
+ @RequireServiceTokenScopes('ia')
115
+ @Controller('ia')
116
+ export class IaController {}
117
+ ```
118
+
119
+ ## Hardening e opzioni
120
+
121
+ - I log delle query **non includono i valori dei parametri** (segreti, credenziali, PII). Per riattivare esplicitamente i valori: `logQueryParameters: true` nelle `databaseOptions`.
122
+ - Pool di connessioni **attivo di default** (`poolSize` default `5`); `poolSize: 0` disabilita e torna a una connessione per query. Gli script di breve durata devono chiamare `await Orm.closePools()` prima di uscire.
123
+ - I JWT sono verificati con algoritmo fissato a `HS256` ed e obbligatoria una scadenza (`expiresIn`) valida.
124
+ - **Chiavi JWT separate per scopo** (`access`, `reset`, `2fa`), derivate dal segreto configurato; il claim `typ: 'access'` e **obbligatorio** per gli access token.
125
+ - La backdoor mock `admin`/`demo` non e mai attiva quando `NODE_ENV=production`.
126
+ - Gli allegati sono **protetti di default**: senza `allegatiOptions.authorize` le richieste rispondono 403. Per la migrazione da versioni precedenti impostare `requireAuthorization: false` (sconsigliato in produzione).
127
+ - Upload allegati: limite di 1 file / 90 MB applicato da Multer e validazione dei magic byte oltre al mimetype dichiarato.
128
+
129
+ ## Integrazione
130
+
131
+ | Scenario | API |
132
+ | --- | --- |
133
+ | Backend Express esistente | `await initializeAccessiModule(app, accessiOptions)` monta le rotte `/api/accessi` su un host Nest isolato |
134
+ | Applicazione NestJS | importa `AccessiModule.forRoot(accessiOptions)` (e/o `AllegatiModule.forRoot(allegatiOptions)`) |
135
+ | Accessi + Allegati insieme | `EmilsoftwareModule.forRoot({ accessiOptions, allegatiOptions })` |
136
+
137
+ Tutta la configurazione e riservata al backend: segreti JWT/SMTP/DB non devono mai raggiungere il browser.
138
+
139
+ ## Riferimento `AccessiOptions`
140
+
141
+ | Campo | Tipo | Note |
142
+ | --- | --- | --- |
143
+ | `databaseOptions` | `Options` | Connessione Firebird (`DatabaseUtilities.createOption`). Supporta `logQueryParameters`, `poolSize`, `wireCrypt`, `pluginName`. |
144
+ | `confirmationEmailUrl` / `confirmationEmailReturnUrl` | `string` | URL per le email di reset. |
145
+ | `customResetPage` / `confirmationEmailPrefix` | `string?` | Pagina di reset personalizzata. |
146
+ | `encryptionKey` | `string` | Solo compatibilita/migrazione password legacy cifrate. |
147
+ | `mockDemoUser` | `boolean` | Utenti fittizi `admin`/`demo`; **mai** attivi in produzione. |
148
+ | `passwordExpiration` / `passwordExpirationDays` | `boolean?` / `number?` | Scadenza password (default 90 giorni). |
149
+ | `legacyPasswordMigrationOnStartup` | `boolean?` | Migrazione password legacy al bootstrap (default `true`). |
150
+ | `autoUpdateDatabase` | `boolean?` | Riconcilia lo schema al bootstrap (default `true`); con `false` verifica in sola lettura. |
151
+ | `jwtOptions` | `{ secret: string; expiresIn: string }` | HS256 con scadenza obbligatoria. |
152
+ | `emailOptions` | `EmailOptions` | SMTP per reset e codici 2FA. |
153
+ | `publicAuthRateLimit` | `PublicAuthRateLimitOptions?` | Limiti degli endpoint pubblici (default attivi). |
154
+ | `publicRegistration` | `{ enabled?: boolean }?` | Registrazione pubblica opt-in. |
155
+ | `federatedAuthentication` | `{ enabled?; allowSelfRegistration? }?` | SSO provider-agnostico. |
156
+ | `extensionFieldsOptions` | `ExtensionFieldsOptions[]?` | Campi applicativi estesi. |
157
+ | `serviceTokens` | `{ enabled?: boolean; defaultTtlDays?: number }?` | Token macchina-a-macchina (default abilitati). |
158
+
159
+ ## Riferimento `AllegatiOptions`
160
+
161
+ | Campo | Tipo | Note |
162
+ | --- | --- | --- |
163
+ | `databaseOptions` | `Options` | Connessione Firebird. |
164
+ | `authorize` | `(req) => boolean \| Promise<boolean>?` | Controllo di autorizzazione applicato a ogni richiesta; `false` → 403. |
165
+ | `requireAuthorization` | `boolean?` | **Default `true`**: senza `authorize` le richieste sono rifiutate con 403 (secure by default). `false` ripristina il comportamento storico (endpoint aperti) durante la migrazione. |
166
+
167
+ ## Contratto risposte ed errori
168
+
169
+ - Successo generico: `RestUtilities.sendOKMessage` → `{ severity, status, statusCode, message }`.
170
+ - Payload: `RestUtilities.sendBaseResponse` → `{ Status: { errorCode, errorDescription }, Result, Message }`.
171
+ - Errore: `{ severity: 'error', status, statusCode, code, message, error, details? }`, con `code` stabile (es. `ACCESSI_UNAUTHORIZED`, `ACCESSI_INVALID_CREDENTIALS`, `PASSWORD_EXPIRED`, `ACCESSI_DATABASE_SCHEMA_OUTDATED`, `ACCESSI_SERVICE_TOKEN_RATE_LIMITED`). Lo status deriva dalla `HttpException` (validazione → 400); errori di schema DB → 503. SQL, stack e dati sensibili non vengono mai esposti.
172
+
173
+ ## Migrazioni e schema
174
+
175
+ - Lo schema Accessi e versionato in `accessiSchema.ts` (`ACCESSI_SCHEMA_VERSION`) e riconciliato in modo **idempotente e ripartibile** da `AccessiDatabaseUpdater` (nessun DDL distruttivo, FK/indici/trigger verificati, sequence avanzate solo in avanti).
176
+ - La versione viene aggiornata **solo dopo** `assertCompatible`; su errore la migrazione si puo rieseguire.
177
+ - Firebird non rende utilizzabili gli oggetti creati nella stessa transazione, quindi il DDL non e racchiuso in un'unica transazione: la robustezza e data da idempotenza + verifica finale.
178
+ - Con `autoUpdateDatabase: false` la libreria verifica lo schema in sola lettura e blocca l'avvio se incompatibile (`AccessiDatabaseUpdater.inspectSchema`).
179
+
180
+ ## Osservabilita
181
+
182
+ - `Logger` strutturato (console colorata + file JSON giornaliero), condiviso per directory.
183
+ - Livello minimo configurabile per istanza (`new Logger(tag, { level: 'info' })`) o globalmente con la variabile `LOG_LEVEL` (default `database` = tutto).
184
+ - Redazione automatica dei campi sensibili (`password`, `token`, `secret`, ...) e serializzazione degli `Error` con stack.
185
+ - I log delle query non contengono i valori dei parametri (salvo `logQueryParameters: true`).
@@ -1 +1 @@
1
- {"version":3,"file":"ContractGenerator.js","sourceRoot":"","sources":["../src/ContractGenerator.ts"],"names":[],"mappings":"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","sourcesContent":["import { jsPDF } from \"jspdf\";\r\nimport autotable, { Styles, UserOptions } from \"jspdf-autotable\";\r\nimport { promises as fs } from 'fs';\r\nimport path from 'path';\r\n\r\nexport interface IPartiContratto {\r\n fornitore: { denominazione: string; codiceFiscale: string; indirizzoCompleto: string; };\r\n cliente: { denominazione: string; codiceFiscale: string; indirizzoCompleto: string; };\r\n}\r\n\r\nexport interface DocumentParams {\r\n parti: IPartiContratto;\r\n tipOutput?: 'f' | 'd' | 'u';\r\n dynamicFields?: { [key: string]: string };\r\n dynamicElements?: { [placeholder: string]: DynamicElement };\r\n}\r\n\r\nexport type DynamicElement = {\r\n type: 'table';\r\n config: {\r\n head: string[];\r\n body: string[][];\r\n options?: UserOptions;\r\n styles?: Partial<Styles>;\r\n };\r\n};\r\n\r\ninterface DocumentConfig {\r\n fontTitolo: { nome: string; dimensione: number; colore: string; installPath?: string; };\r\n fontSottotitolo: { nome: string; dimensione: number; colore: string; installPath?: string; };\r\n fontTesto: { nome: string; dimensione: number; colore: string; installPath?: string; };\r\n fontBold: { nome: string; dimensione: number; colore: string; installPath?: string; };\r\n margini: { sx: number; dx: number; alto: number; basso: number; };\r\n staccoriga: number;\r\n rientro: number;\r\n testi: {\r\n titolo: string;\r\n premessa: string;\r\n Punti: Array<{ titolo: string; Sottopunti: Array<{ titolo: string; contenuto: string; }>; }>;\r\n versione: string;\r\n };\r\n immagini: Array<{ path: string; posizione: [number, number]; dimensioni: [number, number]; coeffDim?: number; }>;\r\n box: { backgrund: string; raggio: number; };\r\n tableStyle?: { lineColor?: string; lineWidth?: number; font?: string; fontSize?: number; cellPadding?: number; fillColor?: string; };\r\n}\r\n\r\nasync function loadImageAsBase64(imagePath: string): Promise<string> {\r\n const absolutePath = path.resolve(imagePath);\r\n const buffer = await fs.readFile(absolutePath);\r\n const ext = path.extname(imagePath).slice(1).toLowerCase();\r\n const base64 = buffer.toString('base64');\r\n return `data:image/${ext};base64,${base64}`;\r\n}\r\n\r\nexport class DocumentGenerator {\r\n private config!: DocumentConfig;\r\n private doc!: jsPDF;\r\n private curX: number = 0;\r\n private curY: number = 0;\r\n private configLoaded: boolean = false;\r\n\r\n constructor(private configPath?: string) { }\r\n\r\n public setConfig(config: DocumentConfig) {\r\n this.config = config;\r\n this.configLoaded = true;\r\n }\r\n\r\n private applyTemplate(template: string, dynamicFields?: { [key: string]: string }): string {\r\n if (!dynamicFields) return template;\r\n return template.replace(/\\$(\\w+)\\$/g, (match, key) =>\r\n dynamicFields[key] !== undefined ? dynamicFields[key] : match\r\n );\r\n }\r\n\r\n private applyPartiPlaceholders(text: string, parti: IPartiContratto): string {\r\n return text.replace(/\\$(fornitore|cliente):(\\w+)\\$/g, (match: string, party: string, field: string) => {\r\n const partyData = (parti as unknown as Record<string, Record<string, string> | undefined>)[party];\r\n const value = partyData?.[field];\r\n return value ?? match;\r\n });\r\n }\r\n\r\n private async loadConfig(): Promise<void> {\r\n if (this.configLoaded) return;\r\n if (!this.configPath) throw new Error(\"No configuration provided.\");\r\n try {\r\n const data = await fs.readFile(this.configPath, 'utf8');\r\n this.config = JSON.parse(data) as DocumentConfig;\r\n this.configLoaded = true;\r\n } catch (error) {\r\n throw new Error(`Error reading configuration file: ${error}`);\r\n }\r\n }\r\n\r\n private initDoc(): void {\r\n this.doc = new jsPDF();\r\n const margins = this.config.margini;\r\n this.curX = margins.sx;\r\n this.curY = margins.alto;\r\n }\r\n\r\n private extractPlaceholder(text: string): string | null {\r\n const match = text.match(/^\\$(\\w+)\\$$/);\r\n return match ? (match[1] ?? null) : null;\r\n }\r\n\r\n private async installFont(fontPath: string, fontName: string): Promise<void> {\r\n const absolutePath = path.resolve(fontPath);\r\n const buffer = await fs.readFile(absolutePath);\r\n const base64Font = buffer.toString('base64');\r\n //console.log(`Installing font ${fontName} from ${absolutePath}`);\r\n this.doc.addFileToVFS(`${fontName}.ttf`, base64Font);\r\n this.doc.addFont(`${fontName}.ttf`, fontName, 'normal');\r\n this.doc.setFont(fontName);\r\n }\r\n\r\n // Writes wrapped text, handling inline bold segments.\r\n private async writeWrappedTextCore(\r\n fontName: string,\r\n fontSize: number,\r\n color: string,\r\n text: string,\r\n x: number,\r\n y: number,\r\n maxWidth: number,\r\n lineSpacingFactor: number = 1.15\r\n ): Promise<number> {\r\n this.doc.setFont(fontName);\r\n this.doc.setFontSize(fontSize);\r\n this.doc.setTextColor(color);\r\n const lines = this.doc.splitTextToSize(text, maxWidth);\r\n const lineHeight = (fontSize * lineSpacingFactor / 72) * 25.4;\r\n for (const line of lines) {\r\n if (y + lineHeight > (this.doc.internal.pageSize.getHeight() - this.config.margini.basso)) {\r\n this.doc.addPage();\r\n y = this.config.margini.alto;\r\n }\r\n // If the line contains inline bold markers, process them.\r\n if (line.includes(\"**\")) {\r\n y = await this.writeLineWithInlineBold(line, x, y, fontName, fontSize, color, maxWidth, lineSpacingFactor);\r\n } else {\r\n this.doc.text(line, x, y);\r\n y += lineHeight;\r\n }\r\n }\r\n return y;\r\n }\r\n\r\n // Writes a single line with inline bold processing.\r\n private async writeLineWithInlineBold(\r\n line: string,\r\n x: number,\r\n y: number,\r\n normalFont: string,\r\n fontSize: number,\r\n color: string,\r\n _maxWidth: number,\r\n lineSpacingFactor: number = 1.15\r\n ): Promise<number> {\r\n let currentX = x;\r\n // Split the line by bold segments. The regex keeps the markers.\r\n const segments = line.split(/(\\*\\*.*?\\*\\*)/);\r\n const lineHeight = (fontSize * lineSpacingFactor / 72) * 25.4;\r\n for (const seg of segments) {\r\n let segText = seg;\r\n let useBold = false;\r\n if (seg.startsWith(\"**\") && seg.endsWith(\"**\")) {\r\n useBold = true;\r\n segText = seg.substring(2, seg.length - 2);\r\n }\r\n if (useBold) {\r\n this.doc.setFont(this.config.fontBold.nome);\r\n } else {\r\n this.doc.setFont(normalFont);\r\n }\r\n this.doc.setFontSize(fontSize);\r\n this.doc.setTextColor(color);\r\n const segWidth = this.doc.getTextWidth(segText);\r\n // Write segment\r\n this.doc.text(segText, currentX, y);\r\n currentX += segWidth;\r\n }\r\n return y + lineHeight;\r\n }\r\n\r\n private async writeWrappedTextWithFont(\r\n fontConf: { nome: string; dimensione: number; colore: string; installPath?: string },\r\n text: string,\r\n x: number,\r\n y: number,\r\n maxWidth: number,\r\n lineSpacingFactor: number = 1.15\r\n ): Promise<void> {\r\n const fontList = this.doc.getFontList();\r\n if (!fontList[fontConf.nome]) {\r\n if (fontConf.installPath) await this.installFont(fontConf.installPath, fontConf.nome);\r\n else throw new Error(\"Font not present and no installPath provided\");\r\n } else {\r\n this.doc.setFont(fontConf.nome);\r\n }\r\n this.curY = await this.writeWrappedTextCore(fontConf.nome, fontConf.dimensione, fontConf.colore, text, x, y, maxWidth, lineSpacingFactor);\r\n }\r\n\r\n public async writeBoxedText(\r\n text: string,\r\n fontConf: { nome: string; dimensione: number; colore: string; installPath?: string },\r\n padding: number = 5,\r\n borderRadius: number = 5,\r\n borderWidth: number = 1,\r\n boxColor?: string\r\n ): Promise<void> {\r\n const fontList = this.doc.getFontList();\r\n if (!fontList[fontConf.nome]) {\r\n if (fontConf.installPath) await this.installFont(fontConf.installPath, fontConf.nome);\r\n else throw new Error(\"Font not present and no installPath provided\");\r\n } else {\r\n this.doc.setFont(fontConf.nome);\r\n }\r\n this.doc.setFontSize(fontConf.dimensione);\r\n this.doc.setTextColor(fontConf.colore);\r\n const pageWidth = this.doc.internal.pageSize.getWidth();\r\n const maxTextWidth = pageWidth - this.config.margini.sx - this.config.margini.dx - 2 * padding;\r\n const lines = this.doc.splitTextToSize(text, maxTextWidth);\r\n const lineHeight = (fontConf.dimensione * 1.15 / 72) * 25.4;\r\n const textHeight = lines.length * lineHeight;\r\n const boxX = this.curX;\r\n const boxY = this.curY;\r\n const boxWidth = maxTextWidth + 2 * padding;\r\n const boxHeight = textHeight + 2 * padding;\r\n if (boxColor) {\r\n this.doc.setFillColor(boxColor);\r\n this.doc.roundedRect(boxX, boxY, boxWidth, boxHeight, borderRadius, borderRadius, \"F\");\r\n }\r\n this.doc.setLineWidth(borderWidth);\r\n this.doc.roundedRect(boxX, boxY, boxWidth, boxHeight, borderRadius, borderRadius);\r\n const textX = boxX + padding;\r\n let textY = boxY + padding + lineHeight;\r\n for (const line of lines) {\r\n if (line.includes(\"**\")) {\r\n textY = await this.writeLineWithInlineBold(line, textX, textY, fontConf.nome, fontConf.dimensione, fontConf.colore, maxTextWidth, 1.15);\r\n } else {\r\n this.doc.text(line, textX, textY);\r\n textY += lineHeight;\r\n }\r\n }\r\n this.curY = boxY + boxHeight + this.config.staccoriga;\r\n }\r\n\r\n\r\n // Checks if text is wrapped in '^' markers for boxed text.\r\n private isBoxedText(text: string): boolean {\r\n return text.startsWith('^') && text.endsWith('^');\r\n }\r\n\r\n // Removes '^' markers.\r\n private stripBoxMarkers(text: string): string {\r\n return text.substring(1, text.length - 1);\r\n }\r\n\r\n // Checks if text is entirely wrapped in '**' markers (not inline).\r\n // (This function is kept for backward compatibility.)\r\n private isBoldText(text: string): boolean {\r\n return text.startsWith('**') && text.endsWith('**');\r\n }\r\n\r\n // Removes '**' markers.\r\n private stripBoldMarkers(text: string): string {\r\n return text.substring(2, text.length - 2);\r\n }\r\n\r\n // Inserts images using the current cursor position.\r\n private async insertLogo(): Promise<void> {\r\n if (this.config.immagini && this.config.immagini.length > 0) {\r\n const startX = this.curX;\r\n for (const imgConf of this.config.immagini) {\r\n const format = imgConf.path.split('.').pop()?.toUpperCase() || 'PNG';\r\n const base64Image = await loadImageAsBase64(imgConf.path);\r\n const startY = this.curY;\r\n if (startY + imgConf.dimensioni[1] > (this.doc.internal.pageSize.getHeight() - this.config.margini.basso)) {\r\n this.doc.addPage();\r\n this.curY = this.config.margini.alto;\r\n }\r\n this.doc.addImage(base64Image, format, startX, this.curY, imgConf.dimensioni[0], imgConf.dimensioni[1]);\r\n //console.log(`Image inserted at X: ${startX}, Y: ${this.curY}`);\r\n this.curY = this.curY + imgConf.dimensioni[1] + this.config.staccoriga;\r\n }\r\n }\r\n }\r\n\r\n // Generates the PDF document by processing images, dynamic fields, tables, and text.\r\n // Supports inline bold formatting and boxed text.\r\n public async generateDocument(params: DocumentParams): Promise<string | ArrayBuffer> {\r\n await this.loadConfig();\r\n this.initDoc();\r\n await this.insertLogo();\r\n const dynamicFields = params.dynamicFields || {};\r\n const dynamicElements = params.dynamicElements || {};\r\n const pageWidth = this.doc.internal.pageSize.getWidth();\r\n const maxWidth = pageWidth - (this.config.margini.sx + this.config.margini.dx);\r\n let titolo = this.applyTemplate(this.config.testi.titolo, dynamicFields);\r\n titolo = this.applyPartiPlaceholders(titolo, params.parti);\r\n await this.writeWrappedTextWithFont(this.config.fontTitolo, titolo, this.curX, this.curY, maxWidth);\r\n this.curY += this.config.staccoriga;\r\n let premessa = this.applyTemplate(this.config.testi.premessa, dynamicFields);\r\n premessa = this.applyPartiPlaceholders(premessa, params.parti);\r\n await this.writeWrappedTextWithFont(this.config.fontTesto, premessa, this.curX, this.curY, maxWidth);\r\n this.curY += this.config.staccoriga;\r\n for (const punto of this.config.testi.Punti) {\r\n let puntoTitolo = this.applyTemplate(punto.titolo, dynamicFields);\r\n puntoTitolo = this.applyPartiPlaceholders(puntoTitolo, params.parti);\r\n await this.writeWrappedTextWithFont(this.config.fontTitolo, puntoTitolo, this.curX, this.curY, maxWidth);\r\n this.curY += this.config.staccoriga;\r\n for (const sub of punto.Sottopunti) {\r\n const placeholder = this.extractPlaceholder(sub.titolo);\r\n if (placeholder && dynamicElements[placeholder] && dynamicElements[placeholder].type === 'table' && dynamicElements[placeholder].config) {\r\n const tableConfig = dynamicElements[placeholder].config!;\r\n const defaultTableOptions = this.config.tableStyle || {};\r\n autotable(this.doc, {\r\n startY: this.curY,\r\n head: [tableConfig.head],\r\n body: tableConfig.body,\r\n ...defaultTableOptions,\r\n ...tableConfig.options,\r\n ...tableConfig.styles\r\n });\r\n this.curY = ((this.doc as unknown) as { lastAutoTable: { finalY: number } }).lastAutoTable.finalY + this.config.staccoriga;\r\n } else {\r\n if (sub.titolo) {\r\n let titleText = this.applyTemplate(sub.titolo, dynamicFields);\r\n titleText = this.applyPartiPlaceholders(titleText, params.parti);\r\n if (this.isBoxedText(titleText)) {\r\n const boxText = this.stripBoxMarkers(titleText);\r\n await this.writeBoxedText(boxText, this.config.fontSottotitolo);\r\n } else if (this.isBoldText(titleText)) {\r\n const boldText = this.stripBoldMarkers(titleText);\r\n\r\n await this.writeWrappedTextWithFont({ ...this.config.fontSottotitolo, nome: this.config.fontSottotitolo.nome },\r\n boldText, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\r\n } else {\r\n await this.writeWrappedTextWithFont(this.config.fontSottotitolo, titleText, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\r\n }\r\n this.curY += this.config.staccoriga;\r\n }\r\n }\r\n const contentPlaceholder = this.extractPlaceholder(sub.contenuto);\r\n if (contentPlaceholder && dynamicElements[contentPlaceholder] && dynamicElements[contentPlaceholder].type === 'table' && dynamicElements[contentPlaceholder].config) {\r\n const tableConfig = dynamicElements[contentPlaceholder].config!;\r\n const defaultTableOptions = this.config.tableStyle || {};\r\n autotable(this.doc, {\r\n startY: this.curY,\r\n head: [tableConfig.head],\r\n body: tableConfig.body,\r\n ...defaultTableOptions,\r\n ...tableConfig.options,\r\n ...tableConfig.styles\r\n });\r\n this.curY = ((this.doc as unknown) as { lastAutoTable: { finalY: number } }).lastAutoTable.finalY + this.config.staccoriga;\r\n } else if (sub.contenuto) {\r\n let contenuto = this.applyTemplate(sub.contenuto, dynamicFields);\r\n contenuto = this.applyPartiPlaceholders(contenuto, params.parti);\r\n if (this.isBoxedText(contenuto)) {\r\n const boxText = this.stripBoxMarkers(contenuto);\r\n await this.writeBoxedText(boxText, this.config.fontTesto);\r\n } else if (this.isBoldText(contenuto)) {\r\n const boldText = this.stripBoldMarkers(contenuto);\r\n await this.writeWrappedTextWithFont({ ...this.config.fontTesto, nome: this.config.fontTesto.nome },\r\n boldText, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\r\n } else {\r\n await this.writeWrappedTextWithFont(this.config.fontTesto, contenuto, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\r\n }\r\n this.curY += this.config.staccoriga;\r\n }\r\n }\r\n this.curY += this.config.staccoriga;\r\n }\r\n const tipOutput = params.tipOutput || 'd';\r\n // Sanitizza il nome: evita path traversal e caratteri non validi nel filesystem.\r\n const safeDenominazione = params.parti.cliente.denominazione\r\n .replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]/g, '_')\r\n .replace(/\\.+$/, '')\r\n .trim() || 'cliente';\r\n const fileName = `Documento_${safeDenominazione}.pdf`;\r\n if (tipOutput === 'd') {\r\n const pdfBuffer = this.doc.output('arraybuffer');\r\n await fs.writeFile(fileName, Buffer.from(pdfBuffer));\r\n return fileName;\r\n } else if (tipOutput === 'u') {\r\n return this.doc.output('arraybuffer');\r\n } else {\r\n return this.doc.output('arraybuffer');\r\n }\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"ContractGenerator.js","sourceRoot":"","sources":["../src/ContractGenerator.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,iCAA8B;AAC9B,sEAAiE;AACjE,2BAAoC;AACpC,gDAAwB;AA2CxB,SAAe,iBAAiB,CAAC,SAAiB;;QAC9C,MAAM,YAAY,GAAG,cAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,aAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,cAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,cAAc,GAAG,WAAW,MAAM,EAAE,CAAC;IAChD,CAAC;CAAA;AAED,MAAa,iBAAiB;IAO1B,YAAoB,UAAmB;QAAnB,eAAU,GAAV,UAAU,CAAS;QAJ/B,SAAI,GAAW,CAAC,CAAC;QACjB,SAAI,GAAW,CAAC,CAAC;QACjB,iBAAY,GAAY,KAAK,CAAC;IAEK,CAAC;IAErC,SAAS,CAAC,MAAsB;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC7B,CAAC;IAEO,aAAa,CAAC,QAAgB,EAAE,aAAyC;QAC7E,IAAI,CAAC,aAAa;YAAE,OAAO,QAAQ,CAAC;QACpC,OAAO,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CACjD,aAAa,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAChE,CAAC;IACN,CAAC;IAEO,sBAAsB,CAAC,IAAY,EAAE,KAAsB;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAAE,CAAC,KAAa,EAAE,KAAa,EAAE,KAAa,EAAE,EAAE;YAClG,MAAM,SAAS,GAAI,KAAuE,CAAC,KAAK,CAAC,CAAC;YAClG,MAAM,KAAK,GAAG,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAG,KAAK,CAAC,CAAC;YACjC,OAAO,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,KAAK,CAAC;QAC1B,CAAC,CAAC,CAAC;IACP,CAAC;IAEa,UAAU;;YACpB,IAAI,IAAI,CAAC,YAAY;gBAAE,OAAO;YAC9B,IAAI,CAAC,IAAI,CAAC,UAAU;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YACpE,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,aAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBACxD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;gBACjD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,EAAE,CAAC,CAAC;YAClE,CAAC;QACL,CAAC;KAAA;IAEO,OAAO;QACX,IAAI,CAAC,GAAG,GAAG,IAAI,aAAK,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7B,CAAC;IAEO,kBAAkB,CAAC,IAAY;;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QACxC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,MAAA,KAAK,CAAC,CAAC,CAAC,mCAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;IAEa,WAAW,CAAC,QAAgB,EAAE,QAAgB;;YACxD,MAAM,YAAY,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,aAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC7C,kEAAkE;YAClE,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,QAAQ,MAAM,EAAE,UAAU,CAAC,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,QAAQ,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACxD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;KAAA;IAED,sDAAsD;IACxC,oBAAoB;6DAC9B,QAAgB,EAChB,QAAgB,EAChB,KAAa,EACb,IAAY,EACZ,CAAS,EACT,CAAS,EACT,QAAgB,EAChB,oBAA4B,IAAI;YAEhC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACvD,MAAM,UAAU,GAAG,CAAC,QAAQ,GAAG,iBAAiB,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;YAC9D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvB,IAAI,CAAC,GAAG,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxF,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;oBACnB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjC,CAAC;gBACD,0DAA0D;gBAC1D,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtB,CAAC,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;gBAC/G,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;oBAC1B,CAAC,IAAI,UAAU,CAAC;gBACpB,CAAC;YACL,CAAC;YACD,OAAO,CAAC,CAAC;QACb,CAAC;KAAA;IAED,oDAAoD;IACtC,uBAAuB;6DACjC,IAAY,EACZ,CAAS,EACT,CAAS,EACT,UAAkB,EAClB,QAAgB,EAChB,KAAa,EACb,SAAiB,EACjB,oBAA4B,IAAI;YAEhC,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,gEAAgE;YAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,CAAC,QAAQ,GAAG,iBAAiB,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;YAC9D,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBACzB,IAAI,OAAO,GAAG,GAAG,CAAC;gBAClB,IAAI,OAAO,GAAG,KAAK,CAAC;gBACpB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7C,OAAO,GAAG,IAAI,CAAC;oBACf,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC/C,CAAC;gBACD,IAAI,OAAO,EAAE,CAAC;oBACV,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAChD,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACjC,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC/B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;gBAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBAChD,gBAAgB;gBAChB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;gBACpC,QAAQ,IAAI,QAAQ,CAAC;YACzB,CAAC;YACD,OAAO,CAAC,GAAG,UAAU,CAAC;QAC1B,CAAC;KAAA;IAEa,wBAAwB;6DAClC,QAAoF,EACpF,IAAY,EACZ,CAAS,EACT,CAAS,EACT,QAAgB,EAChB,oBAA4B,IAAI;YAEhC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,IAAI,QAAQ,CAAC,WAAW;oBAAE,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;;oBACjF,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YACD,IAAI,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAC9I,CAAC;KAAA;IAEY,cAAc;6DACvB,IAAY,EACZ,QAAoF,EACpF,UAAkB,CAAC,EACnB,eAAuB,CAAC,EACxB,cAAsB,CAAC,EACvB,QAAiB;YAEjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,IAAI,QAAQ,CAAC,WAAW;oBAAE,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;;oBACjF,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACpC,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxD,MAAM,YAAY,GAAG,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC;YAC/F,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;YAC3D,MAAM,UAAU,GAAG,CAAC,QAAQ,CAAC,UAAU,GAAG,IAAI,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;YAC5D,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,MAAM,QAAQ,GAAG,YAAY,GAAG,CAAC,GAAG,OAAO,CAAC;YAC5C,MAAM,SAAS,GAAG,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACX,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;YAC3F,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YACnC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YAClF,MAAM,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC;YAC7B,IAAI,KAAK,GAAG,IAAI,GAAG,OAAO,GAAG,UAAU,CAAC;YACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtB,KAAK,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;gBAC5I,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;oBAClC,KAAK,IAAI,UAAU,CAAC;gBACxB,CAAC;YACL,CAAC;YACD,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1D,CAAC;KAAA;IAGD,2DAA2D;IACnD,WAAW,CAAC,IAAY;QAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IAED,uBAAuB;IACf,eAAe,CAAC,IAAY;QAChC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,mEAAmE;IACnE,sDAAsD;IAC9C,UAAU,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC;IAED,wBAAwB;IAChB,gBAAgB,CAAC,IAAY;QACjC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,oDAAoD;IACtC,UAAU;;;YACpB,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC;gBACzB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACzC,MAAM,MAAM,GAAG,CAAA,MAAA,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,0CAAE,WAAW,EAAE,KAAI,KAAK,CAAC;oBACrE,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC;oBACzB,IAAI,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;wBACxG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;wBACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;oBACzC,CAAC;oBACD,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;oBACxG,iEAAiE;oBACjE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;gBAC3E,CAAC;YACL,CAAC;QACL,CAAC;KAAA;IAED,qFAAqF;IACrF,kDAAkD;IACrC,gBAAgB,CAAC,MAAsB;;YAChD,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACxB,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;YACjD,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC;YACrD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxD,MAAM,QAAQ,GAAG,SAAS,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC/E,IAAI,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;YACzE,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3D,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACpG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;YACpC,IAAI,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;YAC7E,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/D,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACrG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;YACpC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC1C,IAAI,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;gBAClE,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrE,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACzG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;gBACpC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;oBACjC,MAAM,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;oBACxD,IAAI,WAAW,IAAI,eAAe,CAAC,WAAW,CAAC,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC;wBACtI,MAAM,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC,MAAO,CAAC;wBACzD,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;wBACzD,IAAA,yBAAS,EAAC,IAAI,CAAC,GAAG,8CACd,MAAM,EAAE,IAAI,CAAC,IAAI,EACjB,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EACxB,IAAI,EAAE,WAAW,CAAC,IAAI,IACnB,mBAAmB,GACnB,WAAW,CAAC,OAAO,GACnB,WAAW,CAAC,MAAM,EACvB,CAAC;wBACH,IAAI,CAAC,IAAI,GAAK,IAAI,CAAC,GAAyD,CAAC,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;oBAC/H,CAAC;yBAAM,CAAC;wBACJ,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;4BACb,IAAI,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;4BAC9D,SAAS,GAAG,IAAI,CAAC,sBAAsB,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;4BACjE,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;gCAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;gCAChD,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;4BACpE,CAAC;iCAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gCACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;gCAElD,MAAM,IAAI,CAAC,wBAAwB,iCAAM,IAAI,CAAC,MAAM,CAAC,eAAe,KAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,KACxG,QAAQ,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;4BAC9F,CAAC;iCAAM,CAAC;gCACJ,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;4BAC5J,CAAC;4BACD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;wBACxC,CAAC;oBACL,CAAC;oBACD,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBAClE,IAAI,kBAAkB,IAAI,eAAe,CAAC,kBAAkB,CAAC,IAAI,eAAe,CAAC,kBAAkB,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,eAAe,CAAC,kBAAkB,CAAC,CAAC,MAAM,EAAE,CAAC;wBAClK,MAAM,WAAW,GAAG,eAAe,CAAC,kBAAkB,CAAC,CAAC,MAAO,CAAC;wBAChE,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;wBACzD,IAAA,yBAAS,EAAC,IAAI,CAAC,GAAG,8CACd,MAAM,EAAE,IAAI,CAAC,IAAI,EACjB,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EACxB,IAAI,EAAE,WAAW,CAAC,IAAI,IACnB,mBAAmB,GACnB,WAAW,CAAC,OAAO,GACnB,WAAW,CAAC,MAAM,EACvB,CAAC;wBACH,IAAI,CAAC,IAAI,GAAK,IAAI,CAAC,GAAyD,CAAC,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;oBAC/H,CAAC;yBAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;wBACvB,IAAI,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;wBACjE,SAAS,GAAG,IAAI,CAAC,sBAAsB,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;wBACjE,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;4BAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;4BAChD,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;wBAC9D,CAAC;6BAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;4BACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;4BAClD,MAAM,IAAI,CAAC,wBAAwB,iCAAM,IAAI,CAAC,MAAM,CAAC,SAAS,KAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,KAC5F,QAAQ,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBAC9F,CAAC;6BAAM,CAAC;4BACJ,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBACtJ,CAAC;wBACD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;oBACxC,CAAC;gBACL,CAAC;gBACD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;YACxC,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,GAAG,CAAC;YAC1C,iFAAiF;YACjF,MAAM,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa;iBACvD,OAAO,CAAC,4BAA4B,EAAE,GAAG,CAAC;iBAC1C,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;iBACnB,IAAI,EAAE,IAAI,SAAS,CAAC;YACzB,MAAM,QAAQ,GAAG,aAAa,iBAAiB,MAAM,CAAC;YACtD,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;gBACpB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;gBACjD,MAAM,aAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;gBACrD,OAAO,QAAQ,CAAC;YACpB,CAAC;iBAAM,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;gBAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACJ,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAC1C,CAAC;QACL,CAAC;KAAA;CACJ;AAnVD,8CAmVC","sourcesContent":["import { jsPDF } from \"jspdf\";\nimport autotable, { Styles, UserOptions } from \"jspdf-autotable\";\nimport { promises as fs } from 'fs';\nimport path from 'path';\n\nexport interface IPartiContratto {\n fornitore: { denominazione: string; codiceFiscale: string; indirizzoCompleto: string; };\n cliente: { denominazione: string; codiceFiscale: string; indirizzoCompleto: string; };\n}\n\nexport interface DocumentParams {\n parti: IPartiContratto;\n tipOutput?: 'f' | 'd' | 'u';\n dynamicFields?: { [key: string]: string };\n dynamicElements?: { [placeholder: string]: DynamicElement };\n}\n\nexport type DynamicElement = {\n type: 'table';\n config: {\n head: string[];\n body: string[][];\n options?: UserOptions;\n styles?: Partial<Styles>;\n };\n};\n\ninterface DocumentConfig {\n fontTitolo: { nome: string; dimensione: number; colore: string; installPath?: string; };\n fontSottotitolo: { nome: string; dimensione: number; colore: string; installPath?: string; };\n fontTesto: { nome: string; dimensione: number; colore: string; installPath?: string; };\n fontBold: { nome: string; dimensione: number; colore: string; installPath?: string; };\n margini: { sx: number; dx: number; alto: number; basso: number; };\n staccoriga: number;\n rientro: number;\n testi: {\n titolo: string;\n premessa: string;\n Punti: Array<{ titolo: string; Sottopunti: Array<{ titolo: string; contenuto: string; }>; }>;\n versione: string;\n };\n immagini: Array<{ path: string; posizione: [number, number]; dimensioni: [number, number]; coeffDim?: number; }>;\n box: { backgrund: string; raggio: number; };\n tableStyle?: { lineColor?: string; lineWidth?: number; font?: string; fontSize?: number; cellPadding?: number; fillColor?: string; };\n}\n\nasync function loadImageAsBase64(imagePath: string): Promise<string> {\n const absolutePath = path.resolve(imagePath);\n const buffer = await fs.readFile(absolutePath);\n const ext = path.extname(imagePath).slice(1).toLowerCase();\n const base64 = buffer.toString('base64');\n return `data:image/${ext};base64,${base64}`;\n}\n\nexport class DocumentGenerator {\n private config!: DocumentConfig;\n private doc!: jsPDF;\n private curX: number = 0;\n private curY: number = 0;\n private configLoaded: boolean = false;\n\n constructor(private configPath?: string) { }\n\n public setConfig(config: DocumentConfig) {\n this.config = config;\n this.configLoaded = true;\n }\n\n private applyTemplate(template: string, dynamicFields?: { [key: string]: string }): string {\n if (!dynamicFields) return template;\n return template.replace(/\\$(\\w+)\\$/g, (match, key) =>\n dynamicFields[key] !== undefined ? dynamicFields[key] : match\n );\n }\n\n private applyPartiPlaceholders(text: string, parti: IPartiContratto): string {\n return text.replace(/\\$(fornitore|cliente):(\\w+)\\$/g, (match: string, party: string, field: string) => {\n const partyData = (parti as unknown as Record<string, Record<string, string> | undefined>)[party];\n const value = partyData?.[field];\n return value ?? match;\n });\n }\n\n private async loadConfig(): Promise<void> {\n if (this.configLoaded) return;\n if (!this.configPath) throw new Error(\"No configuration provided.\");\n try {\n const data = await fs.readFile(this.configPath, 'utf8');\n this.config = JSON.parse(data) as DocumentConfig;\n this.configLoaded = true;\n } catch (error) {\n throw new Error(`Error reading configuration file: ${error}`);\n }\n }\n\n private initDoc(): void {\n this.doc = new jsPDF();\n const margins = this.config.margini;\n this.curX = margins.sx;\n this.curY = margins.alto;\n }\n\n private extractPlaceholder(text: string): string | null {\n const match = text.match(/^\\$(\\w+)\\$$/);\n return match ? (match[1] ?? null) : null;\n }\n\n private async installFont(fontPath: string, fontName: string): Promise<void> {\n const absolutePath = path.resolve(fontPath);\n const buffer = await fs.readFile(absolutePath);\n const base64Font = buffer.toString('base64');\n //console.log(`Installing font ${fontName} from ${absolutePath}`);\n this.doc.addFileToVFS(`${fontName}.ttf`, base64Font);\n this.doc.addFont(`${fontName}.ttf`, fontName, 'normal');\n this.doc.setFont(fontName);\n }\n\n // Writes wrapped text, handling inline bold segments.\n private async writeWrappedTextCore(\n fontName: string,\n fontSize: number,\n color: string,\n text: string,\n x: number,\n y: number,\n maxWidth: number,\n lineSpacingFactor: number = 1.15\n ): Promise<number> {\n this.doc.setFont(fontName);\n this.doc.setFontSize(fontSize);\n this.doc.setTextColor(color);\n const lines = this.doc.splitTextToSize(text, maxWidth);\n const lineHeight = (fontSize * lineSpacingFactor / 72) * 25.4;\n for (const line of lines) {\n if (y + lineHeight > (this.doc.internal.pageSize.getHeight() - this.config.margini.basso)) {\n this.doc.addPage();\n y = this.config.margini.alto;\n }\n // If the line contains inline bold markers, process them.\n if (line.includes(\"**\")) {\n y = await this.writeLineWithInlineBold(line, x, y, fontName, fontSize, color, maxWidth, lineSpacingFactor);\n } else {\n this.doc.text(line, x, y);\n y += lineHeight;\n }\n }\n return y;\n }\n\n // Writes a single line with inline bold processing.\n private async writeLineWithInlineBold(\n line: string,\n x: number,\n y: number,\n normalFont: string,\n fontSize: number,\n color: string,\n _maxWidth: number,\n lineSpacingFactor: number = 1.15\n ): Promise<number> {\n let currentX = x;\n // Split the line by bold segments. The regex keeps the markers.\n const segments = line.split(/(\\*\\*.*?\\*\\*)/);\n const lineHeight = (fontSize * lineSpacingFactor / 72) * 25.4;\n for (const seg of segments) {\n let segText = seg;\n let useBold = false;\n if (seg.startsWith(\"**\") && seg.endsWith(\"**\")) {\n useBold = true;\n segText = seg.substring(2, seg.length - 2);\n }\n if (useBold) {\n this.doc.setFont(this.config.fontBold.nome);\n } else {\n this.doc.setFont(normalFont);\n }\n this.doc.setFontSize(fontSize);\n this.doc.setTextColor(color);\n const segWidth = this.doc.getTextWidth(segText);\n // Write segment\n this.doc.text(segText, currentX, y);\n currentX += segWidth;\n }\n return y + lineHeight;\n }\n\n private async writeWrappedTextWithFont(\n fontConf: { nome: string; dimensione: number; colore: string; installPath?: string },\n text: string,\n x: number,\n y: number,\n maxWidth: number,\n lineSpacingFactor: number = 1.15\n ): Promise<void> {\n const fontList = this.doc.getFontList();\n if (!fontList[fontConf.nome]) {\n if (fontConf.installPath) await this.installFont(fontConf.installPath, fontConf.nome);\n else throw new Error(\"Font not present and no installPath provided\");\n } else {\n this.doc.setFont(fontConf.nome);\n }\n this.curY = await this.writeWrappedTextCore(fontConf.nome, fontConf.dimensione, fontConf.colore, text, x, y, maxWidth, lineSpacingFactor);\n }\n\n public async writeBoxedText(\n text: string,\n fontConf: { nome: string; dimensione: number; colore: string; installPath?: string },\n padding: number = 5,\n borderRadius: number = 5,\n borderWidth: number = 1,\n boxColor?: string\n ): Promise<void> {\n const fontList = this.doc.getFontList();\n if (!fontList[fontConf.nome]) {\n if (fontConf.installPath) await this.installFont(fontConf.installPath, fontConf.nome);\n else throw new Error(\"Font not present and no installPath provided\");\n } else {\n this.doc.setFont(fontConf.nome);\n }\n this.doc.setFontSize(fontConf.dimensione);\n this.doc.setTextColor(fontConf.colore);\n const pageWidth = this.doc.internal.pageSize.getWidth();\n const maxTextWidth = pageWidth - this.config.margini.sx - this.config.margini.dx - 2 * padding;\n const lines = this.doc.splitTextToSize(text, maxTextWidth);\n const lineHeight = (fontConf.dimensione * 1.15 / 72) * 25.4;\n const textHeight = lines.length * lineHeight;\n const boxX = this.curX;\n const boxY = this.curY;\n const boxWidth = maxTextWidth + 2 * padding;\n const boxHeight = textHeight + 2 * padding;\n if (boxColor) {\n this.doc.setFillColor(boxColor);\n this.doc.roundedRect(boxX, boxY, boxWidth, boxHeight, borderRadius, borderRadius, \"F\");\n }\n this.doc.setLineWidth(borderWidth);\n this.doc.roundedRect(boxX, boxY, boxWidth, boxHeight, borderRadius, borderRadius);\n const textX = boxX + padding;\n let textY = boxY + padding + lineHeight;\n for (const line of lines) {\n if (line.includes(\"**\")) {\n textY = await this.writeLineWithInlineBold(line, textX, textY, fontConf.nome, fontConf.dimensione, fontConf.colore, maxTextWidth, 1.15);\n } else {\n this.doc.text(line, textX, textY);\n textY += lineHeight;\n }\n }\n this.curY = boxY + boxHeight + this.config.staccoriga;\n }\n\n\n // Checks if text is wrapped in '^' markers for boxed text.\n private isBoxedText(text: string): boolean {\n return text.startsWith('^') && text.endsWith('^');\n }\n\n // Removes '^' markers.\n private stripBoxMarkers(text: string): string {\n return text.substring(1, text.length - 1);\n }\n\n // Checks if text is entirely wrapped in '**' markers (not inline).\n // (This function is kept for backward compatibility.)\n private isBoldText(text: string): boolean {\n return text.startsWith('**') && text.endsWith('**');\n }\n\n // Removes '**' markers.\n private stripBoldMarkers(text: string): string {\n return text.substring(2, text.length - 2);\n }\n\n // Inserts images using the current cursor position.\n private async insertLogo(): Promise<void> {\n if (this.config.immagini && this.config.immagini.length > 0) {\n const startX = this.curX;\n for (const imgConf of this.config.immagini) {\n const format = imgConf.path.split('.').pop()?.toUpperCase() || 'PNG';\n const base64Image = await loadImageAsBase64(imgConf.path);\n const startY = this.curY;\n if (startY + imgConf.dimensioni[1] > (this.doc.internal.pageSize.getHeight() - this.config.margini.basso)) {\n this.doc.addPage();\n this.curY = this.config.margini.alto;\n }\n this.doc.addImage(base64Image, format, startX, this.curY, imgConf.dimensioni[0], imgConf.dimensioni[1]);\n //console.log(`Image inserted at X: ${startX}, Y: ${this.curY}`);\n this.curY = this.curY + imgConf.dimensioni[1] + this.config.staccoriga;\n }\n }\n }\n\n // Generates the PDF document by processing images, dynamic fields, tables, and text.\n // Supports inline bold formatting and boxed text.\n public async generateDocument(params: DocumentParams): Promise<string | ArrayBuffer> {\n await this.loadConfig();\n this.initDoc();\n await this.insertLogo();\n const dynamicFields = params.dynamicFields || {};\n const dynamicElements = params.dynamicElements || {};\n const pageWidth = this.doc.internal.pageSize.getWidth();\n const maxWidth = pageWidth - (this.config.margini.sx + this.config.margini.dx);\n let titolo = this.applyTemplate(this.config.testi.titolo, dynamicFields);\n titolo = this.applyPartiPlaceholders(titolo, params.parti);\n await this.writeWrappedTextWithFont(this.config.fontTitolo, titolo, this.curX, this.curY, maxWidth);\n this.curY += this.config.staccoriga;\n let premessa = this.applyTemplate(this.config.testi.premessa, dynamicFields);\n premessa = this.applyPartiPlaceholders(premessa, params.parti);\n await this.writeWrappedTextWithFont(this.config.fontTesto, premessa, this.curX, this.curY, maxWidth);\n this.curY += this.config.staccoriga;\n for (const punto of this.config.testi.Punti) {\n let puntoTitolo = this.applyTemplate(punto.titolo, dynamicFields);\n puntoTitolo = this.applyPartiPlaceholders(puntoTitolo, params.parti);\n await this.writeWrappedTextWithFont(this.config.fontTitolo, puntoTitolo, this.curX, this.curY, maxWidth);\n this.curY += this.config.staccoriga;\n for (const sub of punto.Sottopunti) {\n const placeholder = this.extractPlaceholder(sub.titolo);\n if (placeholder && dynamicElements[placeholder] && dynamicElements[placeholder].type === 'table' && dynamicElements[placeholder].config) {\n const tableConfig = dynamicElements[placeholder].config!;\n const defaultTableOptions = this.config.tableStyle || {};\n autotable(this.doc, {\n startY: this.curY,\n head: [tableConfig.head],\n body: tableConfig.body,\n ...defaultTableOptions,\n ...tableConfig.options,\n ...tableConfig.styles\n });\n this.curY = ((this.doc as unknown) as { lastAutoTable: { finalY: number } }).lastAutoTable.finalY + this.config.staccoriga;\n } else {\n if (sub.titolo) {\n let titleText = this.applyTemplate(sub.titolo, dynamicFields);\n titleText = this.applyPartiPlaceholders(titleText, params.parti);\n if (this.isBoxedText(titleText)) {\n const boxText = this.stripBoxMarkers(titleText);\n await this.writeBoxedText(boxText, this.config.fontSottotitolo);\n } else if (this.isBoldText(titleText)) {\n const boldText = this.stripBoldMarkers(titleText);\n\n await this.writeWrappedTextWithFont({ ...this.config.fontSottotitolo, nome: this.config.fontSottotitolo.nome },\n boldText, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\n } else {\n await this.writeWrappedTextWithFont(this.config.fontSottotitolo, titleText, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\n }\n this.curY += this.config.staccoriga;\n }\n }\n const contentPlaceholder = this.extractPlaceholder(sub.contenuto);\n if (contentPlaceholder && dynamicElements[contentPlaceholder] && dynamicElements[contentPlaceholder].type === 'table' && dynamicElements[contentPlaceholder].config) {\n const tableConfig = dynamicElements[contentPlaceholder].config!;\n const defaultTableOptions = this.config.tableStyle || {};\n autotable(this.doc, {\n startY: this.curY,\n head: [tableConfig.head],\n body: tableConfig.body,\n ...defaultTableOptions,\n ...tableConfig.options,\n ...tableConfig.styles\n });\n this.curY = ((this.doc as unknown) as { lastAutoTable: { finalY: number } }).lastAutoTable.finalY + this.config.staccoriga;\n } else if (sub.contenuto) {\n let contenuto = this.applyTemplate(sub.contenuto, dynamicFields);\n contenuto = this.applyPartiPlaceholders(contenuto, params.parti);\n if (this.isBoxedText(contenuto)) {\n const boxText = this.stripBoxMarkers(contenuto);\n await this.writeBoxedText(boxText, this.config.fontTesto);\n } else if (this.isBoldText(contenuto)) {\n const boldText = this.stripBoldMarkers(contenuto);\n await this.writeWrappedTextWithFont({ ...this.config.fontTesto, nome: this.config.fontTesto.nome },\n boldText, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\n } else {\n await this.writeWrappedTextWithFont(this.config.fontTesto, contenuto, this.curX + this.config.rientro, this.curY, maxWidth - this.config.rientro);\n }\n this.curY += this.config.staccoriga;\n }\n }\n this.curY += this.config.staccoriga;\n }\n const tipOutput = params.tipOutput || 'd';\n // Sanitizza il nome: evita path traversal e caratteri non validi nel filesystem.\n const safeDenominazione = params.parti.cliente.denominazione\n .replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]/g, '_')\n .replace(/\\.+$/, '')\n .trim() || 'cliente';\n const fileName = `Documento_${safeDenominazione}.pdf`;\n if (tipOutput === 'd') {\n const pdfBuffer = this.doc.output('arraybuffer');\n await fs.writeFile(fileName, Buffer.from(pdfBuffer));\n return fileName;\n } else if (tipOutput === 'u') {\n return this.doc.output('arraybuffer');\n } else {\n return this.doc.output('arraybuffer');\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DailyFileTransport.js","sourceRoot":"","sources":["../src/DailyFileTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import Transport from 'winston-transport';\r\nimport { appendFile, appendFileSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\ntype LogInfo = {\r\n time?: Date;\r\n [key: string]: unknown;\r\n [key: symbol]: unknown;\r\n};\r\n\r\n/** One append-only JSON-lines file per local calendar day, including idle days. */\r\nexport class DailyFileTransport extends Transport {\r\n private timer?: NodeJS.Timeout;\r\n\r\n constructor(private readonly directory: string) {\r\n super();\r\n mkdirSync(directory, { recursive: true });\r\n this.touchToday();\r\n this.scheduleMidnight();\r\n }\r\n\r\n private filename(date: Date): string {\r\n const month = String(date.getMonth() + 1).padStart(2, '0');\r\n const day = String(date.getDate()).padStart(2, '0');\r\n return join(this.directory, `${date.getFullYear()}-${month}-${day}.json`);\r\n }\r\n\r\n private touchToday(): void {\r\n appendFileSync(this.filename(new Date()), '', { flag: 'a' });\r\n }\r\n\r\n private scheduleMidnight(): void {\r\n const now = new Date();\r\n const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);\r\n this.timer = setTimeout(() => {\r\n try {\r\n this.touchToday();\r\n } catch (error) {\r\n // `emit('error')` senza listener farebbe crashare il processo: emetti solo se osservato.\r\n if (this.listenerCount('error') > 0) {\r\n this.emit('error', error);\r\n }\r\n } finally {\r\n this.scheduleMidnight();\r\n }\r\n }, Math.max(1, midnight.getTime() - now.getTime()));\r\n this.timer.unref();\r\n }\r\n\r\n override log(info: LogInfo, callback: (error?: Error | null) => void): void {\r\n // Capture the event's day even when the transport is draining a backlog.\r\n const date = info.time instanceof Date ? info.time : new Date();\r\n appendFile(this.filename(date), `${info[Symbol.for('message')]}\\n`, { flag: 'a' }, (error) => {\r\n if (!error) this.emit('logged', info);\r\n callback(error);\r\n });\r\n }\r\n\r\n override close(): void {\r\n if (this.timer) clearTimeout(this.timer);\r\n }\r\n\r\n override logv(chunks: { chunk: LogInfo }[], callback: (error?: Error | null) => void): void {\r\n let index = 0;\r\n const next = (error?: Error | null) => {\r\n if (error || index === chunks.length) return callback(error);\r\n const chunk = chunks[index];\r\n index += 1;\r\n if (!chunk) return callback(error);\r\n this.log(chunk.chunk, next);\r\n };\r\n next();\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"DailyFileTransport.js","sourceRoot":"","sources":["../src/DailyFileTransport.ts"],"names":[],"mappings":"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","sourcesContent":["import Transport from 'winston-transport';\nimport { appendFile, appendFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\n\ntype LogInfo = {\n time?: Date;\n [key: string]: unknown;\n [key: symbol]: unknown;\n};\n\n/** One append-only JSON-lines file per local calendar day, including idle days. */\nexport class DailyFileTransport extends Transport {\n private timer?: NodeJS.Timeout;\n\n constructor(private readonly directory: string) {\n super();\n mkdirSync(directory, { recursive: true });\n this.touchToday();\n this.scheduleMidnight();\n }\n\n private filename(date: Date): string {\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const day = String(date.getDate()).padStart(2, '0');\n return join(this.directory, `${date.getFullYear()}-${month}-${day}.json`);\n }\n\n private touchToday(): void {\n appendFileSync(this.filename(new Date()), '', { flag: 'a' });\n }\n\n private scheduleMidnight(): void {\n const now = new Date();\n const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);\n this.timer = setTimeout(() => {\n try {\n this.touchToday();\n } catch (error) {\n // `emit('error')` senza listener farebbe crashare il processo: emetti solo se osservato.\n if (this.listenerCount('error') > 0) {\n this.emit('error', error);\n }\n } finally {\n this.scheduleMidnight();\n }\n }, Math.max(1, midnight.getTime() - now.getTime()));\n this.timer.unref();\n }\n\n override log(info: LogInfo, callback: (error?: Error | null) => void): void {\n // Capture the event's day even when the transport is draining a backlog.\n const date = info.time instanceof Date ? info.time : new Date();\n appendFile(this.filename(date), `${info[Symbol.for('message')]}\\n`, { flag: 'a' }, (error) => {\n if (!error) this.emit('logged', info);\n callback(error);\n });\n }\n\n override close(): void {\n if (this.timer) clearTimeout(this.timer);\n }\n\n override logv(chunks: { chunk: LogInfo }[], callback: (error?: Error | null) => void): void {\n let index = 0;\n const next = (error?: Error | null) => {\n if (error || index === chunks.length) return callback(error);\n const chunk = chunks[index];\n index += 1;\n if (!chunk) return callback(error);\n this.log(chunk.chunk, next);\n };\n next();\n }\n}\n"]}