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.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/admin/autodoc.png +0 -0
- package/admin/i18n/de.json +244 -0
- package/admin/i18n/en.json +241 -0
- package/admin/i18n/es.json +229 -0
- package/admin/i18n/fr.json +235 -0
- package/admin/i18n/it.json +229 -0
- package/admin/i18n/nl.json +229 -0
- package/admin/i18n/pl.json +229 -0
- package/admin/i18n/pt.json +229 -0
- package/admin/i18n/ru.json +229 -0
- package/admin/i18n/uk.json +229 -0
- package/admin/i18n/zh-cn.json +229 -0
- package/admin/jsonConfig.json +1490 -0
- package/io-package.json +253 -0
- package/lib/adapter-config.d.ts +19 -0
- package/lib/aiEnhancer.js +2114 -0
- package/lib/autoHostTopologyMermaid.js +195 -0
- package/lib/dependencyAnalyzer.js +83 -0
- package/lib/diagnosisSnapshot.js +32 -0
- package/lib/discovery.js +953 -0
- package/lib/docTemplateConfig.js +422 -0
- package/lib/documentModel.js +640 -0
- package/lib/forumCard.js +70 -0
- package/lib/guestHelpContent.js +93 -0
- package/lib/guestScriptPrivacy.js +14 -0
- package/lib/hostDisplay.js +19 -0
- package/lib/htmlRenderer.js +4108 -0
- package/lib/htmlThemePresets.js +79 -0
- package/lib/htmlToPdf.js +99 -0
- package/lib/i18n.js +1309 -0
- package/lib/markdownRenderer.js +2025 -0
- package/lib/mermaidAutodocPalette.js +165 -0
- package/lib/mermaidServerSvg.js +252 -0
- package/lib/notifier.js +124 -0
- package/lib/quickStartGuide.js +180 -0
- package/lib/roleMapper.js +90 -0
- package/lib/scriptGroups.js +78 -0
- package/lib/versionTracker.js +312 -0
- package/main.js +1368 -0
- 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(/"/g, '"')
|
|
45
|
+
.replace(/>/g, '>')
|
|
46
|
+
.replace(/</g, '<')
|
|
47
|
+
.replace(/&/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
|
+
};
|
package/lib/notifier.js
ADDED
|
@@ -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;
|