iobroker.autodoc 0.9.35

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -0
  3. package/admin/autodoc.png +0 -0
  4. package/admin/i18n/de.json +244 -0
  5. package/admin/i18n/en.json +241 -0
  6. package/admin/i18n/es.json +229 -0
  7. package/admin/i18n/fr.json +235 -0
  8. package/admin/i18n/it.json +229 -0
  9. package/admin/i18n/nl.json +229 -0
  10. package/admin/i18n/pl.json +229 -0
  11. package/admin/i18n/pt.json +229 -0
  12. package/admin/i18n/ru.json +229 -0
  13. package/admin/i18n/uk.json +229 -0
  14. package/admin/i18n/zh-cn.json +229 -0
  15. package/admin/jsonConfig.json +1490 -0
  16. package/io-package.json +253 -0
  17. package/lib/adapter-config.d.ts +19 -0
  18. package/lib/aiEnhancer.js +2114 -0
  19. package/lib/autoHostTopologyMermaid.js +195 -0
  20. package/lib/dependencyAnalyzer.js +83 -0
  21. package/lib/diagnosisSnapshot.js +32 -0
  22. package/lib/discovery.js +953 -0
  23. package/lib/docTemplateConfig.js +422 -0
  24. package/lib/documentModel.js +640 -0
  25. package/lib/forumCard.js +70 -0
  26. package/lib/guestHelpContent.js +93 -0
  27. package/lib/guestScriptPrivacy.js +14 -0
  28. package/lib/hostDisplay.js +19 -0
  29. package/lib/htmlRenderer.js +4108 -0
  30. package/lib/htmlThemePresets.js +79 -0
  31. package/lib/htmlToPdf.js +99 -0
  32. package/lib/i18n.js +1309 -0
  33. package/lib/markdownRenderer.js +2025 -0
  34. package/lib/mermaidAutodocPalette.js +165 -0
  35. package/lib/mermaidServerSvg.js +252 -0
  36. package/lib/notifier.js +124 -0
  37. package/lib/quickStartGuide.js +180 -0
  38. package/lib/roleMapper.js +90 -0
  39. package/lib/scriptGroups.js +78 -0
  40. package/lib/versionTracker.js +312 -0
  41. package/main.js +1368 -0
  42. package/package.json +88 -0
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Optional named CSS variable packs — same ids as `htmlThemePreset` / `lib/htmlThemePresets.js`.
3
+ */
4
+ 'use strict';
5
+
6
+ const PRESET_SET = new Set(['default', 'highContrast', 'warm', 'slate']);
7
+
8
+ /**
9
+ * @param {string} presetId Raw or normalized `htmlThemePreset` id
10
+ * @returns {string} One of default, highContrast, warm, slate
11
+ */
12
+ function normalizeMermaidPreset(presetId) {
13
+ const s = String(presetId || 'default');
14
+ return PRESET_SET.has(s) ? s : 'default';
15
+ }
16
+
17
+ /**
18
+ * Full `themeVariables` for Mermaid `theme: base` (one HTML palette × light/dark shell).
19
+ *
20
+ * @param {string} presetId Preset id (see {@link normalizeMermaidPreset})
21
+ * @param {boolean} isDark Whether export uses dark diagram palette (body.dark / mmdc dark)
22
+ * @returns {Record<string, string | boolean>} Mermaid themeVariables
23
+ */
24
+ function getAutodocMermaidThemeVariables(presetId, isDark) {
25
+ const p = normalizeMermaidPreset(presetId);
26
+ if (!isDark) {
27
+ switch (p) {
28
+ case 'highContrast':
29
+ return {
30
+ darkMode: false,
31
+ background: '#e2e2e2',
32
+ primaryColor: '#f5f5f5',
33
+ primaryTextColor: '#0a0a0a',
34
+ lineColor: '#0a0a0a',
35
+ arrowheadColor: '#0a0a0a',
36
+ defaultLinkColor: '#0a0a0a',
37
+ clusterBkg: '#dedede',
38
+ clusterBorder: '#1f1f1f',
39
+ titleColor: '#0a0a0a',
40
+ };
41
+ case 'warm':
42
+ return {
43
+ darkMode: false,
44
+ background: '#efe8de',
45
+ primaryColor: '#f5ebe0',
46
+ primaryTextColor: '#2a2218',
47
+ lineColor: '#4a3f32',
48
+ arrowheadColor: '#4a3f32',
49
+ defaultLinkColor: '#4a3f32',
50
+ clusterBkg: '#e2d9cc',
51
+ clusterBorder: '#c4b8a8',
52
+ titleColor: '#2a2218',
53
+ };
54
+ case 'slate':
55
+ return {
56
+ darkMode: false,
57
+ background: '#e4e7ec',
58
+ primaryColor: '#e8ecf0',
59
+ primaryTextColor: '#1a1f2a',
60
+ lineColor: '#3d4859',
61
+ arrowheadColor: '#3d4859',
62
+ defaultLinkColor: '#3d4859',
63
+ clusterBkg: '#d8dee8',
64
+ clusterBorder: '#9aa3b0',
65
+ titleColor: '#1a1f2a',
66
+ };
67
+ default:
68
+ return {
69
+ darkMode: false,
70
+ lineColor: '#334155',
71
+ arrowheadColor: '#334155',
72
+ defaultLinkColor: '#334155',
73
+ clusterBkg: '#f0f3f7',
74
+ clusterBorder: '#c8d0db',
75
+ titleColor: '#333333',
76
+ };
77
+ }
78
+ }
79
+ switch (p) {
80
+ case 'highContrast':
81
+ return {
82
+ darkMode: true,
83
+ background: '#0a0a0a',
84
+ primaryColor: '#2a2a2a',
85
+ primaryTextColor: '#f5f5f5',
86
+ lineColor: '#f0f0f0',
87
+ arrowheadColor: '#f0f0f0',
88
+ defaultLinkColor: '#f0f0f0',
89
+ clusterBkg: '#222222',
90
+ clusterBorder: '#666666',
91
+ titleColor: '#f5f5f5',
92
+ };
93
+ case 'warm':
94
+ return {
95
+ darkMode: true,
96
+ background: '#1a1510',
97
+ primaryColor: '#3a3228',
98
+ primaryTextColor: '#e8ddd0',
99
+ lineColor: '#c8b8a8',
100
+ arrowheadColor: '#c8b8a8',
101
+ defaultLinkColor: '#c8b8a8',
102
+ clusterBkg: '#2a2418',
103
+ clusterBorder: '#4a3f32',
104
+ titleColor: '#e8ddd0',
105
+ };
106
+ case 'slate':
107
+ return {
108
+ darkMode: true,
109
+ background: '#0f1117',
110
+ primaryColor: '#2a3344',
111
+ primaryTextColor: '#e0e6ef',
112
+ lineColor: '#9aa8bc',
113
+ arrowheadColor: '#9aa8bc',
114
+ defaultLinkColor: '#9aa8bc',
115
+ clusterBkg: '#242b38',
116
+ clusterBorder: '#3a4556',
117
+ titleColor: '#e0e6ef',
118
+ };
119
+ default:
120
+ return {
121
+ darkMode: true,
122
+ background: '#1a1d2e',
123
+ primaryColor: '#5c4d7a',
124
+ primaryTextColor: '#f0f0f0',
125
+ lineColor: '#d0d5e0',
126
+ arrowheadColor: '#d0d5e0',
127
+ defaultLinkColor: '#d0d5e0',
128
+ clusterBkg: '#262a3f',
129
+ clusterBorder: '#3d4556',
130
+ titleColor: '#e0e0e0',
131
+ };
132
+ }
133
+ }
134
+
135
+ /**
136
+ * All preset × mode pairs for client `mermaid.initialize` (embedded JSON in HTML).
137
+ *
138
+ * @returns {object} Keys default, highContrast, warm, slate → { light, dark } variable maps
139
+ */
140
+ function getMermaidClientThemeMatrix() {
141
+ return {
142
+ default: {
143
+ light: getAutodocMermaidThemeVariables('default', false),
144
+ dark: getAutodocMermaidThemeVariables('default', true),
145
+ },
146
+ highContrast: {
147
+ light: getAutodocMermaidThemeVariables('highContrast', false),
148
+ dark: getAutodocMermaidThemeVariables('highContrast', true),
149
+ },
150
+ warm: {
151
+ light: getAutodocMermaidThemeVariables('warm', false),
152
+ dark: getAutodocMermaidThemeVariables('warm', true),
153
+ },
154
+ slate: {
155
+ light: getAutodocMermaidThemeVariables('slate', false),
156
+ dark: getAutodocMermaidThemeVariables('slate', true),
157
+ },
158
+ };
159
+ }
160
+
161
+ module.exports = {
162
+ normalizeMermaidPreset,
163
+ getAutodocMermaidThemeVariables,
164
+ getMermaidClientThemeMatrix,
165
+ };
@@ -0,0 +1,252 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Embed Mermaid diagrams as inline SVG during HTML generation (Phase 5 — TODO § 1.2a).
5
+ * Uses @mermaid-js/mermaid-cli (Puppeteer) when installed as optional dependency; otherwise leaves <pre class="mermaid"> for client-side jsDelivr render.
6
+ */
7
+
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+ const os = require('node:os');
11
+ const { execFile } = require('node:child_process');
12
+ const { promisify } = require('node:util');
13
+
14
+ const execFileAsync = promisify(execFile);
15
+
16
+ const { parseHtmlThemePreset } = require('./docTemplateConfig');
17
+ const { getAutodocMermaidThemeVariables, normalizeMermaidPreset } = require('./mermaidAutodocPalette');
18
+
19
+ /** @returns {string|null} Path to mermaid-cli `src/cli.js`, or null if not installed */
20
+ function resolveMmdcCliJs() {
21
+ let dir = path.join(__dirname, '..');
22
+ for (let i = 0; i < 12; i++) {
23
+ const candidate = path.join(dir, 'node_modules', '@mermaid-js', 'mermaid-cli', 'src', 'cli.js');
24
+ if (fs.existsSync(candidate)) {
25
+ return candidate;
26
+ }
27
+ const parent = path.dirname(dir);
28
+ if (parent === dir) {
29
+ break;
30
+ }
31
+ dir = parent;
32
+ }
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Undo {@link esc} in htmlRenderer (ampersand must be last).
38
+ *
39
+ * @param {string} innerHtml Raw inner HTML of `pre.mermaid`
40
+ * @returns {string} Decoded Mermaid source text
41
+ */
42
+ function decodePreEscapedMermaid(innerHtml) {
43
+ return innerHtml
44
+ .replace(/&quot;/g, '"')
45
+ .replace(/&gt;/g, '>')
46
+ .replace(/&lt;/g, '<')
47
+ .replace(/&amp;/g, '&');
48
+ }
49
+
50
+ /**
51
+ * Puppeteer launch options for headless Chromium inside Docker/LXC/Unraid, where the
52
+ * kernel/user-namespace often provides no usable SUID sandbox (Chromium then exits with
53
+ * "No usable sandbox"). Diagram source is adapter-admin-controlled Mermaid text only.
54
+ *
55
+ * @param {string} tmpDir Temp directory (mkdtemp) — config file is removed with the rest
56
+ * @returns {Promise<string>} Absolute path to written JSON file
57
+ * @see https://pptr.dev/troubleshooting
58
+ */
59
+ async function writeMmdcPuppeteerConfigFile(tmpDir) {
60
+ const cfgPath = path.join(tmpDir, 'puppeteer-config.json');
61
+ const cfg = {
62
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
63
+ };
64
+ await fs.promises.writeFile(cfgPath, JSON.stringify(cfg), 'utf8');
65
+ return cfgPath;
66
+ }
67
+
68
+ /**
69
+ * Mermaid JSON for `mmdc -c` — uses `theme: "base"` so `themeVariables` apply (cluster + edge colors).
70
+ * Respects `htmlThemePreset` the same way as export CSS (`lib/htmlThemePresets.js`).
71
+ *
72
+ * @param {string} tmpDir Temp dir for `mermaid-config.json`
73
+ * @param {'default'|'dark'} mmdcTheme Palette from {@link mermaidCliTheme}
74
+ * @param {unknown} [htmlThemePreset] Raw or parsed `htmlThemePreset` config id
75
+ * @returns {Promise<string>} Path to written JSON
76
+ */
77
+ async function writeMmdcMermaidConfigFile(tmpDir, mmdcTheme, htmlThemePreset) {
78
+ const cfgPath = path.join(tmpDir, 'mermaid-config.json');
79
+ const isDark = mmdcTheme === 'dark';
80
+ const preset = normalizeMermaidPreset(parseHtmlThemePreset(htmlThemePreset));
81
+ const cfg = { theme: 'base', themeVariables: getAutodocMermaidThemeVariables(preset, isDark) };
82
+ await fs.promises.writeFile(cfgPath, JSON.stringify(cfg), 'utf8');
83
+ return cfgPath;
84
+ }
85
+
86
+ /**
87
+ * @param {string} source Mermaid definition
88
+ * @param {{ theme: 'default'|'dark', themePreset?: unknown, width?: number, height?: number }} opts mmdc sizing, shell light/dark, HTML theme preset
89
+ * @returns {Promise<string>} SVG markup
90
+ */
91
+ async function runMmdcToSvg(source, opts) {
92
+ const cliJs = resolveMmdcCliJs();
93
+ if (!cliJs || !fs.existsSync(cliJs)) {
94
+ throw new Error('@mermaid-js/mermaid-cli not available');
95
+ }
96
+ const width = opts.width ?? 1400;
97
+ const height = opts.height ?? 1000;
98
+ const palette = opts.theme === 'dark' ? 'dark' : 'default';
99
+ const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'autodoc-mermaid-'));
100
+ const inFile = path.join(tmpDir, 'diagram.mmd');
101
+ const outFile = path.join(tmpDir, 'diagram.svg');
102
+ try {
103
+ await fs.promises.writeFile(inFile, source, 'utf8');
104
+ const puppeteerConfigFile = await writeMmdcPuppeteerConfigFile(tmpDir);
105
+ const mermaidConfigFile = await writeMmdcMermaidConfigFile(tmpDir, palette, opts.themePreset);
106
+ await execFileAsync(
107
+ process.execPath,
108
+ [
109
+ cliJs,
110
+ '-i',
111
+ inFile,
112
+ '-o',
113
+ outFile,
114
+ /* CLI 10.x only allows default|forest|dark|neutral; config overwrites with theme "base". */
115
+ '-t',
116
+ palette,
117
+ '-c',
118
+ mermaidConfigFile,
119
+ '-w',
120
+ String(width),
121
+ '-H',
122
+ String(height),
123
+ '-b',
124
+ 'transparent',
125
+ '-p',
126
+ puppeteerConfigFile,
127
+ '-q',
128
+ ],
129
+ { timeout: 120000, windowsHide: true, maxBuffer: 8 * 1024 * 1024 },
130
+ );
131
+ return await fs.promises.readFile(outFile, 'utf8');
132
+ } finally {
133
+ await fs.promises.rm(tmpDir, { recursive: true, force: true });
134
+ }
135
+ }
136
+
137
+ /**
138
+ * @param {'light'|'dark'|'auto'} colorScheme From {@link parseHtmlColorScheme}
139
+ * @returns {'default'|'dark'} Theme value for mmdc `-t`
140
+ */
141
+ function mermaidCliTheme(colorScheme) {
142
+ return colorScheme === 'dark' ? 'dark' : 'default';
143
+ }
144
+
145
+ /**
146
+ * @param {string} html Full HTML document
147
+ * @param {{ colorScheme?: 'light'|'dark'|'auto', themePreset?: unknown, log?: { debug?: Function, warn?: Function } }} [options] Adapter logger + `htmlThemePreset`
148
+ * @returns {Promise<string>} HTML with diagrams embedded or unchanged
149
+ */
150
+ async function embedMermaidDiagramsInHtml(html, options = {}) {
151
+ const cliJs = resolveMmdcCliJs();
152
+ if (!cliJs || !fs.existsSync(cliJs)) {
153
+ options.log?.debug?.(
154
+ 'Mermaid: @mermaid-js/mermaid-cli not installed — HTML uses client-side Mermaid (jsDelivr). Install optional dependency for embedded SVG.',
155
+ );
156
+ return html;
157
+ }
158
+
159
+ const scheme =
160
+ options.colorScheme === 'light' || options.colorScheme === 'dark' || options.colorScheme === 'auto'
161
+ ? options.colorScheme
162
+ : 'auto';
163
+ const theme = mermaidCliTheme(scheme);
164
+ const themePreset = options.themePreset;
165
+
166
+ /**
167
+ * @param {string} docHtml HTML to scan
168
+ * @param {RegExp} re Full block to replace; group 1 = inner `pre.mermaid` text
169
+ * @returns {Promise<{ html: string, mmdcFailureCount: number, firstMmdcError: string }>} Rebuilt HTML and failure stats
170
+ */
171
+ async function replaceMermaidPreBlocks(docHtml, re) {
172
+ const parts = [];
173
+ let lastIndex = 0;
174
+ let match;
175
+ const reGlobal = new RegExp(re.source, 'g');
176
+ let failCount = 0;
177
+ let firstErr = '';
178
+ while ((match = reGlobal.exec(docHtml)) !== null) {
179
+ parts.push(docHtml.slice(lastIndex, match.index));
180
+ const inner = match[1];
181
+ const fullMatch = match[0];
182
+ const source = decodePreEscapedMermaid(inner).trim();
183
+ if (!source) {
184
+ parts.push(fullMatch);
185
+ } else {
186
+ try {
187
+ const svg = await runMmdcToSvg(source, { theme, themePreset });
188
+ parts.push(`<div class="mermaid-wrap mermaid-svg-embedded">${svg}</div>`);
189
+ } catch (e) {
190
+ failCount++;
191
+ if (!firstErr) {
192
+ firstErr = e instanceof Error ? e.message : String(e);
193
+ }
194
+ const msg = e instanceof Error ? e.message : String(e);
195
+ options.log?.debug?.(
196
+ `Mermaid SVG (mmdc) failed for one diagram — keeping <pre class="mermaid">: ${msg}`,
197
+ );
198
+ parts.push(fullMatch);
199
+ }
200
+ }
201
+ lastIndex = match.index + fullMatch.length;
202
+ }
203
+ parts.push(docHtml.slice(lastIndex));
204
+ return { html: parts.join(''), mmdcFailureCount: failCount, firstMmdcError: firstErr };
205
+ }
206
+
207
+ const wrappedRe = /<div class="mermaid-wrap">\s*<pre class="mermaid">([\s\S]*?)<\/pre>\s*<\/div>/g;
208
+ const barePreRe = /<pre class="mermaid">([\s\S]*?)<\/pre>/g;
209
+
210
+ let mmdcFailureCount = 0;
211
+ let firstMmdcError = '';
212
+ let out = html;
213
+
214
+ const pass1 = await replaceMermaidPreBlocks(out, wrappedRe);
215
+ out = pass1.html;
216
+ mmdcFailureCount += pass1.mmdcFailureCount;
217
+ if (pass1.firstMmdcError) {
218
+ firstMmdcError = pass1.firstMmdcError;
219
+ }
220
+
221
+ const pass2 = await replaceMermaidPreBlocks(out, barePreRe);
222
+ out = pass2.html;
223
+ mmdcFailureCount += pass2.mmdcFailureCount;
224
+ if (!firstMmdcError && pass2.firstMmdcError) {
225
+ firstMmdcError = pass2.firstMmdcError;
226
+ }
227
+
228
+ if (mmdcFailureCount > 0) {
229
+ let hint =
230
+ 'Install OS libraries for headless Chromium on Linux if you see libnss3 (etc.); see https://pptr.dev/troubleshooting — or use generated HTML online (jsDelivr).';
231
+ if (/sandbox|no-sandbox/i.test(firstMmdcError)) {
232
+ hint =
233
+ 'If the error mentions Chromium sandbox / Docker: autodoc passes --no-sandbox to mmdc (update adapter). Else install OS libs (libnss3, …) — https://pptr.dev/troubleshooting.';
234
+ }
235
+ const errShort = firstMmdcError.length > 180 ? `${firstMmdcError.slice(0, 180)}…` : firstMmdcError;
236
+ options.log?.warn?.(
237
+ `Mermaid: ${mmdcFailureCount} diagram(s) not embedded as SVG (mmdc failed). ${hint} First error: ${errShort}`,
238
+ );
239
+ }
240
+ return out;
241
+ }
242
+
243
+ module.exports = {
244
+ getAutodocMermaidThemeVariables,
245
+ embedMermaidDiagramsInHtml,
246
+ resolveMmdcCliJs,
247
+ decodePreEscapedMermaid,
248
+ runMmdcToSvg,
249
+ mermaidCliTheme,
250
+ writeMmdcPuppeteerConfigFile,
251
+ writeMmdcMermaidConfigFile,
252
+ };
@@ -0,0 +1,124 @@
1
+ /**
2
+ * AutoDoc Notifier Module
3
+ * Sends a notification via ioBroker messaging adapters after documentation generation.
4
+ */
5
+
6
+ const ADAPTER_TELEGRAM = 'telegram';
7
+ const ADAPTER_PUSHOVER = 'pushover';
8
+ const ADAPTER_EMAIL = 'email';
9
+ const ADAPTER_SIGNAL = 'signal-cmb';
10
+ const ADAPTER_WHATSAPP = 'whatsapp-cmb';
11
+
12
+ /**
13
+ * Notifier sends a message via sendTo after documentation generation.
14
+ *
15
+ * @param {object} adapter ioBroker adapter instance
16
+ */
17
+ class Notifier {
18
+ /**
19
+ * @param {object} adapter ioBroker adapter instance
20
+ */
21
+ constructor(adapter) {
22
+ this.adapter = adapter;
23
+ }
24
+
25
+ /**
26
+ * Send notification if enabled and configured.
27
+ *
28
+ * @param {object} docModel Generated document model
29
+ * @param {object} changeData Change summary from version tracker
30
+ * @returns {Promise<void>}
31
+ */
32
+ async send(docModel, changeData) {
33
+ const config = this.adapter.config;
34
+
35
+ if (!config.notifyEnabled) {
36
+ return;
37
+ }
38
+
39
+ const instance = (config.notifyInstance || '').trim();
40
+ if (!instance) {
41
+ this.adapter.log.warn('Notification enabled but no adapter instance configured');
42
+ return;
43
+ }
44
+
45
+ const message = this.buildMessage(docModel, changeData);
46
+ const adapterType = instance.split('.')[0].toLowerCase();
47
+ const payload = this.buildPayload(adapterType, message, config.notifyTarget);
48
+
49
+ try {
50
+ await this.adapter.sendToAsync(instance, 'send', payload);
51
+ this.adapter.log.info(`Notification sent via ${instance}`);
52
+ } catch (err) {
53
+ this.adapter.log.warn(`Notification failed (${instance}): ${err.message}`);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Build the notification message text.
59
+ *
60
+ * @param {object} docModel Document model
61
+ * @param {object} changeData Change summary
62
+ * @returns {string} Message text
63
+ */
64
+ buildMessage(docModel, changeData) {
65
+ const template = (this.adapter.config.notifyMessage || '').trim();
66
+ const vars = {
67
+ '{project}': docModel.system.projectName || 'ioBroker',
68
+ '{summary}': changeData.summary || '',
69
+ '{version}': docModel.meta.version || '',
70
+ '{trigger}': docModel.meta.trigger || '',
71
+ };
72
+
73
+ if (template) {
74
+ return template.replace(/\{project\}|\{summary\}|\{version\}|\{trigger\}/g, m => vars[m] || m);
75
+ }
76
+
77
+ return `AutoDoc: "${vars['{project}']}" documentation generated — ${vars['{summary}']} (v${vars['{version}']}, trigger: ${vars['{trigger}']})`;
78
+ }
79
+
80
+ /**
81
+ * Build adapter-specific sendTo payload.
82
+ *
83
+ * @param {string} adapterType Adapter name without instance number
84
+ * @param {string} message Notification text
85
+ * @param {string} target Optional recipient (user/chatId/email)
86
+ * @returns {object} Payload for sendTo
87
+ */
88
+ buildPayload(adapterType, message, target) {
89
+ const t = (target || '').trim();
90
+
91
+ switch (adapterType) {
92
+ case ADAPTER_TELEGRAM:
93
+ return t ? { text: message, user: t } : { text: message };
94
+
95
+ case ADAPTER_EMAIL:
96
+ return {
97
+ text: message,
98
+ subject: 'AutoDoc: Documentation generated',
99
+ ...(t ? { to: t } : {}),
100
+ };
101
+
102
+ case ADAPTER_PUSHOVER:
103
+ return {
104
+ message,
105
+ title: 'AutoDoc',
106
+ ...(t ? { device: t } : {}),
107
+ };
108
+
109
+ case ADAPTER_SIGNAL:
110
+ case ADAPTER_WHATSAPP:
111
+ return t ? { text: message, phone: t } : { text: message };
112
+
113
+ default:
114
+ // Generic fallback: try common fields
115
+ return {
116
+ text: message,
117
+ message,
118
+ ...(t ? { to: t, user: t } : {}),
119
+ };
120
+ }
121
+ }
122
+ }
123
+
124
+ module.exports = Notifier;