@medicus.ai/medicus-report-pdf-generator 1.3.13 → 1.3.15

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 (178) hide show
  1. package/.vscode/settings.json +3 -3
  2. package/HANDOVER.md +146 -0
  3. package/README.md +26 -26
  4. package/app/i18n.config.js +20 -20
  5. package/app/i18n_wellbeing.config.js +20 -20
  6. package/app/services/localeService.js +28 -28
  7. package/assets/Mediclinic/css/nasco_report.css +6297 -6297
  8. package/assets/Mediclinic/css/nasco_report_rtl.css +851 -851
  9. package/assets/Mediclinic/css/style.css +33 -33
  10. package/assets/Mediclinic/js/js.js +112 -112
  11. package/assets/Mediclinic/js/raphael.js +8437 -8437
  12. package/assets/arabic.css +275 -275
  13. package/assets/charts.min.js +7 -7
  14. package/assets/corporate_report/css/report.css +582 -582
  15. package/assets/corporate_report/css/report_rtl.css +144 -144
  16. package/assets/corporate_report/js/js.js +14 -14
  17. package/assets/data/data.json +124 -124
  18. package/assets/data/data2.json +675 -675
  19. package/assets/data/pdf_data_with_history.json +1255 -1255
  20. package/assets/doctor-note-insight-icon.svg +11 -11
  21. package/assets/imc/css/style.css +61 -61
  22. package/assets/jquery-1.4.min.js +151 -151
  23. package/assets/jquery-2.1.0.min.js +3 -3
  24. package/assets/js.js +201 -201
  25. package/assets/medicus_pdf/arabic.css +444 -444
  26. package/assets/medicus_pdf/styles.css +2427 -2427
  27. package/assets/print.css +4 -4
  28. package/assets/qr-code-style.css +68 -68
  29. package/assets/qrcode.min.js +3 -3
  30. package/assets/raphael-min.js +10 -10
  31. package/assets/sanusx/css/sanusx_report.css +707 -707
  32. package/assets/sizing.js +2 -2
  33. package/assets/styles.css +1545 -1545
  34. package/assets/styles.min.css +1358 -1358
  35. package/assets/translation.js +30 -30
  36. package/assets/wellbeing/css/nasco_report.css +3472 -3472
  37. package/assets/wellbeing/css/nasco_report_rtl.css +679 -679
  38. package/assets/wellbeing/js/js.js +111 -111
  39. package/assets/wellbeing/js/raphael.js +8437 -8437
  40. package/base64/cairo-font.js +2 -2
  41. package/config/Mediclinic.json +37 -37
  42. package/config/Najeeb.ai.json +35 -35
  43. package/config/Pha.json +64 -64
  44. package/config/bionext.json +37 -37
  45. package/config/default.json +35 -35
  46. package/config/diagnostikare.json +34 -34
  47. package/config/maisonsante.json +39 -39
  48. package/config/nasco.json +35 -35
  49. package/config/sanitas.json +35 -35
  50. package/config/sanusx.json +34 -34
  51. package/config/test.txt +4 -4
  52. package/data converter.js +53 -53
  53. package/docs/01-architecture-overview.md +149 -0
  54. package/docs/02-configuration-and-whitelabelling.md +111 -0
  55. package/docs/03-templating-and-rendering.md +71 -0
  56. package/docs/04-report-pipelines.md +128 -0
  57. package/docs/05-questions-and-data-model.md +67 -0
  58. package/docs/06-internationalization.md +59 -0
  59. package/docs/07-email-notifications.md +59 -0
  60. package/docs/08-known-issues-and-technical-debt.md +41 -0
  61. package/docs/README.md +22 -0
  62. package/downloadfile.js +6 -6
  63. package/example.txt +1188 -1188
  64. package/index.js +481 -480
  65. package/lib/big_integral_questionnaire.js +370 -298
  66. package/lib/corporate_report_generator.js +399 -399
  67. package/lib/pdf_generator.min.js +525 -525
  68. package/lib/sanusx_report_generator.js +533 -533
  69. package/lib/sendEmail.js +343 -343
  70. package/lib/template.js +2227 -2227
  71. package/lib/wellbeing_report_generator.js +2164 -2134
  72. package/locales/ar-AE.json +169 -169
  73. package/locales/ar-SA.json +169 -169
  74. package/locales/ar.json +397 -397
  75. package/locales/de.json +169 -169
  76. package/locales/en.json +229 -229
  77. package/locales/fr.json +168 -168
  78. package/locales/pt.json +165 -165
  79. package/locales/wellbeing/ar-AE.json +149 -149
  80. package/locales/wellbeing/ar-SA.json +257 -257
  81. package/locales/wellbeing/de.json +301 -301
  82. package/locales/wellbeing/en.json +423 -423
  83. package/locales/wellbeing/es.json +133 -133
  84. package/locales/wellbeing/fr.json +121 -121
  85. package/locales/wellbeing/it-IT.json +121 -121
  86. package/locales/wellbeing/pt.json +121 -121
  87. package/locales/wellbeing/tr.json +121 -121
  88. package/locales/wellbeing/zh-CN.json +121 -121
  89. package/locales/zh-CN.json +165 -165
  90. package/package.json +47 -47
  91. package/preview-big-integral.js +75 -75
  92. package/run.js +15 -15
  93. package/templates/Mediclinic/blocks/first-section.html +205 -205
  94. package/templates/Mediclinic/blocks/footer.html +7 -7
  95. package/templates/Mediclinic/blocks/header-template.html +5 -5
  96. package/templates/Mediclinic/blocks/header.html +20 -20
  97. package/templates/Mediclinic/blocks/patient-table.html +38 -38
  98. package/templates/Mediclinic/blocks/pdf-header.html +9 -9
  99. package/templates/Mediclinic/blocks/tips.html +8 -8
  100. package/templates/Mediclinic/ltr_no_pages.html +37 -37
  101. package/templates/Mediclinic/rtl_no_pages.html +42 -42
  102. package/templates/Mediclinic/wellbeing_template.html +41 -41
  103. package/templates/Pha/blocks/first-section.html +207 -207
  104. package/templates/Pha/blocks/footer.html +7 -7
  105. package/templates/Pha/blocks/header-template.html +5 -5
  106. package/templates/Pha/blocks/header.html +20 -20
  107. package/templates/Pha/blocks/patient-table.html +38 -38
  108. package/templates/Pha/blocks/pdf-header.html +8 -8
  109. package/templates/Pha/blocks/tips.html +8 -8
  110. package/templates/Pha/ltr_no_pages.html +37 -37
  111. package/templates/Pha/rtl_no_pages.html +42 -42
  112. package/templates/Pha/wellbeing_template.html +41 -41
  113. package/templates/base.html +23 -23
  114. package/templates/blocks/ar-first-page-header.html +68 -68
  115. package/templates/blocks/ar-footer.html +54 -54
  116. package/templates/blocks/ar-header.html +67 -67
  117. package/templates/blocks/biomarker-compact-ar.html +27 -27
  118. package/templates/blocks/biomarker-compact.html +27 -27
  119. package/templates/blocks/biomarker-details.html +41 -41
  120. package/templates/blocks/biomarker-insight.html +22 -22
  121. package/templates/blocks/biomarker-min.html +9 -9
  122. package/templates/blocks/doctor-note.html +11 -11
  123. package/templates/blocks/footer.html +35 -35
  124. package/templates/blocks/header-first-page.html +46 -46
  125. package/templates/blocks/header.html +49 -49
  126. package/templates/blocks/panel-compact.html +38 -38
  127. package/templates/blocks/panel-details.html +16 -16
  128. package/templates/blocks/report-summary.html +16 -16
  129. package/templates/blocks/section-title.html +4 -4
  130. package/templates/blocks/signature.html +16 -16
  131. package/templates/blocks/summary-insight.html +15 -15
  132. package/templates/blocks/summary.html +9 -9
  133. package/templates/corporate_report/cover_page.html +23 -23
  134. package/templates/corporate_report/footer.html +9 -9
  135. package/templates/corporate_report/ltr_no_pages.html +20 -20
  136. package/templates/corporate_report/page.html +24 -24
  137. package/templates/corporate_report/participant_analytics.html +5 -5
  138. package/templates/corporate_report/rtl_no_pages.html +20 -20
  139. package/templates/empty.html +10 -10
  140. package/templates/first_page_head.html +157 -157
  141. package/templates/imc/first-header-template.html +53 -53
  142. package/templates/ltr.html +1368 -1368
  143. package/templates/ltr_no_pages.html +89 -89
  144. package/templates/maisonsante/blocks/first-section.html +151 -207
  145. package/templates/maisonsante/blocks/footer.html +7 -7
  146. package/templates/maisonsante/blocks/header-template.html +5 -5
  147. package/templates/maisonsante/blocks/header.html +20 -20
  148. package/templates/maisonsante/blocks/patient-table.html +38 -38
  149. package/templates/maisonsante/blocks/pdf-header.html +8 -8
  150. package/templates/maisonsante/blocks/tips.html +8 -8
  151. package/templates/maisonsante/ltr_no_pages.html +38 -38
  152. package/templates/maisonsante/rtl_no_pages.html +43 -43
  153. package/templates/maisonsante/wellbeing_template.html +41 -41
  154. package/templates/no_pages.html +1224 -1224
  155. package/templates/popup/popup-template.html +129 -129
  156. package/templates/rtl_no_pages.html +92 -92
  157. package/templates/sanusx/blocks/footer.html +6 -6
  158. package/templates/sanusx/blocks/header.html +20 -20
  159. package/templates/sanusx/blocks/personal-details.html +123 -123
  160. package/templates/sanusx/blocks/predictions-section.html +57 -57
  161. package/templates/sanusx/blocks/recommendations-section.html +6 -6
  162. package/templates/sanusx/blocks/super-power-section.html +29 -29
  163. package/templates/sanusx/blocks/tips.html +8 -8
  164. package/templates/sanusx/ltr_no_pages.html +26 -26
  165. package/templates/sanusx/rtl_no_pages.html +36 -36
  166. package/templates/template.html +1377 -1377
  167. package/templates/wellbeing/blocks/first-section.html +106 -106
  168. package/templates/wellbeing/blocks/footer.html +7 -7
  169. package/templates/wellbeing/blocks/header.html +37 -37
  170. package/templates/wellbeing/blocks/tips.html +8 -8
  171. package/templates/wellbeing/ltr_no_pages.html +35 -35
  172. package/templates/wellbeing/rtl_no_pages.html +36 -36
  173. package/templates/wellbeing/wellbeing_template.html +43 -43
  174. package/test.js +74 -74
  175. package/testing-reports/mediclinic/arabic-data.js +0 -439
  176. package/testing-reports/mediclinic/doctor-data.js +0 -523
  177. package/testing-reports/mediclinic/with-wellbeing.js +0 -415
  178. package/testing-reports/pha/notes-example.js +0 -397
@@ -0,0 +1,59 @@
1
+ # 6. Internationalization (i18n) & RTL
2
+
3
+ ## 6.1 Library and the two independent configurations
4
+
5
+ The project uses the **`i18n` npm package** (`"i18n": "^0.8.3"` in `package.json`, resolving to `0.8.6` installed) — not `i18n-nodejs`, not a custom framework. There are **two independently configured instances** of this same package, each a process-wide singleton:
6
+
7
+ - **`app/i18n.config.js`** — `locales`: `en`, `de`, `fr`, `ar-AE`, `ar-SA`, `pt`, `zh-CN`. Directory: `locales/`. Used by: `lib/pdf_generator.js` / `.min.js` (core report) only.
8
+ - **`app/i18n_wellbeing.config.js`** — `locales`: `en`, `de`, `fr`, `ar-AE`, `ar-SA`, `pt`, `zh-CN`, `tr`, `es`. Directory: `locales/wellbeing/`. Used by: `wellbeing_report_generator.js`, `template.js`, `corporate_report_generator.js`, `sanusx_report_generator.js`.
9
+
10
+ **Every generator except the core lab report pulls its strings from `locales/wellbeing/`, not `locales/`** — including the corporate report and SanusX report, despite neither being "wellbeing" products. This is a non-obvious naming trap when you're hunting for a string to translate: check which config the generator you're editing actually requires before assuming which locale folder to edit.
11
+
12
+ Both configs also set `cookie: 'currentLang'`, `queryParameter: 'lang'`, and alias the translate API to `translate`/`translateN` instead of the package default `__`/`__n`. Neither sets `objectNotation`, `fallbacks`, or `updateFiles`, so package defaults apply (see 6.5).
13
+
14
+ Because Node caches `require()`d modules, every file that requires either config gets the **same shared mutable object** — locale state is process-global, not per-request. See 6.6.
15
+
16
+ ## 6.2 `app/services/localeService.js`
17
+
18
+ A thin wrapper class, instantiated separately by each generator with its own `i18n` singleton:
19
+
20
+ - `t(key, args)` → `i18nProvider.__(key, args).message` — the actual string getter. In practice, `args` is never passed at any call site in this codebase — the package's built-in `%s`/vsprintf-style interpolation is wired up but unused.
21
+ - `setLocale(locale)` → **only** calls through to the underlying `i18n.setLocale(locale)` if `locale` is already in that instance's configured `locales` array; otherwise it silently no-ops (see 6.5 for why this matters).
22
+ - `getLocales()`, `getCurrentLocale()`, `translatePlurals()` — thin proxies.
23
+
24
+ Instead of the package's own `%s` interpolation, this codebase uses ad hoc single-token placeholders baked directly into translation strings and manually substituted at the call site, e.g. `localeService.t('predictionsDesc').replace("{$val}", finalScore)`.
25
+
26
+ `lib/template.js` additionally has a second, parallel string-getter, `getLocale(key)`, used at a couple of call sites instead of `localeService.t(key)` — worth confirming both resolve against the same underlying data before refactoring either.
27
+
28
+ ## 6.3 Locale JSON shape
29
+
30
+ Flat (no nesting — `objectNotation` is off), but each value is normally an **object**, not a plain string:
31
+
32
+ ```json
33
+ // locales/en.json
34
+ "APPROVED BY": { "message": "Approved", "description": "before the doctor signature..." }
35
+ ```
36
+
37
+ `LocaleService.t()` must append `.message` itself because of this shape. `locales/en.json` covers header/report labels, section titles, footer/pagination text, biomarker table headers, month names, and a `bigIntegral*` block. `locales/wellbeing/en.json` (424 lines) covers patient-header labels, section labels (`lifeStyle`/`body`/`mind`/`psychological`/`physical`), disclaimer text, corporate-report strings (`Copyright`, `Participant Analytics`), email body strings, and SanusX avatar/prediction content (including the `{$val}`/`{$value}` tokens mentioned above).
38
+
39
+ ## 6.4 RTL handling — three overlapping mechanisms
40
+
41
+ 1. **Whole alternate template files.** Every report family ships a matched pair, `rtl_no_pages.html` / `ltr_no_pages.html` (also under each brand subfolder). The RTL variant hardcodes `<html dir="rtl" lang="ar">` and loads a dedicated `assets/arabic.min.css` alongside the normal stylesheet.
42
+ 2. **Alternate block partials for Arabic.** The core report swaps in `templates/blocks/ar-first-page-header.html`, `ar-header.html`, `ar-footer.html`, `biomarker-compact-ar.html` when RTL.
43
+ 3. **Inline CSS direction/attribute switching** for pieces that aren't full templates — dozens of `isRtl ? ... : ...` inline-style branches (float/padding/text-align) scattered through `lib/template.js` and the report generators.
44
+
45
+ **RTL detection is a substring test:** `language.indexOf('ar') !== -1`, applied independently in the core report, wellbeing generator, and corporate report generator. In **all three** of those, once `isRtl` is true, **the language is forcibly overwritten to `'ar-SA'`**, regardless of whether the caller actually asked for `ar`, `ar-AE`, or `ar-SA`. Practical effect: **`locales/ar-AE.json` / `locales/wellbeing/ar-AE.json` are functionally unreachable** through those three pipelines — only `sanusx_report_generator.js` skips this coercion (though SanusX has its own, separate RTL problem — see [doc 4.D](04-report-pipelines.md#4d-sanusx-report): it never computes an `isRtl` flag at all and always loads the LTR template).
46
+
47
+ ## 6.5 Fallback logic — two layers that disagree
48
+
49
+ - **Package layer:** if a requested locale isn't in the configured `locales` list and there's no `fallbacks` entry (there never is, here), `i18n`'s own `setLocale`/`translate` internals force the locale to `defaultLocale` (`'en'`) — the behavior you'd naively expect.
50
+ - **`LocaleService` layer:** `setLocale()` only forwards to the package **if** the requested locale is already in `getLocales()`. If it isn't, the package's `setLocale` is **never called at all**, so its own fallback-to-`en` logic never runs — **the previously active locale on the shared singleton simply stays in effect.** Net effect: requesting an unsupported locale does not deterministically fall back to English; it silently reuses whatever locale a prior call last set on that same process-global singleton.
51
+ - Separately, the package's `updateFiles: true` default means a **missing translation key** (not a missing locale) causes the literal key string to be auto-persisted back into that locale's JSON file on disk the first time it's looked up, rather than falling back to another locale's text. This is visible today in `locales/wellbeing/en.json`, where a few keys near the bottom (e.g. `bigIntegralSystem`) are stored as bare strings rather than the usual `{message, description}` object — almost certainly auto-written by a prior lookup from the (currently unused) `big_integral_questionnaire.js` locale hook. **If that hook is ever wired up for real, `.message` access on these bare-string entries will return `undefined`, producing blank text in the PDF** — worth fixing (wrap them as proper `{message, description}` objects) before relying on them.
52
+
53
+ ## 6.6 Gotchas for a new engineer
54
+
55
+ - **Global, non-request-scoped locale state.** `i18n.setLocale(...)` mutates a shared, cached singleton. If two report-generation calls run concurrently in the same Node process (this package does not enforce single-threaded/sequential use), one request's language can leak into another's mid-render. There is no per-request locale scoping in use, though the `i18n` package does support it (passing a `req`/`res`-like context) — it's simply not adopted here. This is the same class of bug the codebase already had to specifically work around for temp file paths in `generateFullPdf` (see [doc 1.7](01-architecture-overview.md#17-filetemp-file-handling--an-evolving-pattern-worth-knowing)) — locale state was not given the same treatment.
56
+ - **Orphaned locale files.** `locales/ar.json` exists on disk but is **not** in `app/i18n.config.js`'s `locales` array, and — telling sign — its key set matches the *wellbeing* schema, not the main lab-report schema, suggesting it was dropped in the wrong directory. `locales/wellbeing/it-IT.json` similarly exists but `'it-IT'` is absent from `app/i18n_wellbeing.config.js`'s `locales` array. Neither file is ever loaded.
57
+ - **`ar-AE` is effectively dead** in three of the four generators (see 6.4).
58
+ - **Dead RTL template:** `templates/sanusx/rtl_no_pages.html` is never read by any code path.
59
+ - If you add a new locale, you must add it to **both** the correct `locales` array *and* the `LocaleService`'s implicit gate (it derives from the same array, so this is really one step) — but remember to check whether the RTL-coercion-to-`ar-SA` logic needs updating too if it's a new Arabic variant.
@@ -0,0 +1,59 @@
1
+ # 7. Email Notifications
2
+
3
+ ## 7.1 Module and exports
4
+
5
+ `lib/sendEmail.js` uses **nodemailer** and exports two functions:
6
+
7
+ - **`sendNascoEmail(data, pdfAttachmentPath, mailConfig, clientName)`** — sends a report email with the generated PDF attached. Used by `generateNascoPDF`, `generateFullPdf`, and `generateSanuxPDF` in `index.js` (i.e. shared across the wellbeing, extended-wellbeing, and SanusX flows, despite the "Nasco" name).
8
+ - **`sendEmailNotification(data)`** — a generic notification email, no PDF pipeline attached. Wired to `index.js`'s exported `sendEmail(json)`.
9
+
10
+ ## 7.2 SMTP transport configuration — entirely caller-supplied
11
+
12
+ There are **no environment variables and no hardcoded SMTP credentials** anywhere in this module. The transport is built directly from a `mailConfig` object that the caller constructs and passes in:
13
+
14
+ ```js
15
+ // index.js — constructed identically in generateNascoPDF, generateFullPdf, generateSanuxPDF
16
+ let mailConfig = {
17
+ host: reportData.host,
18
+ port: reportData.port,
19
+ authUser: reportData.authUser,
20
+ authPass: reportData.authPass,
21
+ sendFromEmail: reportData.sendFromEmail,
22
+ secure: reportData.secure
23
+ }
24
+ ```
25
+
26
+ ```js
27
+ // lib/sendEmail.js
28
+ var transporter = nodemailer.createTransport({
29
+ host: mailConfig.host,
30
+ port: mailConfig.port,
31
+ secure: false, // NOTE: hardcoded, see below
32
+ pool: true,
33
+ requireTLS: true,
34
+ maxConnections: 20,
35
+ maxMessages: 1,
36
+ auth: { user: mailConfig.authUser, pass: mailConfig.authPass }
37
+ });
38
+ ```
39
+
40
+ **Known bug:** `mailConfig.secure` is collected from the caller in all three call sites but **never actually read** by `sendMail` — the transporter hardcodes `secure: false` and instead forces a STARTTLS upgrade via `requireTLS: true`. So the connection isn't unencrypted (an unsupportable-TLS server will fail closed), but a caller who explicitly sets `secure: true` expecting implicit TLS (port-465 style) gets no such thing — the flag is dead code. If you ever need genuinely implicit-TLS support, this is where to add it.
41
+
42
+ `maxMessages: 1` combined with `pool: true` means a new SMTP connection/handshake is opened per message despite pooling being enabled — a minor inefficiency, not a security issue.
43
+
44
+ `sendEmailNotification` builds its own, differently-shaped `mailConfig` inline from the same style of caller-supplied fields.
45
+
46
+ ## 7.3 Email content
47
+
48
+ - **Attachment:** yes — the generated PDF, attached by **file path** (nodemailer reads it from disk, not from an in-memory buffer): `attachments: [{ path: pdfAttach, filename: data.name + " WellbeingReport.pdf", contentType: "application/pdf" }]`. In the `generateFullPdf` flow, this path is a per-call, UUID-suffixed temp file that gets unlinked after send (see [doc 1.7](01-architecture-overview.md#17-filetemp-file-handling--an-evolving-pattern-worth-knowing)); in the older `generateNascoPDF`/`generateSanuxPDF` flows it's a fixed shared filename under `output/`.
49
+ - **Subject/body (`sendNascoEmail`):** subject is a fixed, localized string (`localeService.t('wellbeingReport')`); the body is a hardcoded, i18n-translated multi-paragraph template — genuinely translated, but not caller-customizable beyond the patient's name.
50
+ - **Subject/body (`sendEmailNotification`):** fully caller-driven — `data.emailSubject`, plus `customHeaderHTML`/`customBodyHTML`/`customFooterHTML` override fields.
51
+ - **Branding:** `sendNascoEmail` pulls `logo`, `signature`, and a color from `config/{clientName}.json` (falling back to `config/default.json`) — using the client string **as-is, with no case-normalization and no `pha`→`Pha` mapping** (a third, different resolution rule from the ones in [doc 2.2](02-configuration-and-whitelabelling.md#22-how-clientclientname-resolves-to-a-config-file--and-why-its-inconsistent) — the caller must pass the exact on-disk filename casing here).
52
+ - **RTL:** email HTML uses a `lang === "ar"` check to flip `float`/`text-align`/`direction` in its hand-built inline styles — a simpler mechanism than the report templates', and using a different placeholder syntax: single-brace `{token}` (e.g. `{logo}`, `{header_text}`), not the `{{double-brace}}` convention used in the PDF templates. Don't apply PDF-template assumptions when editing email strings.
53
+
54
+ ## 7.4 Security considerations
55
+
56
+ - **SMTP credentials travel in plaintext inside the request payload.** `authUser`/`authPass` are plain JSON fields on the same object that's only base64-*encoded* (not encrypted) end to end — see [doc 1.3](01-architecture-overview.md#13-entry-points-indexjs). Anyone who can observe the call to this library (or a log of it) can trivially recover the SMTP password. If `isDebugging` logging is ever extended to dump the full `reportData`/`mailConfig` object, credentials would land in `output/LOGS-*.txt` in plaintext.
57
+ - **No enforcement of the caller's `secure` intent** — see 7.2. This is a config-drift risk more than an active vulnerability (the connection still requires TLS via `requireTLS`), but it means the `secure` field is misleading and should either be honored or removed from the API surface.
58
+ - **No hardcoded credentials found** in this module. The only hardcoded value is the fallback sender address `noreply@medicus.ai`, which is not a secret.
59
+ - **Recommendation for anyone hardening this:** if the host application controls both ends, consider moving SMTP credentials to a per-deployment secret/environment variable inside the *host* app rather than passing them through this library's payload on every call — this library has no mechanism for that today, so it would be a host-application-level change, not a change to this package's public API shape necessarily, but worth raising with whoever owns the calling system.
@@ -0,0 +1,41 @@
1
+ # 8. Known Issues & Technical Debt
2
+
3
+ Consolidated from all the research behind this documentation set, ranked roughly by how much time/pain each one can cost a new engineer. Each links to the doc section with full detail.
4
+
5
+ ## Critical — will actively bite you
6
+
7
+ 1. **`index.js` requires `lib/pdf_generator.min.js`, not `lib/pdf_generator.js`.** The two files have diverged independently since 2021 with no build step connecting them. Editing the readable source file silently does nothing. → [doc 1.4](01-architecture-overview.md#14-critical-fact-the-core-report-renderer-that-actually-runs-is-pdf_generatorminjs-not-pdf_generatorjs)
8
+ 2. **SMTP credentials travel as plaintext JSON fields** inside a payload that's only base64-*encoded*, not encrypted. A logged request or an intercepted call leaks the SMTP password outright. → [doc 7.4](07-email-notifications.md#74-security-considerations)
9
+ 3. **Client-name → config-file resolution is inconsistent across all five places that do it** (wellbeing generator, corporate generator, sendEmail, sanusx generator, core report which doesn't do it at all). Onboarding a new brand or debugging "why isn't my branding showing" requires checking the exact rule for the specific generator in use. → [doc 2.2](02-configuration-and-whitelabelling.md#22-how-clientclientname-resolves-to-a-config-file--and-why-its-inconsistent)
10
+ 4. **`corporate_report_generator.js` lower-cases the client string before building a filename path**, but the actual `config/*.json` files are capitalized. Works today only because of case-insensitive filesystems (Windows/macOS); will silently fall back to `default.json` on Linux. → [doc 2.2](02-configuration-and-whitelabelling.md#22-how-clientclientname-resolves-to-a-config-file--and-why-its-inconsistent)
11
+ 5. **`generateFullPdf`'s `showHeaderLogo || true` bug** — the header logo can never be suppressed regardless of what the caller passes. → [doc 4.B](04-report-pipelines.md#4b-wellbeing-report--optional-merged-smartreport)
12
+
13
+ ## High — will cost real debugging time
14
+
15
+ 6. **`sanusx_report_generator.js` ignores its `clientName` parameter for branding entirely** — always loads `config/sanusx.json`. Effectively single-brand despite accepting a client argument. → [doc 4.D](04-report-pipelines.md#4d-sanusx-report)
16
+ 7. **SanusX RTL support is broken** — no `isRtl` flag is ever computed, the LTR template loads unconditionally, and the RTL template that does exist on disk references asset paths that don't exist under `assets/sanusx/`. → [doc 4.D](04-report-pipelines.md#4d-sanusx-report)
17
+ 8. **SanusX's PDF is hard-limited to one page** (`pageRanges: '1'`) — any overflow is silently dropped, not pushed to page 2. → [doc 4.D](04-report-pipelines.md#4d-sanusx-report)
18
+ 9. **`lib/big_integral_questionnaire.js` renders 100% hardcoded dummy data**, not real patient answers. Easy to mistake for a real, working feature. → [doc 4.B.1](04-report-pipelines.md#4b1-libbig_integral_questionnairejs--read-this-before-assuming-its-a-real-feature)
19
+ 10. **Locale state is a process-global mutable singleton** (`i18n.setLocale(...)` mutates a shared, cached module). Concurrent report-generation calls in the same process can leak one request's language into another's render. The codebase already had to specifically patch around an analogous bug for temp-file paths (`generateFullPdf`'s UUID scheme) but never applied the same fix to locale state. → [doc 6.6](06-internationalization.md#66-gotchas-for-a-new-engineer)
20
+ 11. **Older flows (`generateMedicusPDF`, `generateNascoPDF`, `generateSanuxPDF`) write to fixed, shared filenames under `output/`** with no cleanup — concurrent requests can clobber each other's files, and disk usage grows unbounded over time. Only `generateFullPdf` was hardened with per-call UUID temp paths + cleanup. → [doc 1.7](01-architecture-overview.md#17-filetemp-file-handling--an-evolving-pattern-worth-knowing)
21
+ 12. **`LocaleService.setLocale()` silently no-ops on an unsupported locale** instead of falling back to English like the underlying package would — the previously active locale on the shared singleton just stays in effect. → [doc 6.5](06-internationalization.md#65-fallback-logic--two-layers-that-disagree)
22
+ 13. **`renderDoctorDetails` (wellbeing `data.Profile`) does not HTML-escape answer values** — a real risk if any upstream source of Q&A text isn't already sanitized. → [doc 5.4](05-questions-and-data-model.md#54-security-note-unescaped-answer-text)
23
+
24
+ ## Medium — worth fixing, lower urgency
25
+
26
+ 14. **No fallback from a brand template folder to shared `templates/blocks/`** for individual missing blocks — the "extended" wellbeing flow throws `ENOENT` if a client folder is incomplete or missing entirely; only `Mediclinic`, `Pha`, `maisonsante` are supported today. → [doc 2.4](02-configuration-and-whitelabelling.md#24-template-folder-selection--no-blocks-level-fallback)
27
+ 15. **Two independent copies of Chart.js** (npm `chart.js`, unused/dead server-side import, vs. bundled `assets/charts.min.js`, actually used client-side inside headless Chrome) can silently drift out of version sync. → [doc 3.4](03-templating-and-rendering.md#34-charts)
28
+ 16. **`generatePatientQR`/`generateQrCode` doesn't generate QR codes** — it's a generic "rasterize this HTML string" utility; the caller must pre-render the QR (e.g. as inline SVG) before calling it. Misleading name. → [doc 3.5](03-templating-and-rendering.md#35-qr-codes--two-unrelated-mechanisms)
29
+ 17. **Orphaned/dead locale files**: `locales/ar.json` (wrong schema for its directory, not in the configured locales list) and `locales/wellbeing/it-IT.json` (not in the configured locales list). → [doc 6.6](06-internationalization.md#66-gotchas-for-a-new-engineer)
30
+ 18. **`ar-AE` is functionally unreachable** in three of the four generators — RTL detection coerces any `"ar"`-containing language straight to `ar-SA`. → [doc 6.4](06-internationalization.md#64-rtl-handling--three-overlapping-mechanisms)
31
+ 19. **Auto-write-on-missing-key behavior** (`i18n`'s `updateFiles: true` default) has already silently written malformed entries into `locales/wellbeing/en.json` (bare strings instead of `{message, description}` objects) from a currently-unused lookup path — a landmine if that lookup is ever wired up for real. → [doc 6.5](06-internationalization.md#65-fallback-logic--two-layers-that-disagree)
32
+ 20. **Corporate report has a live external CDN dependency at render time** (`page.addScriptTag({ url: 'https://cdn.jsdelivr.net/...' })`) — unlike the other three pipelines, which bundle their own JS/CSS locally. A network outage or CDN change could break corporate-report generation specifically. → [doc 4.C](04-report-pipelines.md#4c-corporate-report)
33
+ 21. **Onboarding a new brand's corporate-report cover background requires editing shared CSS by client-name selector** (`.cover-overlay-container.bionext {...}`), bypassing the config-JSON theming model otherwise used everywhere else. → [doc 4.C](04-report-pipelines.md#4c-corporate-report)
34
+ 22. **Corporate/SanusX translation strings live in the `locales/wellbeing/` bucket**, not a dedicated namespace, despite neither product being "wellbeing." → [doc 6.1](06-internationalization.md#61-library-and-the-two-independent-configurations)
35
+ 23. **`sendNascoEmail` is reused across wellbeing, extended-wellbeing, and SanusX flows** despite its Nasco-specific name — a shared helper with a misleading, client-specific name baked into common infrastructure. → [doc 7.1](07-email-notifications.md#71-module-and-exports)
36
+ 24. **Dead code accumulation**: unused `templates/base.html`/`template.html`/`ltr.html`/`no_pages.html`/`empty.html`/`first_page_head.html`; `templates/popup/popup-template.html` (unreferenced anywhere); `templates/sanusx/blocks/tips.html` (unreferenced — a different `tips.html` under `templates/wellbeing/blocks/` is the one actually read); `combinePDFBuffers`/`isEmpty` copy-pasted but unused in `corporate_report_generator.js`; `generateHeaderInfo()`'s output computed but discarded (target selector commented out); `historyData` always an empty array, returned but never populated, across all three non-core generators; dead `chart.js`/`qr-image`/`qrcode` npm imports server-side. None of these are actively harmful, but they add noise when searching the codebase and should be pruned opportunistically.
37
+ 25. **`mailConfig.secure` is collected from every caller but never read** by the actual transport — dead parameter, misleading API surface. → [doc 7.2](07-email-notifications.md#72-smtp-transport-configuration--entirely-caller-supplied)
38
+
39
+ ## Suggested first fixes if you're picking one place to start
40
+
41
+ If asked to spend a day improving this codebase's reliability rather than adding features, the highest-leverage fixes are (1) wiring an actual build step (or simply deleting `pdf_generator.js` and renaming `.min.js`) to remove the source/runtime divergence risk, (3)/(4) unifying client-name resolution into one shared helper function used by all five call sites, and (10) scoping locale state per-request instead of relying on a shared singleton.
package/docs/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # Medicus PDF Generator — Documentation
2
+
3
+ This is the full technical documentation for `@medicus.ai/medicus-report-pdf-generator`, a Node.js library that turns JSON health-report data into branded PDF documents (and, for one flow, emails them out).
4
+
5
+ **Read this first:** [`08-known-issues-and-technical-debt.md`](08-known-issues-and-technical-debt.md) and the project-root [`HANDOVER.md`](../HANDOVER.md) contain the handful of facts that will save you the most debugging time (in particular: `index.js` runs `lib/pdf_generator.min.js`, not the readable `lib/pdf_generator.js`).
6
+
7
+ ## Contents
8
+
9
+ 1. [Architecture Overview](01-architecture-overview.md) — what this package is, module map, entry points, dependencies, data flow.
10
+ 2. [Configuration & Whitelabelling](02-configuration-and-whitelabelling.md) — the `config/` system, how a `client` string selects branding/templates, per-generator inconsistencies.
11
+ 3. [Templating & Rendering Pipeline](03-templating-and-rendering.md) — how HTML is built from data (no template engine — string replace + jsdom/jQuery), how it becomes a PDF (Puppeteer), charts, QR codes.
12
+ 4. [Report Pipelines](04-report-pipelines.md) — the four distinct report generators (Core "Medicus", Wellbeing, Corporate, SanusX): purpose, entry points, data shape, brand support.
13
+ 5. [Questions, Answers & the Data Model](05-questions-and-data-model.md) — directly answers "where do questions come from and how are they rendered."
14
+ 6. [Internationalization (i18n) & RTL](06-internationalization.md) — locale system, Arabic/RTL handling, gotchas.
15
+ 7. [Email Notifications](07-email-notifications.md) — `lib/sendEmail.js`, SMTP config, security notes.
16
+ 8. [Known Issues & Technical Debt](08-known-issues-and-technical-debt.md) — consolidated list of bugs, dead code, and risks found while writing this documentation.
17
+
18
+ ## What this project is NOT
19
+
20
+ - It is **not a web server**. There is no HTTP listener, no routes, no session handling anywhere in this repository. It is a plain npm library — `index.js` exports async functions that a **host application** (e.g. a Meteor app, per the README) calls directly in-process, or via the `run.js` child-process wrapper.
21
+ - It has **no authentication or authorization layer**. There are no auth-related dependencies in `package.json` (no JWT, no passport, no session store). Any access control, user identity, or permission checking is entirely the host application's responsibility — this package trusts whatever JSON payload it is handed.
22
+ - It has **no database**. Every function call is stateless: it receives a JSON payload (usually base64-encoded), renders it, and returns a PDF/HTML/base64 string. Nothing is persisted except transient files under `output/` or the OS temp directory.
package/downloadfile.js CHANGED
@@ -1,7 +1,7 @@
1
- const puppeteer = require('puppeteer');
2
- const browserFetcher = puppeteer.createBrowserFetcher({platform: 'win64'});
3
- const revision = require('puppeteer/package').puppeteer.chromium_revision;
4
-
5
- browserFetcher.download(revision)
6
- .then(() => console.log('Done, with revision number: ', revision))
1
+ const puppeteer = require('puppeteer');
2
+ const browserFetcher = puppeteer.createBrowserFetcher({platform: 'win64'});
3
+ const revision = require('puppeteer/package').puppeteer.chromium_revision;
4
+
5
+ browserFetcher.download(revision)
6
+ .then(() => console.log('Done, with revision number: ', revision))
7
7
  .catch(error => console.log('Error', error));