iobroker.eos-admin 7.10.1 → 7.10.2
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/INSTALL_TEST_V7.10.2.md +22 -0
- package/MERGE_UPDATE_README_V7.10.2.md +11 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +15 -16
- package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +1410 -0
- package/PUBLISH_STABLE_V7.10.2.md +21 -0
- package/README.md +4 -4
- package/README_STABILITY_V7.10.2.md +20 -0
- package/RELEASE_ACCEPTANCE_V7.10.2.md +13 -0
- package/RELEASE_NOTES_V7.10.2.md +11 -0
- package/build/lib/eosAutoUpdate.js +418 -20
- package/build/lib/eosAutoUpdate.js.map +1 -0
- package/build/lib/eosRequestSecurity.js +4 -1
- package/build/lib/eosRequestSecurity.js.map +1 -0
- package/build/lib/web.js +40 -9
- package/build/lib/web.js.map +1 -1
- package/io-package.json +60 -5
- package/package.json +22 -16
- package/tools/nexowatt-auto-update-selftest.cjs +144 -32
- package/tools/nexowatt-backend-runtime-selftest.cjs +147 -0
- package/tools/nexowatt-merge-update-selftest.cjs +7 -1
- package/tools/nexowatt-prebuilt-release-selftest.cjs +292 -0
- package/tools/nexowatt-stable-v7102-selftest.cjs +51 -0
- package/tools/nexowatt-validate-package.cjs +35 -11
- package/INSTALL_TEST_V7.10.1.md +0 -10
- package/MERGE_UPDATE_README_V7.10.1.md +0 -10
- package/PUBLISH_STABLE_V7.10.1.md +0 -10
- package/README_STABILITY_V7.10.1.md +0 -10
- package/RELEASE_ACCEPTANCE_V7.10.1.md +0 -10
- package/RELEASE_NOTES_V7.10.1.md +0 -10
- package/adminWww/js/eos-release-watch.js +0 -77
- package/build/lib/eosPassword.js +0 -72
- package/build/lib/nexowattStableAuth.js +0 -74
- package/tools/nexowatt-stable-v7101-selftest.cjs +0 -15
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const crypto = require('node:crypto');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { spawnSync } = require('node:child_process');
|
|
8
|
+
|
|
9
|
+
const root = path.resolve(__dirname, '..');
|
|
10
|
+
const manifestFile = path.join(root, 'NEXOWATT_EOS_PREBUILT_MANIFEST.json');
|
|
11
|
+
const packageFile = path.join(root, 'package.json');
|
|
12
|
+
const trackedRoots = [
|
|
13
|
+
'src',
|
|
14
|
+
'build',
|
|
15
|
+
'src-admin/src',
|
|
16
|
+
'src-admin/public',
|
|
17
|
+
'adminWww',
|
|
18
|
+
'admin',
|
|
19
|
+
'public',
|
|
20
|
+
'tools',
|
|
21
|
+
];
|
|
22
|
+
const trackedFiles = [
|
|
23
|
+
'tasks.mts',
|
|
24
|
+
'tsconfig.json',
|
|
25
|
+
'tsconfig.build.json',
|
|
26
|
+
];
|
|
27
|
+
const expectedPrepublishOnly =
|
|
28
|
+
'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime && npm run check:eos-publish-channel && npm run check:eos-package && npm run check:eos-stability';
|
|
29
|
+
const expectedPrepack =
|
|
30
|
+
'node tools/nexowatt-sync-release-version.cjs --quiet && node tools/nexowatt-ensure-release-defaults.cjs && node tools/nexowatt-clean-legacy-runtime.cjs --quiet && node tools/nexowatt-prebuilt-release-selftest.cjs';
|
|
31
|
+
|
|
32
|
+
function normalize(relative) {
|
|
33
|
+
return relative.replace(/\\/g, '/');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function walk(relative, output) {
|
|
37
|
+
const absolute = path.join(root, relative);
|
|
38
|
+
if (!fs.existsSync(absolute)) return;
|
|
39
|
+
const stat = fs.lstatSync(absolute);
|
|
40
|
+
if (stat.isSymbolicLink()) {
|
|
41
|
+
throw new Error(`symbolic links are not allowed in the sealed release: ${normalize(relative)}`);
|
|
42
|
+
}
|
|
43
|
+
if (stat.isFile()) {
|
|
44
|
+
output.push(normalize(relative));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!stat.isDirectory()) return;
|
|
48
|
+
for (const name of fs.readdirSync(absolute).sort()) {
|
|
49
|
+
walk(path.join(relative, name), output);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function collectTrackedFiles() {
|
|
54
|
+
const files = [];
|
|
55
|
+
for (const relative of trackedRoots) walk(relative, files);
|
|
56
|
+
for (const relative of trackedFiles) {
|
|
57
|
+
const absolute = path.join(root, relative);
|
|
58
|
+
if (!fs.existsSync(absolute) || !fs.statSync(absolute).isFile()) {
|
|
59
|
+
throw new Error(`tracked release input is missing: ${relative}`);
|
|
60
|
+
}
|
|
61
|
+
files.push(normalize(relative));
|
|
62
|
+
}
|
|
63
|
+
return [...new Set(files)].sort();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sha256(relative) {
|
|
67
|
+
return crypto.createHash('sha256').update(fs.readFileSync(path.join(root, relative))).digest('hex');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createManifest() {
|
|
71
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
72
|
+
const files = collectTrackedFiles();
|
|
73
|
+
const hashes = {};
|
|
74
|
+
for (const relative of files) hashes[relative] = sha256(relative);
|
|
75
|
+
return {
|
|
76
|
+
format: 1,
|
|
77
|
+
package: pkg.name,
|
|
78
|
+
version: pkg.version,
|
|
79
|
+
algorithm: 'sha256',
|
|
80
|
+
fileCount: files.length,
|
|
81
|
+
trackedRoots,
|
|
82
|
+
trackedFiles,
|
|
83
|
+
files: hashes,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function writeManifest() {
|
|
88
|
+
const manifest = createManifest();
|
|
89
|
+
fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
90
|
+
console.log(`[NexoWatt EOS prebuilt release] sealed ${manifest.fileCount} source/runtime file(s) for ${manifest.version}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveLocal(fromFile, specifier) {
|
|
94
|
+
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
95
|
+
const candidates = [base, `${base}.js`, `${base}.json`, path.join(base, 'index.js')];
|
|
96
|
+
if (fs.existsSync(base) && fs.statSync(base).isDirectory()) {
|
|
97
|
+
const nestedPackage = path.join(base, 'package.json');
|
|
98
|
+
if (fs.existsSync(nestedPackage)) {
|
|
99
|
+
try {
|
|
100
|
+
const value = JSON.parse(fs.readFileSync(nestedPackage, 'utf8'));
|
|
101
|
+
if (value.main) candidates.unshift(path.resolve(base, value.main));
|
|
102
|
+
} catch {
|
|
103
|
+
// The normal package validator reports malformed JSON in detail.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return candidates.find(candidate => fs.existsSync(candidate) && fs.statSync(candidate).isFile()) || null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function inspectRuntimeClosure(failures) {
|
|
111
|
+
const entry = path.join(root, 'build', 'main.js');
|
|
112
|
+
const visited = new Set();
|
|
113
|
+
const runtimeFiles = new Set();
|
|
114
|
+
|
|
115
|
+
function inspect(file) {
|
|
116
|
+
const absolute = path.resolve(file);
|
|
117
|
+
if (visited.has(absolute)) return;
|
|
118
|
+
visited.add(absolute);
|
|
119
|
+
runtimeFiles.add(absolute);
|
|
120
|
+
if (!fs.existsSync(absolute)) {
|
|
121
|
+
failures.push(`missing runtime file ${normalize(path.relative(root, absolute))}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!absolute.endsWith('.js')) return;
|
|
125
|
+
|
|
126
|
+
const parsed = spawnSync(process.execPath, ['--check', absolute], {
|
|
127
|
+
encoding: 'utf8',
|
|
128
|
+
windowsHide: true,
|
|
129
|
+
});
|
|
130
|
+
if (parsed.status !== 0) {
|
|
131
|
+
failures.push(
|
|
132
|
+
`${normalize(path.relative(root, absolute))} does not parse: ${(parsed.stderr || parsed.stdout || '').trim()}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const text = fs.readFileSync(absolute, 'utf8');
|
|
137
|
+
const patterns = [
|
|
138
|
+
/\brequire\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
139
|
+
/\brequire\.resolve\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
140
|
+
/\bimport\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
141
|
+
];
|
|
142
|
+
for (const pattern of patterns) {
|
|
143
|
+
pattern.lastIndex = 0;
|
|
144
|
+
let match;
|
|
145
|
+
while ((match = pattern.exec(text))) {
|
|
146
|
+
const specifier = match[1];
|
|
147
|
+
if (!specifier.startsWith('.')) continue;
|
|
148
|
+
const target = resolveLocal(absolute, specifier);
|
|
149
|
+
if (!target) {
|
|
150
|
+
failures.push(`${normalize(path.relative(root, absolute))} -> ${specifier} cannot be resolved`);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
inspect(target);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
inspect(entry);
|
|
159
|
+
for (const required of [
|
|
160
|
+
'build/main.js',
|
|
161
|
+
'build/lib/web.js',
|
|
162
|
+
'build/lib/eosAutoUpdate.js',
|
|
163
|
+
'build/lib/eosRequestSecurity.js',
|
|
164
|
+
]) {
|
|
165
|
+
if (!runtimeFiles.has(path.join(root, required))) {
|
|
166
|
+
failures.push(`entrypoint closure does not include required module ${required}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return runtimeFiles.size;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function verifyManifest(failures) {
|
|
173
|
+
if (!fs.existsSync(manifestFile)) {
|
|
174
|
+
failures.push('NEXOWATT_EOS_PREBUILT_MANIFEST.json is missing');
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
let manifest;
|
|
178
|
+
try {
|
|
179
|
+
manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
|
|
180
|
+
} catch (error) {
|
|
181
|
+
failures.push(`prebuilt manifest is invalid JSON: ${error.message}`);
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
185
|
+
if (manifest.format !== 1) failures.push(`unsupported prebuilt manifest format ${manifest.format}`);
|
|
186
|
+
if (manifest.package !== pkg.name) failures.push(`prebuilt manifest package ${manifest.package} does not match ${pkg.name}`);
|
|
187
|
+
if (manifest.version !== pkg.version) failures.push(`prebuilt manifest version ${manifest.version} does not match ${pkg.version}`);
|
|
188
|
+
if (manifest.algorithm !== 'sha256') failures.push(`prebuilt manifest algorithm must be sha256, got ${manifest.algorithm}`);
|
|
189
|
+
|
|
190
|
+
let currentFiles = [];
|
|
191
|
+
try {
|
|
192
|
+
currentFiles = collectTrackedFiles();
|
|
193
|
+
} catch (error) {
|
|
194
|
+
failures.push(error.message);
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
const sealedFiles = Object.keys(manifest.files || {}).sort();
|
|
198
|
+
const currentSet = new Set(currentFiles);
|
|
199
|
+
const sealedSet = new Set(sealedFiles);
|
|
200
|
+
for (const relative of currentFiles) {
|
|
201
|
+
if (!sealedSet.has(relative)) failures.push(`unsealed release file: ${relative}`);
|
|
202
|
+
}
|
|
203
|
+
for (const relative of sealedFiles) {
|
|
204
|
+
if (!currentSet.has(relative)) failures.push(`sealed release file is missing: ${relative}`);
|
|
205
|
+
}
|
|
206
|
+
for (const relative of sealedFiles) {
|
|
207
|
+
if (!currentSet.has(relative)) continue;
|
|
208
|
+
const actual = sha256(relative);
|
|
209
|
+
if (actual !== manifest.files[relative]) failures.push(`sealed release file changed after validation: ${relative}`);
|
|
210
|
+
}
|
|
211
|
+
if (manifest.fileCount !== sealedFiles.length) {
|
|
212
|
+
failures.push(`prebuilt manifest fileCount ${manifest.fileCount} does not match ${sealedFiles.length}`);
|
|
213
|
+
}
|
|
214
|
+
return sealedFiles.length;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function verifyLifecycle(failures) {
|
|
218
|
+
const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
219
|
+
const scripts = pkg.scripts || {};
|
|
220
|
+
if (scripts.prepublishOnly !== expectedPrepublishOnly) {
|
|
221
|
+
failures.push('prepublishOnly is not the canonical dependency-free direct-publish workflow');
|
|
222
|
+
}
|
|
223
|
+
if (scripts.prepack !== expectedPrepack) {
|
|
224
|
+
failures.push('prepack is not the canonical dependency-free prebuilt-artifact verifier');
|
|
225
|
+
}
|
|
226
|
+
if (scripts['check:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs') {
|
|
227
|
+
failures.push('check:eos-prebuilt-release script is missing or incorrect');
|
|
228
|
+
}
|
|
229
|
+
if (scripts['seal:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs --write') {
|
|
230
|
+
failures.push('seal:eos-prebuilt-release script is missing or incorrect');
|
|
231
|
+
}
|
|
232
|
+
for (const [name, command] of [
|
|
233
|
+
['prepublishOnly', scripts.prepublishOnly || ''],
|
|
234
|
+
['prepack', scripts.prepack || ''],
|
|
235
|
+
]) {
|
|
236
|
+
for (const forbidden of [
|
|
237
|
+
/(?:^|\s)tsc(?:\s|$)/i,
|
|
238
|
+
/(?:^|\s)tsx(?:\s|$)/i,
|
|
239
|
+
/build:backend/i,
|
|
240
|
+
/build:frontend/i,
|
|
241
|
+
/(?:^|\s)npx(?:\s|$)/i,
|
|
242
|
+
/npm\s+(?:i|install|ci)(?:\s|$)/i,
|
|
243
|
+
]) {
|
|
244
|
+
if (forbidden.test(command)) failures.push(`${name} must not require local development dependencies: ${command}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const backendRuntimeSelftestFile = path.join(root, 'tools', 'nexowatt-backend-runtime-selftest.cjs');
|
|
248
|
+
if (!fs.existsSync(backendRuntimeSelftestFile)) {
|
|
249
|
+
failures.push('tools/nexowatt-backend-runtime-selftest.cjs is missing');
|
|
250
|
+
} else {
|
|
251
|
+
const backendRuntimeSelftest = fs.readFileSync(backendRuntimeSelftestFile, 'utf8');
|
|
252
|
+
if (!backendRuntimeSelftest.includes('dependency-free on Windows')) {
|
|
253
|
+
failures.push('backend runtime selftest is missing the cross-platform publish marker');
|
|
254
|
+
}
|
|
255
|
+
if (/const\s+npm\s*=\s*process\.platform|spawnSync\(\s*npm\s*,|['"]npm\.cmd['"]/.test(backendRuntimeSelftest)) {
|
|
256
|
+
failures.push('backend runtime selftest must not spawn a nested npm process');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const packageFiles = new Set((pkg.files || []).map(value => String(value).replace(/\\/g, '/')));
|
|
260
|
+
if (![...packageFiles].some(value => value.replace(/\/+$/, '') === 'build')) {
|
|
261
|
+
failures.push('package.json files must include the complete build directory');
|
|
262
|
+
}
|
|
263
|
+
for (const required of [
|
|
264
|
+
'tools/nexowatt-prebuilt-release-selftest.cjs',
|
|
265
|
+
'NEXOWATT_EOS_PREBUILT_MANIFEST.json',
|
|
266
|
+
]) {
|
|
267
|
+
if (!packageFiles.has(required)) failures.push(`package.json files is missing ${required}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (process.argv.includes('--write')) {
|
|
272
|
+
try {
|
|
273
|
+
writeManifest();
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error(`[NexoWatt EOS prebuilt release] ${error instanceof Error ? error.message : String(error)}`);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const failures = [];
|
|
282
|
+
verifyLifecycle(failures);
|
|
283
|
+
const sealedCount = verifyManifest(failures);
|
|
284
|
+
const runtimeCount = inspectRuntimeClosure(failures);
|
|
285
|
+
|
|
286
|
+
if (failures.length) {
|
|
287
|
+
console.error(`[NexoWatt EOS prebuilt release] ${failures.length} failure(s):\n${failures.join('\n')}`);
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
console.log(
|
|
291
|
+
`[NexoWatt EOS prebuilt release] OK (${sealedCount} sealed source/runtime files; ${runtimeCount} backend runtime files; no tsc/tsx required for npm publish)`,
|
|
292
|
+
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const root = path.resolve(__dirname, '..');
|
|
7
|
+
const read = relative => fs.readFileSync(path.join(root, relative), 'utf8');
|
|
8
|
+
const json = relative => JSON.parse(read(relative));
|
|
9
|
+
const must = (value, message) => {
|
|
10
|
+
if (!value) {
|
|
11
|
+
console.error(`[NexoWatt EOS stable v7102] ${message}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const pkg = json('package.json');
|
|
17
|
+
const io = json('io-package.json');
|
|
18
|
+
const info = json('NEXOWATT_EOS_BUILD_INFO.json');
|
|
19
|
+
const main = read('src/main.ts');
|
|
20
|
+
const web = read('src/lib/web.ts');
|
|
21
|
+
const boot = read('adminWww/js/eos-role-bootstrap.js');
|
|
22
|
+
const role = read('adminWww/js/eos-role-ui.js');
|
|
23
|
+
const css = read('adminWww/css/nexowatt-native-shell.css');
|
|
24
|
+
const index = read('adminWww/index.html');
|
|
25
|
+
const ems = read('adminWww/js/eos-ems-overview.js');
|
|
26
|
+
const intro = read('src-admin/src/tabs/Intro.tsx');
|
|
27
|
+
const bundle = read('adminWww/assets/Intro-DkwRiz1n-v84.js');
|
|
28
|
+
const stable = read('adminWww/static/js/nexowatt-stable-v7101.js');
|
|
29
|
+
const auto = read('src/lib/eosAutoUpdate.ts');
|
|
30
|
+
|
|
31
|
+
must(pkg.version === '7.10.2' && io.version === '7.10.2' && io.common.version === '7.10.2' && info.version === '7.10.2', 'version drift');
|
|
32
|
+
must(io.native.eosRequireFirstLoginPassword === false && io.native.eosRequireFirstLoginPasswordChange === false, 'forced first password still enabled');
|
|
33
|
+
must(!index.includes('eos-account-management.js'), 'custom reset UI still loaded');
|
|
34
|
+
must(!boot.includes('installIntegratedFirstLogin(base);') && !boot.includes('showFirstLoginPassword(resolved, base);'), 'forced first-login UI still active');
|
|
35
|
+
must(main.includes('standardPasswordMode: true') && main.includes('nexowattPasswordChangeRequired: false'), 'account migration missing');
|
|
36
|
+
must(role.includes("route === 'tab-users'") && main.includes("command === 'changePassword'"), 'role-aware native password editing missing');
|
|
37
|
+
must(css.includes('persistent native overview scrolling') && stable.includes("overflow-y','scroll"), 'persistent scroll missing');
|
|
38
|
+
must(intro.includes('eos-overview-edit-controls') && bundle.includes('eos-overview-edit-controls'), 'edit control not in bottom grid');
|
|
39
|
+
must(!ems.includes('Vollständige EMS-Diagnose öffnen'), 'unsupported EMS link remains');
|
|
40
|
+
must(index.includes('eos-auto-update.js?v=7101') && auto.includes("const POLICY: NexoWattAutoUpdatePolicy = 'major'"), 'auto update missing');
|
|
41
|
+
|
|
42
|
+
// 7.10.2 hotfix acceptance: no native writes on the running instance, delayed
|
|
43
|
+
// startup reconciliation, cleanup on Web.close and complete backend package.
|
|
44
|
+
must(auto.includes("const ENABLED_STATE_ID = 'info.nexowattStableUpdatesEnabled'"), 'restart-safe enabled state missing');
|
|
45
|
+
must(auto.includes("const STATUS_STATE_ID = 'info.nexowattStableUpdatesState'"), 'restart-safe status state missing');
|
|
46
|
+
must(auto.includes('const STARTUP_RECONCILE_DELAY_MS = 30_000'), 'startup grace period missing');
|
|
47
|
+
must(!auto.includes('extendForeignObjectAsync(this.instanceId'), 'runtime still writes the running instance native configuration');
|
|
48
|
+
must(web.includes('this.nexowattStableUpdateManager?.stop();'), 'auto-update manager is not stopped with the web server');
|
|
49
|
+
must(fs.existsSync(path.join(root, 'build/lib/eosRequestSecurity.js')), 'eosRequestSecurity runtime module missing');
|
|
50
|
+
|
|
51
|
+
console.log('[NexoWatt EOS stable v7102] OK (v7101 UI baseline plus restart-loop and package-completeness hotfix)');
|
|
@@ -11,6 +11,8 @@ const fail = msg => {
|
|
|
11
11
|
const read = file => fs.readFileSync(path.join(root, file), 'utf8');
|
|
12
12
|
const readJson = file => JSON.parse(read(file));
|
|
13
13
|
const exists = file => fs.existsSync(path.join(root, file));
|
|
14
|
+
const canonicalPrepublishOnly = 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime && npm run check:eos-publish-channel && npm run check:eos-package && npm run check:eos-stability';
|
|
15
|
+
const canonicalPrepack = 'node tools/nexowatt-sync-release-version.cjs --quiet && node tools/nexowatt-ensure-release-defaults.cjs && node tools/nexowatt-clean-legacy-runtime.cjs --quiet && node tools/nexowatt-prebuilt-release-selftest.cjs';
|
|
14
16
|
|
|
15
17
|
const pkg = readJson('package.json');
|
|
16
18
|
const pkgLock = readJson('package-lock.json');
|
|
@@ -41,8 +43,16 @@ if (pkg.scripts['test:eos-version-sync'] !== 'node tools/nexowatt-version-sync-s
|
|
|
41
43
|
if (pkg.scripts['verify:eos-merge'] !== 'node tools/nexowatt-merge-update-selftest.cjs') fail('merge update selftest script is missing or incorrect');
|
|
42
44
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-version-sync-selftest.cjs')) fail('version sync selftest is not part of check:eos-stability');
|
|
43
45
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-merge-update-selftest.cjs')) fail('merge update selftest is not part of check:eos-stability');
|
|
44
|
-
if (pkg.scripts['check:eos-stable-
|
|
45
|
-
if (
|
|
46
|
+
if (pkg.scripts['check:eos-stable-v7102'] !== 'node tools/nexowatt-stable-v7102-selftest.cjs') fail('stable v7102 selftest script is missing or incorrect');
|
|
47
|
+
if (pkg.scripts.prepublishOnly !== canonicalPrepublishOnly) fail('prepublishOnly must use the dependency-free direct-publish workflow');
|
|
48
|
+
if (/\b(?:tsc|tsx)\b|build:(?:backend|frontend)|\bnpx\b|npm\s+(?:i|install|ci)\b/i.test(pkg.scripts.prepublishOnly || '')) fail('prepublishOnly must not require local development dependencies');
|
|
49
|
+
if (pkg.scripts['check:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs') fail('prebuilt release selftest script is missing or incorrect');
|
|
50
|
+
if (pkg.scripts['seal:eos-prebuilt-release'] !== 'node tools/nexowatt-prebuilt-release-selftest.cjs --write') fail('prebuilt release sealing script is missing or incorrect');
|
|
51
|
+
const backendRuntimeSelftest = read('tools/nexowatt-backend-runtime-selftest.cjs');
|
|
52
|
+
if (!backendRuntimeSelftest.includes('dependency-free on Windows')) fail('backend runtime selftest is missing the cross-platform publish marker');
|
|
53
|
+
if (/const\s+npm\s*=\s*process\.platform|spawnSync\(\s*npm\s*,|['"]npm\.cmd['"]/.test(backendRuntimeSelftest)) fail('backend runtime selftest must not spawn a nested npm process');
|
|
54
|
+
if (!(pkg.files || []).some(value => String(value).replace(/\\/g, '/').replace(/\/+$/, '') === 'build')) fail('package.json files must include the complete build directory');
|
|
55
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-prebuilt-release-selftest.cjs')) fail('prebuilt release selftest is not part of check:eos-stability');
|
|
46
56
|
if (pkg.scripts['precheck:eos-package'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-package must synchronize versions, normalize release defaults and clean stale runtime files');
|
|
47
57
|
if (pkg.scripts['precheck:eos-stability'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-stability must synchronize versions, normalize release defaults and clean stale runtime files');
|
|
48
58
|
if (pkg.scripts['test:eos-publish-channel'] !== 'node tools/nexowatt-publish-channel-selftest.cjs') fail('publish channel selftest script is missing');
|
|
@@ -53,6 +63,9 @@ if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flo
|
|
|
53
63
|
if (io.native?.auth !== true) fail('fresh sales installations must default to authenticated access');
|
|
54
64
|
if (io.native?.eosHideLegacyBackupFromNonAdmins !== true) fail('internal BackItUp reserve must default to Admin/Service visibility');
|
|
55
65
|
if (io.native?.nexowattHideLegacyBackupFromNonAdmins !== true) fail('NexoWatt backup visibility compatibility flag must default to true');
|
|
66
|
+
const instanceObjectIds = new Set((io.instanceObjects || []).map(object => object?._id));
|
|
67
|
+
if (!instanceObjectIds.has('info.nexowattStableUpdatesEnabled')) fail('restart-safe auto-update enabled state is missing');
|
|
68
|
+
if (!instanceObjectIds.has('info.nexowattStableUpdatesState')) fail('restart-safe auto-update runtime state is missing');
|
|
56
69
|
for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
|
|
57
70
|
if (JSON.stringify(pkgLock.packages?.['']?.[key]) !== JSON.stringify(pkg[key])) {
|
|
58
71
|
fail(`package-lock root ${key} must match package.json`);
|
|
@@ -114,23 +127,27 @@ for (const file of [
|
|
|
114
127
|
'src-admin/src/components/Drawer.tsx',
|
|
115
128
|
'src-admin/src/components/DrawerItem.tsx',
|
|
116
129
|
'admin/admin.svg',
|
|
130
|
+
'build/lib/eosRequestSecurity.js',
|
|
117
131
|
'LICENSE',
|
|
118
132
|
'NEXOWATT_PROPRIETARY_LICENSE.md',
|
|
119
133
|
'THIRD_PARTY_NOTICES.md',
|
|
120
|
-
'README_STABILITY_V7.10.
|
|
121
|
-
'RELEASE_NOTES_V7.10.
|
|
122
|
-
'PUBLISH_STABLE_V7.10.
|
|
123
|
-
'INSTALL_TEST_V7.10.
|
|
124
|
-
'RELEASE_ACCEPTANCE_V7.10.
|
|
134
|
+
'README_STABILITY_V7.10.2.md',
|
|
135
|
+
'RELEASE_NOTES_V7.10.2.md',
|
|
136
|
+
'PUBLISH_STABLE_V7.10.2.md',
|
|
137
|
+
'INSTALL_TEST_V7.10.2.md',
|
|
138
|
+
'RELEASE_ACCEPTANCE_V7.10.2.md',
|
|
125
139
|
'MERGE_UPDATE.ps1',
|
|
126
140
|
'MERGE_UPDATE.cmd',
|
|
127
|
-
'MERGE_UPDATE_README_V7.10.
|
|
141
|
+
'MERGE_UPDATE_README_V7.10.2.md',
|
|
128
142
|
'tools/nexowatt-patch-built-frontend.cjs',
|
|
129
143
|
'tools/nexowatt-native-shell-selftest.cjs',
|
|
130
144
|
'tools/nexowatt-clean-legacy-runtime.cjs',
|
|
131
145
|
'tools/nexowatt-runtime-cleanup-selftest.cjs',
|
|
132
146
|
'tools/nexowatt-esm-syntax-selftest.cjs',
|
|
133
147
|
'tools/nexowatt-import-integrity-selftest.cjs',
|
|
148
|
+
'tools/nexowatt-backend-runtime-selftest.cjs',
|
|
149
|
+
'tools/nexowatt-prebuilt-release-selftest.cjs',
|
|
150
|
+
'NEXOWATT_EOS_PREBUILT_MANIFEST.json',
|
|
134
151
|
'tools/nexowatt-entrypoint-smoke-selftest.cjs',
|
|
135
152
|
'tools/nexowatt-role-access-selftest.cjs',
|
|
136
153
|
'tools/nexowatt-first-login-selftest.cjs',
|
|
@@ -142,7 +159,7 @@ for (const file of [
|
|
|
142
159
|
'tools/nexowatt-sync-release-version.cjs',
|
|
143
160
|
'tools/nexowatt-version-sync-selftest.cjs',
|
|
144
161
|
'tools/nexowatt-merge-update-selftest.cjs',
|
|
145
|
-
'tools/nexowatt-stable-
|
|
162
|
+
'tools/nexowatt-stable-v7102-selftest.cjs',
|
|
146
163
|
'tools/nexowatt-password-write-selftest.cjs',
|
|
147
164
|
'tools/nexowatt-account-password-flow-selftest.cjs',
|
|
148
165
|
'tools/nexowatt-ems-overview-selftest.cjs',
|
|
@@ -255,19 +272,26 @@ if (!mf.includes(`remoteEntry-${runtime}.js`) || !mf.includes(`index-D2ymscJA-${
|
|
|
255
272
|
const webBuild = read('build/lib/web.js');
|
|
256
273
|
if (!webBuild.includes('refreshLifetime: 60 * 60 * 24 * 7')) fail('build/lib/web.js must keep upstream-compatible refresh lifetime');
|
|
257
274
|
if (!webBuild.includes('Follow upstream admin semantics again')) fail('build/lib/web.js lacks session compatibility fix');
|
|
275
|
+
if (!exists('build/lib/eosRequestSecurity.js')) fail('build/lib/eosRequestSecurity.js is missing');
|
|
276
|
+
const autoUpdateSource = read('src/lib/eosAutoUpdate.ts');
|
|
277
|
+
if (autoUpdateSource.includes('extendForeignObjectAsync(this.instanceId')) fail('auto-update runtime must not write the running instance native configuration');
|
|
278
|
+
if (!autoUpdateSource.includes("const ENABLED_STATE_ID = 'info.nexowattStableUpdatesEnabled'")) fail('restart-safe auto-update state storage is missing');
|
|
258
279
|
const mainBuild = read('build/main.js');
|
|
259
280
|
if (!mainBuild.includes('v37 BackItUp/runtime-adapter compatibility')) fail('build/main.js lacks BackItUp compatibility guard');
|
|
260
281
|
|
|
261
282
|
if (!pkg.scripts['nexowatt:patch-built-frontend']) fail('missing nexowatt:patch-built-frontend script');
|
|
262
283
|
if (pkg.scripts['check:eos-esm'] !== 'node tools/nexowatt-esm-syntax-selftest.cjs') fail('check:eos-esm script is missing or incorrect');
|
|
263
284
|
if (pkg.scripts['check:eos-imports'] !== 'node tools/nexowatt-import-integrity-selftest.cjs') fail('check:eos-imports script is missing or incorrect');
|
|
285
|
+
if (pkg.scripts['check:eos-backend-runtime'] !== 'node tools/nexowatt-backend-runtime-selftest.cjs') fail('check:eos-backend-runtime script is missing or incorrect');
|
|
264
286
|
if (pkg.scripts['check:eos-entry'] !== 'node tools/nexowatt-entrypoint-smoke-selftest.cjs') fail('check:eos-entry script is missing or incorrect');
|
|
265
287
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-esm-syntax-selftest.cjs')) fail('ESM syntax selftest is not part of check:eos-stability');
|
|
266
288
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-import-integrity-selftest.cjs')) fail('import integrity selftest is not part of check:eos-stability');
|
|
289
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-backend-runtime-selftest.cjs')) fail('backend runtime selftest is not part of check:eos-stability');
|
|
267
290
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-entrypoint-smoke-selftest.cjs')) fail('entrypoint smoke selftest is not part of check:eos-stability');
|
|
268
291
|
if (pkg.scripts['clean:eos-runtime'] !== 'node tools/nexowatt-clean-legacy-runtime.cjs') fail('clean:eos-runtime script is missing or incorrect');
|
|
269
|
-
if (pkg.scripts.prepack !==
|
|
270
|
-
if (
|
|
292
|
+
if (pkg.scripts.prepack !== canonicalPrepack) fail('prepack must verify the sealed prebuilt artifact without tsc/tsx');
|
|
293
|
+
if (/\b(?:tsc|tsx)\b|build:(?:backend|frontend)|\bnpx\b|npm\s+(?:i|install|ci)\b/i.test(pkg.scripts.prepack || '')) fail('prepack must not require local development dependencies');
|
|
294
|
+
if (pkg.scripts.build !== 'npm run build:frontend && npm run build:backend && npm run clean:eos-runtime && npm run seal:eos-prebuilt-release') fail('build must clean and seal the prebuilt release after compilation');
|
|
271
295
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-role-access-selftest.cjs')) fail('role access selftest is not part of check:eos-stability');
|
|
272
296
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('standard account-password flow selftest is not part of check:eos-stability');
|
|
273
297
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('standard account-password management selftest is not part of check:eos-stability');
|
package/INSTALL_TEST_V7.10.1.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
package/RELEASE_NOTES_V7.10.1.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.10.1
|
|
2
|
-
|
|
3
|
-
- Basis: veröffentlichte 7.9.98-Stable-Repository-ZIP.
|
|
4
|
-
- Keine erzwungene EOS-Erstpasswortseite und kein eigener EOS-Passwortreset.
|
|
5
|
-
- Standardpasswort `nexowatt` nur bei neu angelegten oder wirklich passwortlosen Konten.
|
|
6
|
-
- Passwortänderung unter Zugänge & Rechte über die native ioBroker-Passwortfunktion.
|
|
7
|
-
- Dauerhafter vertikaler Cockpit-Scrollbereich.
|
|
8
|
-
- Bearbeiten-Stift innerhalb des untersten Kartenrasters.
|
|
9
|
-
- Nicht vorhandener Link zur vollständigen EMS-Diagnose entfernt.
|
|
10
|
-
- Automatische Stable-Updates nur für installierte NexoWatt-Adapter, standardmäßig aktiv und durch Admin/Service abschaltbar.
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
(() => {
|
|
2
|
-
'use strict';
|
|
3
|
-
const RELEASE = '7.10.0';
|
|
4
|
-
const RELEASE_KEY = 'nexowatt.eos.loadedRelease';
|
|
5
|
-
const RELOAD_KEY = 'nexowatt.eos.reloadTarget';
|
|
6
|
-
const CHECK_MS = 15000;
|
|
7
|
-
let refreshing = false;
|
|
8
|
-
|
|
9
|
-
const normalize = value => String(value || '').trim().replace(/^v/i, '');
|
|
10
|
-
const validRelease = value => /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(normalize(value));
|
|
11
|
-
const reloadUrl = version => {
|
|
12
|
-
const url = new URL(window.location.href);
|
|
13
|
-
url.searchParams.set('_eosRelease', normalize(version) || RELEASE);
|
|
14
|
-
url.searchParams.set('_eosReload', String(Date.now()));
|
|
15
|
-
return url.toString();
|
|
16
|
-
};
|
|
17
|
-
const purgeBrowserCaches = async () => {
|
|
18
|
-
const jobs = [];
|
|
19
|
-
try {
|
|
20
|
-
if ('caches' in window) {
|
|
21
|
-
jobs.push(caches.keys().then(keys => Promise.all(keys.map(key => caches.delete(key)))));
|
|
22
|
-
}
|
|
23
|
-
} catch (_) { /* Cache API is optional */ }
|
|
24
|
-
try {
|
|
25
|
-
if (navigator.serviceWorker?.getRegistrations) {
|
|
26
|
-
jobs.push(navigator.serviceWorker.getRegistrations().then(rows => Promise.all(rows.map(row => row.unregister()))));
|
|
27
|
-
}
|
|
28
|
-
} catch (_) { /* service workers may be unavailable */ }
|
|
29
|
-
await Promise.allSettled(jobs);
|
|
30
|
-
};
|
|
31
|
-
const reloadFor = async version => {
|
|
32
|
-
const target = normalize(version) || RELEASE;
|
|
33
|
-
if (refreshing || sessionStorage.getItem(RELOAD_KEY) === target) return;
|
|
34
|
-
refreshing = true;
|
|
35
|
-
sessionStorage.setItem(RELOAD_KEY, target);
|
|
36
|
-
document.documentElement.classList.add('eos-release-refreshing');
|
|
37
|
-
await purgeBrowserCaches();
|
|
38
|
-
window.location.replace(reloadUrl(target));
|
|
39
|
-
};
|
|
40
|
-
const checkServerVersion = async () => {
|
|
41
|
-
try {
|
|
42
|
-
const endpoint = new URL('./version', window.location.href);
|
|
43
|
-
endpoint.searchParams.set('_eosTs', String(Date.now()));
|
|
44
|
-
const response = await fetch(endpoint, {
|
|
45
|
-
cache: 'no-store',
|
|
46
|
-
credentials: 'same-origin',
|
|
47
|
-
headers: { 'Cache-Control': 'no-cache' },
|
|
48
|
-
});
|
|
49
|
-
if (!response.ok) return;
|
|
50
|
-
const serverVersion = normalize(await response.text());
|
|
51
|
-
if (validRelease(serverVersion) && serverVersion !== RELEASE) {
|
|
52
|
-
await reloadFor(serverVersion);
|
|
53
|
-
}
|
|
54
|
-
} catch (_) { /* adapter restart or temporary network interruption */ }
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
window.NEXOWATT_EOS_RELEASE = RELEASE;
|
|
58
|
-
const previous = normalize(localStorage.getItem(RELEASE_KEY));
|
|
59
|
-
localStorage.setItem(RELEASE_KEY, RELEASE);
|
|
60
|
-
const loadedFor = normalize(new URL(window.location.href).searchParams.get('_eosRelease'));
|
|
61
|
-
if (previous && previous !== RELEASE && loadedFor !== RELEASE) {
|
|
62
|
-
void reloadFor(RELEASE);
|
|
63
|
-
} else {
|
|
64
|
-
sessionStorage.removeItem(RELOAD_KEY);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
window.addEventListener('focus', () => void checkServerVersion());
|
|
68
|
-
document.addEventListener('visibilitychange', () => {
|
|
69
|
-
if (document.visibilityState === 'visible') void checkServerVersion();
|
|
70
|
-
});
|
|
71
|
-
window.setInterval(() => void checkServerVersion(), CHECK_MS);
|
|
72
|
-
window.NEXOWATT_EOS_RELEASE_WATCH = Object.freeze({
|
|
73
|
-
release: RELEASE,
|
|
74
|
-
check: checkServerVersion,
|
|
75
|
-
purge: purgeBrowserCaches,
|
|
76
|
-
});
|
|
77
|
-
})();
|