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,422 @@
1
+ /**
2
+ * Optional documentation layout: admin chapter visibility, custom Markdown sections.
3
+ * See PLAN.md → Custom Templates (Phase 5).
4
+ */
5
+
6
+ /**
7
+ * Chapters in generated Admin HTML / Markdown (admin profile). Order matches the default render sequence
8
+ * (custom before changelog, same as body builder).
9
+ */
10
+ const DEFAULT_ADMIN_CHAPTER_ORDER = [
11
+ 'manual',
12
+ 'system',
13
+ 'adapters',
14
+ 'rooms',
15
+ 'scripts',
16
+ 'schedule',
17
+ 'userdata',
18
+ 'aliases',
19
+ 'maintenance',
20
+ 'diagnosis',
21
+ 'troubleshooting',
22
+ 'custom',
23
+ 'changelog',
24
+ 'appendices',
25
+ ];
26
+
27
+ /**
28
+ * Extra chapter IDs that are valid in admin/user hidden-chapter lists but are not standalone chapters.
29
+ * 'mermaidAuto' hides only the auto-generated host topology (within the manual section).
30
+ * 'mermaid' hides only the user-defined Mermaid diagram.
31
+ */
32
+ const EXTRA_HIDDEN_CHAPTER_IDS = ['mermaidAuto'];
33
+
34
+ /** User/Family HTML + matching Markdown export (profile user). */
35
+ const USER_HTML_CHAPTER_KEYS = [
36
+ 'manual',
37
+ 'ai',
38
+ 'guestHelp',
39
+ 'atAGlance',
40
+ 'rooms',
41
+ 'scripts',
42
+ 'routines',
43
+ 'ownerPlaybook',
44
+ 'mermaid',
45
+ 'adapters',
46
+ 'custom',
47
+ 'system',
48
+ 'troubleshooting',
49
+ ];
50
+
51
+ /**
52
+ * Onboarding/Guest HTML + keys that apply to Markdown (profile onboarding).
53
+ * Order matches the original renderOnboardingHtml sequence:
54
+ * welcome (always first, not in loop) → quickstart → tips → guestHelp → stats → ai → capabilities
55
+ * → mermaid (noop, rendered in welcome) → rooms → routines → ownerPlaybook → automations
56
+ * → adapters → custom → hint → system → manual
57
+ */
58
+ const ONBOARDING_HTML_CHAPTER_KEYS = [
59
+ 'welcome',
60
+ 'quickstart',
61
+ 'tips',
62
+ 'guestHelp',
63
+ 'stats',
64
+ 'ai',
65
+ 'capabilities',
66
+ 'mermaid',
67
+ 'rooms',
68
+ 'routines',
69
+ 'ownerPlaybook',
70
+ 'automations',
71
+ 'adapters',
72
+ 'custom',
73
+ 'hint',
74
+ 'system',
75
+ 'manual',
76
+ ];
77
+
78
+ const MAX_CUSTOM_SECTIONS = 12;
79
+ const MAX_CUSTOM_BODY_CHARS = 24000;
80
+ const PROFILE_SET = new Set(['admin', 'user', 'onboarding']);
81
+
82
+ /**
83
+ * @param {unknown} config - raw adapter `this.config`
84
+ * @returns {string[]} allowed admin chapter ids to hide
85
+ */
86
+ function parseAdminHiddenChapters(config) {
87
+ return parseHiddenJsonArray(config, 'adminHiddenChaptersJson', 'adminHiddenChapters', [
88
+ ...DEFAULT_ADMIN_CHAPTER_ORDER,
89
+ ...EXTRA_HIDDEN_CHAPTER_IDS,
90
+ ]);
91
+ }
92
+
93
+ /**
94
+ * @param {unknown} config - raw adapter `this.config`
95
+ * @returns {string[]} user-profile chapter ids to hide
96
+ */
97
+ function parseUserHiddenChapters(config) {
98
+ return parseHiddenJsonArray(config, 'userHiddenChaptersJson', 'userHiddenChapters', [
99
+ ...USER_HTML_CHAPTER_KEYS,
100
+ ...EXTRA_HIDDEN_CHAPTER_IDS,
101
+ ]);
102
+ }
103
+
104
+ /**
105
+ * @param {unknown} config - raw adapter `this.config`
106
+ * @returns {string[]} onboarding chapter ids to hide
107
+ */
108
+ function parseOnboardingHiddenChapters(config) {
109
+ return parseHiddenJsonArray(
110
+ config,
111
+ 'onboardingHiddenChaptersJson',
112
+ 'onboardingHiddenChapters',
113
+ ONBOARDING_HTML_CHAPTER_KEYS,
114
+ );
115
+ }
116
+
117
+ /**
118
+ * @param {unknown} config - raw adapter `this.config`
119
+ * @param {string} jsonKey - primary JSON string field name
120
+ * @param {string} legacyKey - fallback field for migration
121
+ * @param {string[]} allowedList - whitelist of chapter id strings
122
+ * @returns {string[]} filtered hidden chapter ids
123
+ */
124
+ function parseHiddenJsonArray(config, jsonKey, legacyKey, allowedList) {
125
+ if (!config) {
126
+ return [];
127
+ }
128
+ let raw = config[jsonKey];
129
+ if (raw === undefined || raw === null) {
130
+ raw = config[legacyKey];
131
+ }
132
+ let arr = [];
133
+ if (typeof raw === 'string') {
134
+ const s = raw.trim();
135
+ if (!s) {
136
+ return [];
137
+ }
138
+ try {
139
+ arr = JSON.parse(s);
140
+ } catch {
141
+ return [];
142
+ }
143
+ } else if (Array.isArray(raw)) {
144
+ arr = raw;
145
+ } else {
146
+ return [];
147
+ }
148
+ if (!Array.isArray(arr)) {
149
+ return [];
150
+ }
151
+ const allowed = new Set(allowedList);
152
+ return arr.map(k => String(k).trim()).filter(k => allowed.has(k));
153
+ }
154
+
155
+ /**
156
+ * @param {unknown} config - raw adapter `this.config`
157
+ * @returns {{ title: string, bodyMarkdown: string, profiles?: string[], anchorId: string }[]} custom Markdown blocks for export
158
+ */
159
+ function parseCustomDocSections(config) {
160
+ if (!config || typeof config !== 'object') {
161
+ return [];
162
+ }
163
+ const raw = Reflect.get(config, 'customDocSectionsJson');
164
+ let arr = [];
165
+ if (typeof raw === 'string') {
166
+ const s = raw.trim();
167
+ if (!s) {
168
+ return [];
169
+ }
170
+ try {
171
+ arr = JSON.parse(s);
172
+ } catch {
173
+ return [];
174
+ }
175
+ } else if (Array.isArray(raw)) {
176
+ arr = raw;
177
+ } else {
178
+ return [];
179
+ }
180
+ if (!Array.isArray(arr)) {
181
+ return [];
182
+ }
183
+ const out = [];
184
+ for (let i = 0; i < arr.length && out.length < MAX_CUSTOM_SECTIONS; i++) {
185
+ const row = arr[i];
186
+ if (!row || typeof row !== 'object') {
187
+ continue;
188
+ }
189
+ const title = String(row.title || '').trim();
190
+ const body = String(row.body || row.bodyMarkdown || '').trim();
191
+ if (!title || !body) {
192
+ continue;
193
+ }
194
+ let profiles;
195
+ if (Array.isArray(row.profiles) && row.profiles.length) {
196
+ profiles = row.profiles.map(p => String(p).trim().toLowerCase()).filter(p => PROFILE_SET.has(p));
197
+ if (profiles.length === 0) {
198
+ continue;
199
+ }
200
+ }
201
+ const bodyMarkdown = body.length > MAX_CUSTOM_BODY_CHARS ? body.slice(0, MAX_CUSTOM_BODY_CHARS) : body;
202
+ const anchorId = `custom-doc-${out.length}`;
203
+ out.push({ title, bodyMarkdown, profiles, anchorId });
204
+ }
205
+ return out;
206
+ }
207
+
208
+ /**
209
+ * @param {string} [raw] - `htmlColorScheme` config value
210
+ * @returns {'auto'|'light'|'dark'} Normalized theme mode for HTML shell
211
+ */
212
+ function parseHtmlColorScheme(raw) {
213
+ const s = String(raw || 'auto')
214
+ .trim()
215
+ .toLowerCase();
216
+ if (s === 'light' || s === 'dark') {
217
+ return s;
218
+ }
219
+ return 'auto';
220
+ }
221
+
222
+ /**
223
+ * @param {string} [raw] - user CSS font stack
224
+ * @returns {string} sanitized `font-family` fragment or empty
225
+ */
226
+ function sanitizeFontStack(raw) {
227
+ if (raw == null || !String(raw).trim()) {
228
+ return '';
229
+ }
230
+ return String(raw)
231
+ .replace(/[{}<>]/g, '')
232
+ .trim()
233
+ .slice(0, 280);
234
+ }
235
+
236
+ /**
237
+ * @param {string} [raw] - logo URL for HTML export
238
+ * @returns {string} safe https/same-site path or empty
239
+ */
240
+ function sanitizeLogoUrl(raw) {
241
+ if (raw == null || !String(raw).trim()) {
242
+ return '';
243
+ }
244
+ const s = String(raw).trim();
245
+ if (/^\s*javascript:/i.test(s) || /^\s*data:/i.test(s)) {
246
+ return '';
247
+ }
248
+ if (/^https?:\/\//i.test(s)) {
249
+ return s;
250
+ }
251
+ if (s.startsWith('/') && !s.startsWith('//')) {
252
+ return s;
253
+ }
254
+ return '';
255
+ }
256
+
257
+ /**
258
+ * Merge custom chapter order with the default list (duplicates / unknown keys are ignored; missing keys are appended in default order).
259
+ *
260
+ * @param {string[]} defaultOrder - product default chapter sequence
261
+ * @param {string[]} userOrder - user-provided id order
262
+ * @returns {string[]} merged id list, unknown ids dropped
263
+ */
264
+ function mergeChapterOrder(defaultOrder, userOrder) {
265
+ if (!userOrder || userOrder.length === 0) {
266
+ return defaultOrder.slice();
267
+ }
268
+ const seen = new Set();
269
+ const out = [];
270
+ for (const k of userOrder) {
271
+ if (defaultOrder.includes(k) && !seen.has(k)) {
272
+ out.push(k);
273
+ seen.add(k);
274
+ }
275
+ }
276
+ for (const k of defaultOrder) {
277
+ if (!seen.has(k)) {
278
+ out.push(k);
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+
284
+ /**
285
+ * @param {unknown} config - raw adapter `this.config`
286
+ * @param {string} jsonKey - primary JSON string field
287
+ * @param {string} legacyKey - legacy field name
288
+ * @param {string[]} allowedList - valid chapter ids
289
+ * @param {string[]} defaultOrder - fallback when unset or invalid
290
+ * @returns {string[]} final chapter id order
291
+ */
292
+ function parseChapterOrderJson(config, jsonKey, legacyKey, allowedList, defaultOrder) {
293
+ if (!config) {
294
+ return defaultOrder.slice();
295
+ }
296
+ let raw = config[jsonKey];
297
+ if (raw === undefined || raw === null) {
298
+ raw = config[legacyKey];
299
+ }
300
+ let arr = [];
301
+ if (typeof raw === 'string') {
302
+ const s = raw.trim();
303
+ if (!s) {
304
+ return defaultOrder.slice();
305
+ }
306
+ try {
307
+ arr = JSON.parse(s);
308
+ } catch {
309
+ return defaultOrder.slice();
310
+ }
311
+ } else if (Array.isArray(raw)) {
312
+ arr = raw;
313
+ } else {
314
+ return defaultOrder.slice();
315
+ }
316
+ if (!Array.isArray(arr) || arr.length === 0) {
317
+ return defaultOrder.slice();
318
+ }
319
+ const allowed = new Set(allowedList);
320
+ const seen = new Set();
321
+ const user = [];
322
+ for (const x of arr) {
323
+ const k = String(x).trim();
324
+ if (allowed.has(k) && !seen.has(k)) {
325
+ user.push(k);
326
+ seen.add(k);
327
+ }
328
+ }
329
+ if (user.length === 0) {
330
+ return defaultOrder.slice();
331
+ }
332
+ return mergeChapterOrder(defaultOrder, user);
333
+ }
334
+
335
+ /**
336
+ * @param {unknown} config - raw adapter `this.config`
337
+ * @returns {string[]} admin HTML/Markdown chapter order
338
+ */
339
+ function parseAdminChapterOrder(config) {
340
+ return parseChapterOrderJson(
341
+ config,
342
+ 'adminChapterOrderJson',
343
+ 'adminChapterOrder',
344
+ DEFAULT_ADMIN_CHAPTER_ORDER,
345
+ DEFAULT_ADMIN_CHAPTER_ORDER,
346
+ );
347
+ }
348
+
349
+ /**
350
+ * @param {unknown} config - raw adapter `this.config`
351
+ * @returns {string[]} user HTML/Markdown chapter order
352
+ */
353
+ function parseUserChapterOrder(config) {
354
+ return parseChapterOrderJson(
355
+ config,
356
+ 'userChapterOrderJson',
357
+ 'userChapterOrder',
358
+ USER_HTML_CHAPTER_KEYS,
359
+ USER_HTML_CHAPTER_KEYS,
360
+ );
361
+ }
362
+
363
+ /**
364
+ * @param {unknown} config - raw adapter `this.config`
365
+ * @returns {string[]} onboarding HTML/Markdown chapter order
366
+ */
367
+ function parseOnboardingChapterOrder(config) {
368
+ return parseChapterOrderJson(
369
+ config,
370
+ 'onboardingChapterOrderJson',
371
+ 'onboardingChapterOrder',
372
+ ONBOARDING_HTML_CHAPTER_KEYS,
373
+ ONBOARDING_HTML_CHAPTER_KEYS,
374
+ );
375
+ }
376
+
377
+ const HTML_THEME_PRESET_IDS = new Set(['default', 'highContrast', 'warm', 'slate']);
378
+
379
+ /**
380
+ * @param {unknown} raw - config value (preset id)
381
+ * @returns {string} Preset id for HTML export
382
+ */
383
+ function parseHtmlThemePreset(raw) {
384
+ const s = String(raw == null ? 'default' : raw)
385
+ .trim()
386
+ .toLowerCase()
387
+ .replace(/[\s_-]+/g, '');
388
+ const map = {
389
+ default: 'default',
390
+ highcontrast: 'highContrast',
391
+ warm: 'warm',
392
+ slate: 'slate',
393
+ };
394
+ if (map[s] !== undefined) {
395
+ return map[s];
396
+ }
397
+ const rawId = String(raw == null ? '' : raw).trim();
398
+ if (HTML_THEME_PRESET_IDS.has(rawId)) {
399
+ return rawId;
400
+ }
401
+ return 'default';
402
+ }
403
+
404
+ module.exports = {
405
+ DEFAULT_ADMIN_CHAPTER_ORDER,
406
+ USER_HTML_CHAPTER_KEYS,
407
+ ONBOARDING_HTML_CHAPTER_KEYS,
408
+ parseAdminHiddenChapters,
409
+ parseUserHiddenChapters,
410
+ parseOnboardingHiddenChapters,
411
+ parseCustomDocSections,
412
+ parseHtmlColorScheme,
413
+ parseAdminChapterOrder,
414
+ parseUserChapterOrder,
415
+ parseOnboardingChapterOrder,
416
+ mergeChapterOrder,
417
+ parseHtmlThemePreset,
418
+ sanitizeFontStack,
419
+ sanitizeLogoUrl,
420
+ MAX_CUSTOM_SECTIONS,
421
+ MAX_CUSTOM_BODY_CHARS,
422
+ };