@iobroker/js-controller-adapter 7.2.3 → 8.0.0-alpha.1-20260921-e5941ca8a
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/build/cjs/index.d.ts +0 -1
- package/build/cjs/index.js +5 -8
- package/build/cjs/index.js.map +2 -2
- package/build/cjs/lib/_Types.d.ts +394 -63
- package/build/cjs/lib/adapter/adapter.d.ts +1818 -177
- package/build/cjs/lib/adapter/adapter.js +715 -1035
- package/build/cjs/lib/adapter/adapter.js.map +2 -2
- package/build/cjs/lib/adapter/asyncAdapter.d.ts +124 -0
- package/build/cjs/lib/adapter/asyncAdapter.js +184 -0
- package/build/cjs/lib/adapter/asyncAdapter.js.map +7 -0
- package/build/cjs/lib/adapter/context.d.ts +34 -0
- package/build/cjs/lib/adapter/context.js +17 -0
- package/build/cjs/lib/adapter/context.js.map +7 -0
- package/build/cjs/lib/adapter/defaultObjs.d.ts +5 -0
- package/build/cjs/lib/adapter/defaultObjs.js +2 -0
- package/build/cjs/lib/adapter/defaultObjs.js.map +2 -2
- package/build/cjs/lib/adapter/log.d.ts +25 -0
- package/build/cjs/lib/adapter/log.js +29 -0
- package/build/cjs/lib/adapter/log.js.map +2 -2
- package/build/cjs/lib/adapter/managers/AdapterContextBase.d.ts +49 -0
- package/build/cjs/lib/adapter/managers/AdapterContextBase.js +98 -0
- package/build/cjs/lib/adapter/managers/AdapterContextBase.js.map +7 -0
- package/build/cjs/lib/adapter/managers/AliasManager.d.ts +103 -0
- package/build/cjs/lib/adapter/managers/AliasManager.js +293 -0
- package/build/cjs/lib/adapter/managers/AliasManager.js.map +7 -0
- package/build/cjs/lib/adapter/managers/CertificateManager.d.ts +50 -0
- package/build/cjs/lib/adapter/managers/CertificateManager.js +174 -0
- package/build/cjs/lib/adapter/managers/CertificateManager.js.map +7 -0
- package/build/cjs/lib/adapter/managers/MessagingManager.d.ts +58 -0
- package/build/cjs/lib/adapter/managers/MessagingManager.js +289 -0
- package/build/cjs/lib/adapter/managers/MessagingManager.js.map +7 -0
- package/build/cjs/lib/adapter/managers/SubscriptionManager.d.ts +107 -0
- package/build/cjs/lib/adapter/managers/SubscriptionManager.js +349 -0
- package/build/cjs/lib/adapter/managers/SubscriptionManager.js.map +7 -0
- package/build/cjs/lib/adapter/userInterfaceMessagingController.d.ts +10 -0
- package/build/cjs/lib/adapter/userInterfaceMessagingController.js +9 -2
- package/build/cjs/lib/adapter/userInterfaceMessagingController.js.map +2 -2
- package/build/cjs/lib/adapter/utils.js +15 -1
- package/build/cjs/lib/adapter/utils.js.map +2 -2
- package/build/cjs/lib/adapter/validator.d.ts +10 -5
- package/build/cjs/lib/adapter/validator.js +6 -5
- package/build/cjs/lib/adapter/validator.js.map +2 -2
- package/build/esm/index.d.ts +0 -1
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index.js +0 -1
- package/build/esm/index.js.map +1 -1
- package/build/esm/lib/_Types.d.ts +394 -63
- package/build/esm/lib/_Types.d.ts.map +1 -1
- package/build/esm/lib/adapter/adapter.d.ts +1818 -177
- package/build/esm/lib/adapter/adapter.d.ts.map +1 -1
- package/build/esm/lib/adapter/adapter.js +827 -1215
- package/build/esm/lib/adapter/adapter.js.map +1 -1
- package/build/esm/lib/adapter/asyncAdapter.d.ts +124 -0
- package/build/esm/lib/adapter/asyncAdapter.d.ts.map +1 -0
- package/build/esm/lib/adapter/asyncAdapter.js +157 -0
- package/build/esm/lib/adapter/asyncAdapter.js.map +1 -0
- package/build/esm/lib/adapter/context.d.ts +34 -0
- package/build/esm/lib/adapter/context.d.ts.map +1 -0
- package/build/esm/lib/adapter/context.js +2 -0
- package/build/esm/lib/adapter/context.js.map +1 -0
- package/build/esm/lib/adapter/defaultObjs.d.ts +5 -0
- package/build/esm/lib/adapter/defaultObjs.d.ts.map +1 -1
- package/build/esm/lib/adapter/defaultObjs.js +5 -0
- package/build/esm/lib/adapter/defaultObjs.js.map +1 -1
- package/build/esm/lib/adapter/log.d.ts +25 -0
- package/build/esm/lib/adapter/log.d.ts.map +1 -1
- package/build/esm/lib/adapter/log.js +25 -0
- package/build/esm/lib/adapter/log.js.map +1 -1
- package/build/esm/lib/adapter/managers/AdapterContextBase.d.ts +49 -0
- package/build/esm/lib/adapter/managers/AdapterContextBase.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/AdapterContextBase.js +77 -0
- package/build/esm/lib/adapter/managers/AdapterContextBase.js.map +1 -0
- package/build/esm/lib/adapter/managers/AliasManager.d.ts +103 -0
- package/build/esm/lib/adapter/managers/AliasManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/AliasManager.js +269 -0
- package/build/esm/lib/adapter/managers/AliasManager.js.map +1 -0
- package/build/esm/lib/adapter/managers/CertificateManager.d.ts +50 -0
- package/build/esm/lib/adapter/managers/CertificateManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/CertificateManager.js +142 -0
- package/build/esm/lib/adapter/managers/CertificateManager.js.map +1 -0
- package/build/esm/lib/adapter/managers/MessagingManager.d.ts +58 -0
- package/build/esm/lib/adapter/managers/MessagingManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/MessagingManager.js +267 -0
- package/build/esm/lib/adapter/managers/MessagingManager.js.map +1 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.d.ts +107 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.d.ts.map +1 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.js +374 -0
- package/build/esm/lib/adapter/managers/SubscriptionManager.js.map +1 -0
- package/build/esm/lib/adapter/userInterfaceMessagingController.d.ts +10 -0
- package/build/esm/lib/adapter/userInterfaceMessagingController.d.ts.map +1 -1
- package/build/esm/lib/adapter/userInterfaceMessagingController.js +8 -2
- package/build/esm/lib/adapter/userInterfaceMessagingController.js.map +1 -1
- package/build/esm/lib/adapter/utils.d.ts.map +1 -1
- package/build/esm/lib/adapter/utils.js +1 -1
- package/build/esm/lib/adapter/utils.js.map +1 -1
- package/build/esm/lib/adapter/validator.d.ts +10 -5
- package/build/esm/lib/adapter/validator.d.ts.map +1 -1
- package/build/esm/lib/adapter/validator.js +8 -5
- package/build/esm/lib/adapter/validator.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +25 -20
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var CertificateManager_exports = {};
|
|
31
|
+
__export(CertificateManager_exports, {
|
|
32
|
+
CERTIFICATES_OBJECT_ID: () => CERTIFICATES_OBJECT_ID,
|
|
33
|
+
CertificateManager: () => CertificateManager
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(CertificateManager_exports);
|
|
36
|
+
var import_fs_extra = __toESM(require("fs-extra"), 1);
|
|
37
|
+
var import_js_controller_common = require("@iobroker/js-controller-common");
|
|
38
|
+
var import_AdapterContextBase = require("../../../lib/adapter/managers/AdapterContextBase.js");
|
|
39
|
+
const CERTIFICATES_OBJECT_ID = "system.certificates";
|
|
40
|
+
class CertificateManager extends import_AdapterContextBase.AdapterContextBase {
|
|
41
|
+
static {
|
|
42
|
+
__name(this, "CertificateManager");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Raw (unresolved) values of the certificates handed out by the last {@link getCertificates} call,
|
|
46
|
+
* keyed by certificate name. Used to tell a relevant change of `system.certificates` from an
|
|
47
|
+
* unrelated one. `undefined` until certificates have been requested at least once.
|
|
48
|
+
*/
|
|
49
|
+
#usedCertificates;
|
|
50
|
+
/**
|
|
51
|
+
* @param ctx Shared adapter context providing live runtime state
|
|
52
|
+
*/
|
|
53
|
+
constructor(ctx) {
|
|
54
|
+
super(ctx);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Tells whether a new version of the `system.certificates` object actually changes one of the
|
|
58
|
+
* certificates handed out by the last {@link getCertificates} call. Unrelated certificates being
|
|
59
|
+
* added, changed or removed are ignored, so that editing some other adapter's certificate does
|
|
60
|
+
* not restart this one.
|
|
61
|
+
*
|
|
62
|
+
* Compares the raw (unresolved) values: for a file-backed certificate that is the path, so
|
|
63
|
+
* repointing it to a different file counts as a change, while a change of the file content is
|
|
64
|
+
* left to the file watcher.
|
|
65
|
+
*
|
|
66
|
+
* @param obj the new `system.certificates` object, or null/undefined if it was deleted
|
|
67
|
+
*/
|
|
68
|
+
hasRelevantChange(obj) {
|
|
69
|
+
const usedCertificates = this.#usedCertificates;
|
|
70
|
+
if (!usedCertificates) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const certificates = obj?.native?.certificates;
|
|
74
|
+
for (const [name, value] of usedCertificates) {
|
|
75
|
+
if (certificates?.[name] !== value) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Forgets the certificates handed out by the last {@link getCertificates} call, so a subsequent
|
|
83
|
+
* change of `system.certificates` is no longer treated as relevant by {@link hasRelevantChange}.
|
|
84
|
+
* Used when the adapter stops watching its certificates.
|
|
85
|
+
*/
|
|
86
|
+
stopWatching() {
|
|
87
|
+
this.#usedCertificates = void 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Loads the named certificates from `system.certificates`. File-backed values (short strings that
|
|
91
|
+
* are existing paths) are read from disk and their paths collected in `certFilePaths` so the caller
|
|
92
|
+
* can watch them.
|
|
93
|
+
*
|
|
94
|
+
* @param names public/private/chained certificate names
|
|
95
|
+
* @param names.publicName Name of the public certificate
|
|
96
|
+
* @param names.privateName Name of the private key
|
|
97
|
+
* @param names.chainedName Name of the chained certificate
|
|
98
|
+
* @throws {Error} `ERROR_DB_CLOSED` when the objects database is not connected
|
|
99
|
+
* @throws {Error} `ERROR_NOT_FOUND` when the requested certificates are not configured
|
|
100
|
+
*/
|
|
101
|
+
async getCertificates(names) {
|
|
102
|
+
const { publicName, privateName, chainedName } = names;
|
|
103
|
+
const objects = this.objects;
|
|
104
|
+
let obj;
|
|
105
|
+
try {
|
|
106
|
+
obj = await objects.getObject(CERTIFICATES_OBJECT_ID);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
if (!obj?.native.certificates || !publicName || !privateName || !obj.native.certificates[publicName] || !obj.native.certificates[privateName] || chainedName && !obj.native.certificates[chainedName]) {
|
|
110
|
+
this.logger.error(`${this.namespaceLog} Cannot configure secure web server, because no certificates found: ${publicName}, ${privateName}, ${chainedName}`);
|
|
111
|
+
if (publicName === "defaultPublic" || privateName === "defaultPrivate") {
|
|
112
|
+
this.logger.info(`${this.namespaceLog} Default certificates seem to be configured but missing. You can execute "iobroker cert create" in your shell to create these.`);
|
|
113
|
+
}
|
|
114
|
+
throw new Error(import_js_controller_common.tools.ERRORS.ERROR_NOT_FOUND);
|
|
115
|
+
}
|
|
116
|
+
const certFilePaths = new Array();
|
|
117
|
+
let ca;
|
|
118
|
+
if (chainedName) {
|
|
119
|
+
const chained = this.#resolveCert(obj.native.certificates[chainedName], certFilePaths).split("-----END CERTIFICATE-----\r\n");
|
|
120
|
+
if (chained.join("").length >= 512) {
|
|
121
|
+
const caArr = new Array();
|
|
122
|
+
for (const cert of chained) {
|
|
123
|
+
if (cert.replace(/(\r\n|\r|\n)/g, "").trim()) {
|
|
124
|
+
caArr.push(`${cert}-----END CERTIFICATE-----\r
|
|
125
|
+
`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
ca = caArr.join("");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const certs = {
|
|
132
|
+
key: this.#resolveCert(obj.native.certificates[privateName], certFilePaths),
|
|
133
|
+
cert: this.#resolveCert(obj.native.certificates[publicName], certFilePaths),
|
|
134
|
+
ca
|
|
135
|
+
};
|
|
136
|
+
this.#usedCertificates = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const name of [publicName, privateName, chainedName]) {
|
|
138
|
+
if (name) {
|
|
139
|
+
this.#usedCertificates.set(name, obj.native.certificates[name]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { certs, useLetsEncrypt: obj.native.letsEncrypt, certFilePaths };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Returns the certificate content. When `value` is a short string that is an existing file path,
|
|
146
|
+
* reads the file, records the path in `certFilePaths` (deduped), and returns the file content.
|
|
147
|
+
* Otherwise returns `value` unchanged.
|
|
148
|
+
*
|
|
149
|
+
* @param value inline certificate or a path to one
|
|
150
|
+
* @param certFilePaths accumulator for file-backed paths (mutated)
|
|
151
|
+
*/
|
|
152
|
+
#resolveCert(value, certFilePaths) {
|
|
153
|
+
if (typeof value === "string") {
|
|
154
|
+
try {
|
|
155
|
+
if (value.length < 1024 && import_fs_extra.default.existsSync(value)) {
|
|
156
|
+
const content = import_fs_extra.default.readFileSync(value, "utf8");
|
|
157
|
+
if (!certFilePaths.includes(value)) {
|
|
158
|
+
certFilePaths.push(value);
|
|
159
|
+
}
|
|
160
|
+
return content;
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
this.logger.error(`${this.namespaceLog} Could not read certificate from file ${value}: ${e.message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
170
|
+
0 && (module.exports = {
|
|
171
|
+
CERTIFICATES_OBJECT_ID,
|
|
172
|
+
CertificateManager
|
|
173
|
+
});
|
|
174
|
+
//# sourceMappingURL=CertificateManager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/lib/adapter/managers/CertificateManager.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from 'fs-extra';\nimport { tools } from '@iobroker/js-controller-common';\nimport type { InternalGetCertificatesResult } from '../../_Types.js';\nimport type { AdapterContext } from '@/lib/adapter/context.js';\nimport { AdapterContextBase } from '@/lib/adapter/managers/AdapterContextBase.js';\n\n/** ID of the object holding all configured certificates */\nexport const CERTIFICATES_OBJECT_ID = 'system.certificates';\n\n/** Fetches SSL certificates from `system.certificates`, resolving file-backed values to their contents. */\nexport class CertificateManager extends AdapterContextBase {\n /**\n * Raw (unresolved) values of the certificates handed out by the last {@link getCertificates} call,\n * keyed by certificate name. Used to tell a relevant change of `system.certificates` from an\n * unrelated one. `undefined` until certificates have been requested at least once.\n */\n #usedCertificates?: Map<string, string>;\n\n /**\n * @param ctx Shared adapter context providing live runtime state\n */\n constructor(ctx: AdapterContext) {\n super(ctx);\n }\n\n /**\n * Tells whether a new version of the `system.certificates` object actually changes one of the\n * certificates handed out by the last {@link getCertificates} call. Unrelated certificates being\n * added, changed or removed are ignored, so that editing some other adapter's certificate does\n * not restart this one.\n *\n * Compares the raw (unresolved) values: for a file-backed certificate that is the path, so\n * repointing it to a different file counts as a change, while a change of the file content is\n * left to the file watcher.\n *\n * @param obj the new `system.certificates` object, or null/undefined if it was deleted\n */\n hasRelevantChange(obj: ioBroker.OtherObject | null | undefined): boolean {\n const usedCertificates = this.#usedCertificates;\n if (!usedCertificates) {\n // certificates were never requested, so nothing can have changed for us\n return false;\n }\n\n const certificates: Record<string, string> | undefined = obj?.native?.certificates;\n\n for (const [name, value] of usedCertificates) {\n if (certificates?.[name] !== value) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Forgets the certificates handed out by the last {@link getCertificates} call, so a subsequent\n * change of `system.certificates` is no longer treated as relevant by {@link hasRelevantChange}.\n * Used when the adapter stops watching its certificates.\n */\n stopWatching(): void {\n this.#usedCertificates = undefined;\n }\n\n /**\n * Loads the named certificates from `system.certificates`. File-backed values (short strings that\n * are existing paths) are read from disk and their paths collected in `certFilePaths` so the caller\n * can watch them.\n *\n * @param names public/private/chained certificate names\n * @param names.publicName Name of the public certificate\n * @param names.privateName Name of the private key\n * @param names.chainedName Name of the chained certificate\n * @throws {Error} `ERROR_DB_CLOSED` when the objects database is not connected\n * @throws {Error} `ERROR_NOT_FOUND` when the requested certificates are not configured\n */\n async getCertificates(names: {\n publicName?: string;\n privateName?: string;\n chainedName?: string;\n }): Promise<InternalGetCertificatesResult> {\n const { publicName, privateName, chainedName } = names;\n const objects = this.objects;\n\n let obj: ioBroker.OtherObject | undefined | null;\n try {\n obj = await objects.getObject(CERTIFICATES_OBJECT_ID);\n } catch {\n // ignore\n }\n\n if (\n !obj?.native.certificates ||\n !publicName ||\n !privateName ||\n !obj.native.certificates[publicName] ||\n !obj.native.certificates[privateName] ||\n (chainedName && !obj.native.certificates[chainedName])\n ) {\n this.logger.error(\n `${this.namespaceLog} Cannot configure secure web server, because no certificates found: ${publicName}, ${privateName}, ${chainedName}`,\n );\n if (publicName === 'defaultPublic' || privateName === 'defaultPrivate') {\n this.logger.info(\n `${this.namespaceLog} Default certificates seem to be configured but missing. You can execute \"iobroker cert create\" in your shell to create these.`,\n );\n }\n throw new Error(tools.ERRORS.ERROR_NOT_FOUND);\n }\n\n const certFilePaths = new Array<string>();\n\n let ca: string | undefined;\n if (chainedName) {\n const chained = this.#resolveCert(obj.native.certificates[chainedName], certFilePaths).split(\n '-----END CERTIFICATE-----\\r\\n',\n );\n if (chained.join('').length >= 512) {\n const caArr = new Array<string>();\n for (const cert of chained) {\n if (cert.replace(/(\\r\\n|\\r|\\n)/g, '').trim()) {\n caArr.push(`${cert}-----END CERTIFICATE-----\\r\\n`);\n }\n }\n ca = caArr.join('');\n }\n }\n\n const certs: ioBroker.Certificates = {\n key: this.#resolveCert(obj.native.certificates[privateName], certFilePaths),\n cert: this.#resolveCert(obj.native.certificates[publicName], certFilePaths),\n ca,\n };\n\n // remember the raw values, so a change of `system.certificates` can be told from an unrelated one\n this.#usedCertificates = new Map<string, string>();\n for (const name of [publicName, privateName, chainedName]) {\n if (name) {\n this.#usedCertificates.set(name, obj.native.certificates[name]);\n }\n }\n\n return { certs, useLetsEncrypt: obj.native.letsEncrypt, certFilePaths };\n }\n\n /**\n * Returns the certificate content. When `value` is a short string that is an existing file path,\n * reads the file, records the path in `certFilePaths` (deduped), and returns the file content.\n * Otherwise returns `value` unchanged.\n *\n * @param value inline certificate or a path to one\n * @param certFilePaths accumulator for file-backed paths (mutated)\n */\n #resolveCert(value: string, certFilePaths: string[]): string {\n if (typeof value === 'string') {\n try {\n if (value.length < 1024 && fs.existsSync(value)) {\n const content = fs.readFileSync(value, 'utf8');\n if (!certFilePaths.includes(value)) {\n certFilePaths.push(value);\n }\n return content;\n }\n } catch (e: any) {\n this.logger.error(`${this.namespaceLog} Could not read certificate from file ${value}: ${e.message}`);\n }\n }\n return value;\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;AAAA,sBAAe;AACf,kCAAsB;AAGtB,gCAAmC;AAG5B,MAAM,yBAAyB;AAGhC,MAAO,2BAA2B,6CAAkB;EAV1D,OAU0D;;;;;;;;EAMtD;;;;EAKA,YAAY,KAAmB;AAC3B,UAAM,GAAG;EACb;;;;;;;;;;;;;EAcA,kBAAkB,KAA4C;AAC1D,UAAM,mBAAmB,KAAK;AAC9B,QAAI,CAAC,kBAAkB;AAEnB,aAAO;IACX;AAEA,UAAM,eAAmD,KAAK,QAAQ;AAEtE,eAAW,CAAC,MAAM,KAAK,KAAK,kBAAkB;AAC1C,UAAI,eAAe,IAAI,MAAM,OAAO;AAChC,eAAO;MACX;IACJ;AAEA,WAAO;EACX;;;;;;EAOA,eAAY;AACR,SAAK,oBAAoB;EAC7B;;;;;;;;;;;;;EAcA,MAAM,gBAAgB,OAIrB;AACG,UAAM,EAAE,YAAY,aAAa,YAAW,IAAK;AACjD,UAAM,UAAU,KAAK;AAErB,QAAI;AACJ,QAAI;AACA,YAAM,MAAM,QAAQ,UAAU,sBAAsB;IACxD,QAAQ;IAER;AAEA,QACI,CAAC,KAAK,OAAO,gBACb,CAAC,cACD,CAAC,eACD,CAAC,IAAI,OAAO,aAAa,UAAU,KACnC,CAAC,IAAI,OAAO,aAAa,WAAW,KACnC,eAAe,CAAC,IAAI,OAAO,aAAa,WAAW,GACtD;AACE,WAAK,OAAO,MACR,GAAG,KAAK,YAAY,uEAAuE,UAAU,KAAK,WAAW,KAAK,WAAW,EAAE;AAE3I,UAAI,eAAe,mBAAmB,gBAAgB,kBAAkB;AACpE,aAAK,OAAO,KACR,GAAG,KAAK,YAAY,gIAAgI;MAE5J;AACA,YAAM,IAAI,MAAM,kCAAM,OAAO,eAAe;IAChD;AAEA,UAAM,gBAAgB,IAAI,MAAK;AAE/B,QAAI;AACJ,QAAI,aAAa;AACb,YAAM,UAAU,KAAK,aAAa,IAAI,OAAO,aAAa,WAAW,GAAG,aAAa,EAAE,MACnF,+BAA+B;AAEnC,UAAI,QAAQ,KAAK,EAAE,EAAE,UAAU,KAAK;AAChC,cAAM,QAAQ,IAAI,MAAK;AACvB,mBAAW,QAAQ,SAAS;AACxB,cAAI,KAAK,QAAQ,iBAAiB,EAAE,EAAE,KAAI,GAAI;AAC1C,kBAAM,KAAK,GAAG,IAAI;CAA+B;UACrD;QACJ;AACA,aAAK,MAAM,KAAK,EAAE;MACtB;IACJ;AAEA,UAAM,QAA+B;MACjC,KAAK,KAAK,aAAa,IAAI,OAAO,aAAa,WAAW,GAAG,aAAa;MAC1E,MAAM,KAAK,aAAa,IAAI,OAAO,aAAa,UAAU,GAAG,aAAa;MAC1E;;AAIJ,SAAK,oBAAoB,oBAAI,IAAG;AAChC,eAAW,QAAQ,CAAC,YAAY,aAAa,WAAW,GAAG;AACvD,UAAI,MAAM;AACN,aAAK,kBAAkB,IAAI,MAAM,IAAI,OAAO,aAAa,IAAI,CAAC;MAClE;IACJ;AAEA,WAAO,EAAE,OAAO,gBAAgB,IAAI,OAAO,aAAa,cAAa;EACzE;;;;;;;;;EAUA,aAAa,OAAe,eAAuB;AAC/C,QAAI,OAAO,UAAU,UAAU;AAC3B,UAAI;AACA,YAAI,MAAM,SAAS,QAAQ,gBAAAA,QAAG,WAAW,KAAK,GAAG;AAC7C,gBAAM,UAAU,gBAAAA,QAAG,aAAa,OAAO,MAAM;AAC7C,cAAI,CAAC,cAAc,SAAS,KAAK,GAAG;AAChC,0BAAc,KAAK,KAAK;UAC5B;AACA,iBAAO;QACX;MACJ,SAAS,GAAQ;AACb,aAAK,OAAO,MAAM,GAAG,KAAK,YAAY,yCAAyC,KAAK,KAAK,EAAE,OAAO,EAAE;MACxG;IACJ;AACA,WAAO;EACX;;",
|
|
6
|
+
"names": ["fs"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { InternalSendToHostOptions, InternalSendToOptions, NotificationOptions, SendToUserInterfaceClientOptions } from '../../_Types.js';
|
|
2
|
+
import type { AdapterContext } from '../../../lib/adapter/context.js';
|
|
3
|
+
import { AdapterContextBase } from '../../../lib/adapter/managers/AdapterContextBase.js';
|
|
4
|
+
/** Owns the adapter's outbound messaging and the pending-reply registry. */
|
|
5
|
+
export declare class MessagingManager extends AdapterContextBase {
|
|
6
|
+
#private;
|
|
7
|
+
/**
|
|
8
|
+
* @param ctx Shared adapter context providing live runtime state
|
|
9
|
+
*/
|
|
10
|
+
constructor(ctx: AdapterContext);
|
|
11
|
+
/**
|
|
12
|
+
* Sends a message to another adapter instance.
|
|
13
|
+
*
|
|
14
|
+
* When `opts.expectReply` is true and the target is a specific instance (not broadcast),
|
|
15
|
+
* the returned promise resolves with the reply message when it arrives, or rejects with
|
|
16
|
+
* `Error('Timeout exceeded')` if `opts.options.timeout` elapses.
|
|
17
|
+
*
|
|
18
|
+
* @param opts Normalized send options
|
|
19
|
+
*/
|
|
20
|
+
sendTo(opts: InternalSendToOptions): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Sends a message to a host, or broadcasts to all hosts when `hostName` is `null`.
|
|
23
|
+
*
|
|
24
|
+
* When `opts.expectReply` is true and a specific host is targeted, the returned promise
|
|
25
|
+
* resolves with the reply message when it arrives, or rejects with `Error('Timeout exceeded')`
|
|
26
|
+
* if `opts.options.timeout` elapses. Broadcasts (hostName === null) resolve void.
|
|
27
|
+
*
|
|
28
|
+
* @param opts Normalized send options
|
|
29
|
+
*/
|
|
30
|
+
sendToHost(opts: InternalSendToHostOptions): Promise<any>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a pending reply promise for an acked messagebox message.
|
|
33
|
+
* Returns true if a pending entry was found and consumed.
|
|
34
|
+
*
|
|
35
|
+
* @param obj incoming message object from the messagebox
|
|
36
|
+
*/
|
|
37
|
+
resolveCallback(obj: ioBroker.Message): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Rejects all pending reply promises and clears their timers (used on stop).
|
|
40
|
+
*/
|
|
41
|
+
clearPendingCallbacks(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Sends a message to a single UI client, or broadcasts to all connected UI clients when `clientId` is omitted.
|
|
44
|
+
*
|
|
45
|
+
* @param options clientId and data options
|
|
46
|
+
*/
|
|
47
|
+
sendToUI(options: SendToUserInterfaceClientOptions): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Sends an addNotification command to the host of this adapter instance.
|
|
50
|
+
*
|
|
51
|
+
* @param scope notification scope
|
|
52
|
+
* @param category notification category, or `null` to match by scope regex
|
|
53
|
+
* @param message notification message
|
|
54
|
+
* @param options additional notification options
|
|
55
|
+
*/
|
|
56
|
+
registerNotification(scope: string, category: string | null, message: string, options?: NotificationOptions): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=MessagingManager.d.ts.map
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var MessagingManager_exports = {};
|
|
21
|
+
__export(MessagingManager_exports, {
|
|
22
|
+
MessagingManager: () => MessagingManager
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(MessagingManager_exports);
|
|
25
|
+
var import_AdapterContextBase = require("../../../lib/adapter/managers/AdapterContextBase.js");
|
|
26
|
+
var import_utils = require("../../../lib/adapter/utils.js");
|
|
27
|
+
class MessagingManager extends import_AdapterContextBase.AdapterContextBase {
|
|
28
|
+
static {
|
|
29
|
+
__name(this, "MessagingManager");
|
|
30
|
+
}
|
|
31
|
+
#pending = /* @__PURE__ */ new Map();
|
|
32
|
+
#callbackId = 1;
|
|
33
|
+
#mboxSubscribed = false;
|
|
34
|
+
/**
|
|
35
|
+
* @param ctx Shared adapter context providing live runtime state
|
|
36
|
+
*/
|
|
37
|
+
constructor(ctx) {
|
|
38
|
+
super(ctx);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sends a message to another adapter instance.
|
|
42
|
+
*
|
|
43
|
+
* When `opts.expectReply` is true and the target is a specific instance (not broadcast),
|
|
44
|
+
* the returned promise resolves with the reply message when it arrives, or rejects with
|
|
45
|
+
* `Error('Timeout exceeded')` if `opts.options.timeout` elapses.
|
|
46
|
+
*
|
|
47
|
+
* @param opts Normalized send options
|
|
48
|
+
*/
|
|
49
|
+
async sendTo(opts) {
|
|
50
|
+
const { command, message, callback, options, expectReply } = opts;
|
|
51
|
+
let { instanceName } = opts;
|
|
52
|
+
if (!instanceName) {
|
|
53
|
+
throw new Error("No instanceName provided or not a string");
|
|
54
|
+
}
|
|
55
|
+
if (!instanceName.startsWith("system.adapter.")) {
|
|
56
|
+
instanceName = `system.adapter.${instanceName}`;
|
|
57
|
+
}
|
|
58
|
+
const obj = {
|
|
59
|
+
command,
|
|
60
|
+
message,
|
|
61
|
+
from: `system.adapter.${this.namespace}`
|
|
62
|
+
};
|
|
63
|
+
const states = this.states;
|
|
64
|
+
if (typeof message !== "object") {
|
|
65
|
+
this.logger.silly(`${this.namespaceLog} sendTo "${command}" to ${instanceName} from system.adapter.${this.namespace}: ${message}`);
|
|
66
|
+
} else {
|
|
67
|
+
this.logger.silly(`${this.namespaceLog} sendTo "${command}" to ${instanceName} from system.adapter.${this.namespace}`);
|
|
68
|
+
}
|
|
69
|
+
if (!instanceName.match(/\.[0-9]+$/)) {
|
|
70
|
+
const objects = this.objects;
|
|
71
|
+
const res = await objects.getObjectView("system", "instance", {
|
|
72
|
+
startkey: `${instanceName}.`,
|
|
73
|
+
endkey: `${instanceName}.\u9999`
|
|
74
|
+
}).catch(() => void 0);
|
|
75
|
+
if (res) {
|
|
76
|
+
for (const row of res.rows) {
|
|
77
|
+
await states.pushMessage(row.id, obj);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (expectReply) {
|
|
83
|
+
return this.#registerReply(states, instanceName, obj, options);
|
|
84
|
+
}
|
|
85
|
+
if (callback) {
|
|
86
|
+
obj.callback = { ...callback, ack: true };
|
|
87
|
+
}
|
|
88
|
+
await states.pushMessage(instanceName, obj);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Subscribes to this instance's messagebox on demand when the adapter does not advertise
|
|
92
|
+
* messagebox support, so replies to outbound messages are received. Idempotent.
|
|
93
|
+
*
|
|
94
|
+
* @param states the connected states DB client
|
|
95
|
+
*/
|
|
96
|
+
#maybeSubscribe(states) {
|
|
97
|
+
const common = this.common;
|
|
98
|
+
if (common && !(0, import_utils.isMessageboxSupported)(common) && !this.#mboxSubscribed) {
|
|
99
|
+
this.#mboxSubscribed = true;
|
|
100
|
+
states.subscribeMessage(`system.adapter.${this.namespace}`).catch((e) => this.logger.error(`${this.namespaceLog} Cannot subscribe to messages: ${e.message}`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Sets a reply callback header on `obj`, registers a pending-reply resolver keyed by its id,
|
|
105
|
+
* subscribes the messagebox on demand, pushes the message and returns a promise that resolves
|
|
106
|
+
* with the reply once `resolveCallback` is invoked for the matching id, or rejects with
|
|
107
|
+
* `Error('Timeout exceeded')` when `options.timeout` elapses.
|
|
108
|
+
*
|
|
109
|
+
* @param states the connected states DB client
|
|
110
|
+
* @param target the recipient id (instance or host) for `pushMessage`
|
|
111
|
+
* @param obj the message to send; its `callback` header is set here
|
|
112
|
+
* @param options additional send options (e.g. timeout)
|
|
113
|
+
*/
|
|
114
|
+
async #registerReply(states, target, obj, options) {
|
|
115
|
+
this.#maybeSubscribe(states);
|
|
116
|
+
const id = this.#callbackId++;
|
|
117
|
+
if (this.#callbackId >= 4294967295) {
|
|
118
|
+
this.#callbackId = 1;
|
|
119
|
+
}
|
|
120
|
+
obj.callback = {
|
|
121
|
+
message: obj.message,
|
|
122
|
+
id,
|
|
123
|
+
ack: false,
|
|
124
|
+
time: Date.now()
|
|
125
|
+
};
|
|
126
|
+
let pendingEntry;
|
|
127
|
+
const replyPromise = new Promise((resolve, reject) => {
|
|
128
|
+
let timer;
|
|
129
|
+
if (options?.timeout) {
|
|
130
|
+
timer = setTimeout(() => {
|
|
131
|
+
if (this.#pending.has(id)) {
|
|
132
|
+
this.#pending.delete(id);
|
|
133
|
+
reject(new Error("Timeout exceeded"));
|
|
134
|
+
}
|
|
135
|
+
}, options.timeout);
|
|
136
|
+
}
|
|
137
|
+
pendingEntry = { resolve, reject, timer, time: Date.now() };
|
|
138
|
+
this.#pending.set(id, pendingEntry);
|
|
139
|
+
});
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
for (const [_id, entry] of this.#pending) {
|
|
142
|
+
if (now - entry.time > 36e5) {
|
|
143
|
+
this.#pending.delete(_id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
await states.pushMessage(target, obj);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
if (pendingEntry.timer) {
|
|
150
|
+
clearTimeout(pendingEntry.timer);
|
|
151
|
+
}
|
|
152
|
+
this.#pending.delete(id);
|
|
153
|
+
throw e;
|
|
154
|
+
}
|
|
155
|
+
return replyPromise;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Sends a message to a host, or broadcasts to all hosts when `hostName` is `null`.
|
|
159
|
+
*
|
|
160
|
+
* When `opts.expectReply` is true and a specific host is targeted, the returned promise
|
|
161
|
+
* resolves with the reply message when it arrives, or rejects with `Error('Timeout exceeded')`
|
|
162
|
+
* if `opts.options.timeout` elapses. Broadcasts (hostName === null) resolve void.
|
|
163
|
+
*
|
|
164
|
+
* @param opts Normalized send options
|
|
165
|
+
*/
|
|
166
|
+
async sendToHost(opts) {
|
|
167
|
+
const { command, message, expectReply, options, callback } = opts;
|
|
168
|
+
let { hostName } = opts;
|
|
169
|
+
const obj = {
|
|
170
|
+
command,
|
|
171
|
+
message,
|
|
172
|
+
from: `system.adapter.${this.namespace}`
|
|
173
|
+
};
|
|
174
|
+
const states = this.states;
|
|
175
|
+
if (hostName && !hostName.startsWith("system.host.")) {
|
|
176
|
+
hostName = `system.host.${hostName}`;
|
|
177
|
+
}
|
|
178
|
+
if (!hostName) {
|
|
179
|
+
const objects = this.objects;
|
|
180
|
+
const res = await objects.getObjectList({ startkey: "system.host.", endkey: "system.host.\u9999" });
|
|
181
|
+
if (res?.rows.length) {
|
|
182
|
+
for (const row of res.rows) {
|
|
183
|
+
const parts = row.id.split(".");
|
|
184
|
+
if (parts.length === 3) {
|
|
185
|
+
await states.pushMessage(row.id, obj);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (expectReply) {
|
|
192
|
+
return this.#registerReply(states, hostName, obj, options);
|
|
193
|
+
}
|
|
194
|
+
if (callback) {
|
|
195
|
+
obj.callback = { ...callback, ack: true };
|
|
196
|
+
}
|
|
197
|
+
await states.pushMessage(hostName, obj);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Resolves a pending reply promise for an acked messagebox message.
|
|
201
|
+
* Returns true if a pending entry was found and consumed.
|
|
202
|
+
*
|
|
203
|
+
* @param obj incoming message object from the messagebox
|
|
204
|
+
*/
|
|
205
|
+
resolveCallback(obj) {
|
|
206
|
+
if (!obj.callback?.ack || !obj.callback.id) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
const entry = this.#pending.get(obj.callback.id);
|
|
210
|
+
if (!entry) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
if (entry.timer) {
|
|
214
|
+
clearTimeout(entry.timer);
|
|
215
|
+
}
|
|
216
|
+
this.#pending.delete(obj.callback.id);
|
|
217
|
+
entry.resolve(obj.message);
|
|
218
|
+
const now = Date.now();
|
|
219
|
+
for (const [id, pending] of this.#pending) {
|
|
220
|
+
if (now - pending.time > 36e5) {
|
|
221
|
+
this.#pending.delete(id);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Rejects all pending reply promises and clears their timers (used on stop).
|
|
228
|
+
*/
|
|
229
|
+
clearPendingCallbacks() {
|
|
230
|
+
if (this.#pending.size) {
|
|
231
|
+
const err = new Error("Adapter stopped");
|
|
232
|
+
for (const entry of this.#pending.values()) {
|
|
233
|
+
if (entry.timer) {
|
|
234
|
+
clearTimeout(entry.timer);
|
|
235
|
+
}
|
|
236
|
+
entry.reject(err);
|
|
237
|
+
}
|
|
238
|
+
this.#pending.clear();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Sends a message to a single UI client, or broadcasts to all connected UI clients when `clientId` is omitted.
|
|
243
|
+
*
|
|
244
|
+
* @param options clientId and data options
|
|
245
|
+
*/
|
|
246
|
+
sendToUI(options) {
|
|
247
|
+
const states = this.states;
|
|
248
|
+
const { clientId, data } = options;
|
|
249
|
+
if (clientId === void 0) {
|
|
250
|
+
return this.uiMessagingController.sendToAllClients({
|
|
251
|
+
data,
|
|
252
|
+
states
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return this.uiMessagingController.sendToClient({
|
|
256
|
+
clientId,
|
|
257
|
+
data,
|
|
258
|
+
states
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Sends an addNotification command to the host of this adapter instance.
|
|
263
|
+
*
|
|
264
|
+
* @param scope notification scope
|
|
265
|
+
* @param category notification category, or `null` to match by scope regex
|
|
266
|
+
* @param message notification message
|
|
267
|
+
* @param options additional notification options
|
|
268
|
+
*/
|
|
269
|
+
async registerNotification(scope, category, message, options) {
|
|
270
|
+
const states = this.states;
|
|
271
|
+
const obj = {
|
|
272
|
+
command: "addNotification",
|
|
273
|
+
message: {
|
|
274
|
+
scope,
|
|
275
|
+
category,
|
|
276
|
+
message,
|
|
277
|
+
instance: this.namespace,
|
|
278
|
+
contextData: options?.contextData
|
|
279
|
+
},
|
|
280
|
+
from: `system.adapter.${this.namespace}`
|
|
281
|
+
};
|
|
282
|
+
await states.pushMessage(`system.host.${this.host}`, obj);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
286
|
+
0 && (module.exports = {
|
|
287
|
+
MessagingManager
|
|
288
|
+
});
|
|
289
|
+
//# sourceMappingURL=MessagingManager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/lib/adapter/managers/MessagingManager.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Client as StatesInRedisClient } from '@iobroker/db-states-redis';\nimport type {\n InternalSendToHostOptions,\n InternalSendToOptions,\n NotificationOptions,\n SendToOptions,\n SendToUserInterfaceClientOptions,\n} from '../../_Types.js';\nimport type { AdapterContext } from '@/lib/adapter/context.js';\nimport { AdapterContextBase } from '@/lib/adapter/managers/AdapterContextBase.js';\nimport { isMessageboxSupported } from '@/lib/adapter/utils.js';\n\n/** Entry in the pending-reply registry for sendTo calls with expectReply=true */\ninterface PendingReply {\n resolve: (msg: any) => void;\n reject: (e: Error) => void;\n timer?: NodeJS.Timeout;\n time: number;\n}\n\n/** Owns the adapter's outbound messaging and the pending-reply registry. */\nexport class MessagingManager extends AdapterContextBase {\n readonly #pending = new Map<number, PendingReply>();\n #callbackId = 1;\n #mboxSubscribed = false;\n\n /**\n * @param ctx Shared adapter context providing live runtime state\n */\n constructor(ctx: AdapterContext) {\n super(ctx);\n }\n\n /**\n * Sends a message to another adapter instance.\n *\n * When `opts.expectReply` is true and the target is a specific instance (not broadcast),\n * the returned promise resolves with the reply message when it arrives, or rejects with\n * `Error('Timeout exceeded')` if `opts.options.timeout` elapses.\n *\n * @param opts Normalized send options\n */\n async sendTo(opts: InternalSendToOptions): Promise<any> {\n const { command, message, callback, options, expectReply } = opts;\n let { instanceName } = opts;\n\n if (!instanceName) {\n throw new Error('No instanceName provided or not a string');\n }\n\n if (!instanceName.startsWith('system.adapter.')) {\n instanceName = `system.adapter.${instanceName}`;\n }\n\n const obj: ioBroker.SendableMessage = {\n command,\n message,\n from: `system.adapter.${this.namespace}`,\n };\n\n const states = this.states;\n\n if (typeof message !== 'object') {\n this.logger.silly(\n `${this.namespaceLog} sendTo \"${command}\" to ${instanceName} from system.adapter.${this.namespace}: ${message}`,\n );\n } else {\n this.logger.silly(\n `${this.namespaceLog} sendTo \"${command}\" to ${instanceName} from system.adapter.${this.namespace}`,\n );\n }\n\n if (!instanceName.match(/\\.[0-9]+$/)) {\n const objects = this.objects;\n\n // Only the instance lookup is best-effort; per-instance push errors must surface.\n const res = await objects\n .getObjectView('system', 'instance', {\n startkey: `${instanceName}.`,\n endkey: `${instanceName}.\\u9999`,\n })\n .catch(() => undefined);\n\n if (res) {\n for (const row of res.rows) {\n await states.pushMessage(row.id, obj);\n }\n }\n return;\n }\n\n if (expectReply) {\n return this.#registerReply(states, instanceName, obj, options);\n }\n\n if (callback) {\n obj.callback = { ...callback, ack: true };\n }\n\n await states.pushMessage(instanceName, obj);\n }\n\n /**\n * Subscribes to this instance's messagebox on demand when the adapter does not advertise\n * messagebox support, so replies to outbound messages are received. Idempotent.\n *\n * @param states the connected states DB client\n */\n #maybeSubscribe(states: StatesInRedisClient): void {\n const common = this.common;\n if (common && !isMessageboxSupported(common) && !this.#mboxSubscribed) {\n this.#mboxSubscribed = true;\n states\n .subscribeMessage(`system.adapter.${this.namespace}`)\n .catch(e => this.logger.error(`${this.namespaceLog} Cannot subscribe to messages: ${e.message}`));\n }\n }\n\n /**\n * Sets a reply callback header on `obj`, registers a pending-reply resolver keyed by its id,\n * subscribes the messagebox on demand, pushes the message and returns a promise that resolves\n * with the reply once `resolveCallback` is invoked for the matching id, or rejects with\n * `Error('Timeout exceeded')` when `options.timeout` elapses.\n *\n * @param states the connected states DB client\n * @param target the recipient id (instance or host) for `pushMessage`\n * @param obj the message to send; its `callback` header is set here\n * @param options additional send options (e.g. timeout)\n */\n async #registerReply(\n states: StatesInRedisClient,\n target: string,\n obj: ioBroker.SendableMessage,\n options?: SendToOptions,\n ): Promise<any> {\n this.#maybeSubscribe(states);\n\n const id = this.#callbackId++;\n if (this.#callbackId >= 0xffffffff) {\n this.#callbackId = 1;\n }\n\n obj.callback = {\n message: obj.message,\n id,\n ack: false,\n time: Date.now(),\n };\n\n let pendingEntry!: PendingReply;\n const replyPromise = new Promise<any>((resolve, reject) => {\n let timer: NodeJS.Timeout | undefined;\n if (options?.timeout) {\n timer = setTimeout(() => {\n if (this.#pending.has(id)) {\n this.#pending.delete(id);\n reject(new Error('Timeout exceeded'));\n }\n }, options.timeout);\n }\n\n pendingEntry = { resolve, reject, timer, time: Date.now() };\n this.#pending.set(id, pendingEntry);\n });\n\n const now = Date.now();\n for (const [_id, entry] of this.#pending) {\n if (now - entry.time > 3_600_000) {\n this.#pending.delete(_id);\n }\n }\n\n try {\n await states.pushMessage(target, obj);\n } catch (e) {\n if (pendingEntry.timer) {\n clearTimeout(pendingEntry.timer);\n }\n this.#pending.delete(id);\n throw e;\n }\n return replyPromise;\n }\n\n /**\n * Sends a message to a host, or broadcasts to all hosts when `hostName` is `null`.\n *\n * When `opts.expectReply` is true and a specific host is targeted, the returned promise\n * resolves with the reply message when it arrives, or rejects with `Error('Timeout exceeded')`\n * if `opts.options.timeout` elapses. Broadcasts (hostName === null) resolve void.\n *\n * @param opts Normalized send options\n */\n async sendToHost(opts: InternalSendToHostOptions): Promise<any> {\n const { command, message, expectReply, options, callback } = opts;\n let { hostName } = opts;\n const obj: ioBroker.SendableMessage = {\n command,\n message,\n from: `system.adapter.${this.namespace}`,\n };\n\n const states = this.states;\n\n if (hostName && !hostName.startsWith('system.host.')) {\n hostName = `system.host.${hostName}`;\n }\n\n if (!hostName) {\n const objects = this.objects;\n\n const res = await objects.getObjectList({ startkey: 'system.host.', endkey: 'system.host.\\u9999' });\n\n if (res?.rows.length) {\n for (const row of res.rows) {\n const parts: string[] = row.id.split('.');\n // ignore system.host.name.alive and so on\n if (parts.length === 3) {\n await states.pushMessage(row.id, obj);\n }\n }\n }\n return;\n }\n\n if (expectReply) {\n return this.#registerReply(states, hostName, obj, options);\n }\n\n if (callback) {\n obj.callback = { ...callback, ack: true };\n }\n\n await states.pushMessage(hostName, obj);\n }\n\n /**\n * Resolves a pending reply promise for an acked messagebox message.\n * Returns true if a pending entry was found and consumed.\n *\n * @param obj incoming message object from the messagebox\n */\n resolveCallback(obj: ioBroker.Message): boolean {\n if (!obj.callback?.ack || !obj.callback.id) {\n return false;\n }\n\n const entry = this.#pending.get(obj.callback.id);\n if (!entry) {\n return false;\n }\n\n if (entry.timer) {\n clearTimeout(entry.timer);\n }\n this.#pending.delete(obj.callback.id);\n entry.resolve(obj.message);\n\n const now = Date.now();\n for (const [id, pending] of this.#pending) {\n if (now - pending.time > 3_600_000) {\n this.#pending.delete(id);\n }\n }\n\n return true;\n }\n\n /**\n * Rejects all pending reply promises and clears their timers (used on stop).\n */\n clearPendingCallbacks(): void {\n if (this.#pending.size) {\n const err = new Error('Adapter stopped');\n for (const entry of this.#pending.values()) {\n if (entry.timer) {\n clearTimeout(entry.timer);\n }\n entry.reject(err);\n }\n this.#pending.clear();\n }\n }\n\n /**\n * Sends a message to a single UI client, or broadcasts to all connected UI clients when `clientId` is omitted.\n *\n * @param options clientId and data options\n */\n sendToUI(options: SendToUserInterfaceClientOptions): Promise<void> {\n const states = this.states;\n\n const { clientId, data } = options;\n\n if (clientId === undefined) {\n return this.uiMessagingController.sendToAllClients({\n data,\n states,\n });\n }\n\n return this.uiMessagingController.sendToClient({\n clientId,\n data,\n states,\n });\n }\n\n /**\n * Sends an addNotification command to the host of this adapter instance.\n *\n * @param scope notification scope\n * @param category notification category, or `null` to match by scope regex\n * @param message notification message\n * @param options additional notification options\n */\n async registerNotification(\n scope: string,\n category: string | null,\n message: string,\n options?: NotificationOptions,\n ): Promise<void> {\n const states = this.states;\n\n const obj = {\n command: 'addNotification',\n message: {\n scope,\n category,\n message,\n instance: this.namespace,\n contextData: options?.contextData,\n },\n from: `system.adapter.${this.namespace}`,\n };\n\n await states.pushMessage(`system.host.${this.host}`, obj);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|