tabby-rdp 0.5.1 → 0.5.3
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/CHANGELOG.md +75 -0
- package/README.md +41 -11
- package/TROUBLESHOOTING.md +82 -0
- package/dist/accountRevisions.js +46 -0
- package/dist/accounts.js +5 -1
- package/dist/connectionStatus.js +21 -4
- package/dist/desktop.service.js +296 -96
- package/dist/desktopForm.js +5 -4
- package/dist/diskStorage.js +212 -0
- package/dist/fileTransfer.js +6 -53
- package/dist/help.js +10 -62
- package/dist/hostCompat.js +86 -0
- package/dist/index.js +5 -2
- package/dist/ironrdp.js +26 -0
- package/dist/keyboard.js +12 -1
- package/dist/packageChangeDialog.js +186 -0
- package/dist/rdcleanpath.js +17 -4
- package/dist/rdpFile.js +13 -4
- package/dist/rdpProfile.js +44 -14
- package/dist/releaseSettings.js +72 -0
- package/dist/releases.js +411 -0
- package/dist/settingsPage.js +110 -115
- package/dist/signin.js +73 -10
- package/dist/targets.js +9 -1
- package/dist/updates.js +77 -89
- package/docs/desk.md +157 -0
- package/package.json +11 -2
- package/vendor/SHA256SUMS +1 -0
- package/vendor/ironrdp-factory.js +3237 -0
package/dist/releases.js
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReleaseManager = exports.RELEASE_STATE_KEY = exports.RELEASES = exports.REGISTRY = void 0;
|
|
4
|
+
exports.releaseVersion = releaseVersion;
|
|
5
|
+
exports.newer = newer;
|
|
6
|
+
exports.registryJSON = registryJSON;
|
|
7
|
+
exports.parseCatalog = parseCatalog;
|
|
8
|
+
exports.publishedVersions = publishedVersions;
|
|
9
|
+
const semver = require("semver");
|
|
10
|
+
const rxjs_1 = require("rxjs");
|
|
11
|
+
exports.REGISTRY = 'https://registry.npmjs.org/tabby-rdp';
|
|
12
|
+
exports.RELEASES = 'https://github.com/meanaverage/tabby-rdp/releases';
|
|
13
|
+
exports.RELEASE_STATE_KEY = 'tabby-rdp.releases.v1';
|
|
14
|
+
const BASELINE = '0.5.1';
|
|
15
|
+
const CACHE_MS = 15 * 60 * 1000;
|
|
16
|
+
/** Exact, canonical versions only: never an npm selector, alias, URL or tag. */
|
|
17
|
+
function releaseVersion(value) {
|
|
18
|
+
return typeof value === 'string' && semver.valid(value) === value && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:beta|rc)\.(0|[1-9]\d*))?$/.test(value);
|
|
19
|
+
}
|
|
20
|
+
function newer(a, b) {
|
|
21
|
+
return releaseVersion(a) && releaseVersion(b) && semver.gt(a, b);
|
|
22
|
+
}
|
|
23
|
+
const greatest = (a, b) => semver.gt(a, b) ? a : b;
|
|
24
|
+
function policy(value) {
|
|
25
|
+
return value && typeof value.minimumTabbyVersion === 'string' && semver.valid(value.minimumTabbyVersion) &&
|
|
26
|
+
releaseVersion(value.rollbackFloor) ? { minimumTabbyVersion: value.minimumTabbyVersion, rollbackFloor: value.rollbackFloor } : undefined;
|
|
27
|
+
}
|
|
28
|
+
/** Bound both the response body and elapsed time, including slow streaming responses. */
|
|
29
|
+
async function registryJSON(url, timeoutMs = 15000, maxBytes = 4 * 1024 * 1024) {
|
|
30
|
+
const abort = new AbortController();
|
|
31
|
+
const timer = setTimeout(() => abort.abort(), timeoutMs);
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(url, { cache: 'no-store', signal: abort.signal });
|
|
34
|
+
if (!response.ok || !response.body) {
|
|
35
|
+
throw new Error(`The registry answered ${response.status}.`);
|
|
36
|
+
}
|
|
37
|
+
const reader = response.body.getReader();
|
|
38
|
+
const chunks = [];
|
|
39
|
+
let size = 0;
|
|
40
|
+
for (;;) {
|
|
41
|
+
const { done, value } = await reader.read();
|
|
42
|
+
if (done) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
size += value.byteLength;
|
|
46
|
+
if (size > maxBytes) {
|
|
47
|
+
throw new Error('The registry\'s answer is too large.');
|
|
48
|
+
}
|
|
49
|
+
chunks.push(value);
|
|
50
|
+
}
|
|
51
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
abort.abort();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function parseCatalog(data) {
|
|
59
|
+
if (data?.name !== 'tabby-rdp' || !data.versions || typeof data.versions !== 'object' || Array.isArray(data.versions)) {
|
|
60
|
+
throw new Error('The registry returned an invalid version list.');
|
|
61
|
+
}
|
|
62
|
+
const entries = Object.entries(data.versions);
|
|
63
|
+
if (entries.length > 5000) {
|
|
64
|
+
throw new Error('The registry\'s version list is too large.');
|
|
65
|
+
}
|
|
66
|
+
const versions = [];
|
|
67
|
+
for (const [version, raw] of entries) {
|
|
68
|
+
const p = raw;
|
|
69
|
+
if (!releaseVersion(version) || p?.name !== 'tabby-rdp' || p.version !== version) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// 0.5.1 predates this field. Its requirements and irreversible credential migration were reviewed explicitly.
|
|
73
|
+
let compatibility = policy(p.tabbyRdp) ?? (version === BASELINE ? { minimumTabbyVersion: '1.0.236', rollbackFloor: BASELINE } : undefined);
|
|
74
|
+
if (compatibility && semver.gt(compatibility.rollbackFloor, version)) {
|
|
75
|
+
compatibility = undefined;
|
|
76
|
+
}
|
|
77
|
+
versions.push({ version, policy: compatibility, node: typeof p.engines?.node === 'string' ? p.engines.node : undefined,
|
|
78
|
+
os: Array.isArray(p.os) ? p.os.filter((x) => typeof x === 'string') : undefined,
|
|
79
|
+
cpu: Array.isArray(p.cpu) ? p.cpu.filter((x) => typeof x === 'string') : undefined,
|
|
80
|
+
deprecated: !!p.deprecated });
|
|
81
|
+
}
|
|
82
|
+
versions.sort((a, b) => semver.rcompare(a.version, b.version));
|
|
83
|
+
const tag = (name, preview) => {
|
|
84
|
+
const v = data['dist-tags']?.[name];
|
|
85
|
+
return releaseVersion(v) && !!semver.prerelease(v) === preview && versions.some(p => p.version === v) ? v : null;
|
|
86
|
+
};
|
|
87
|
+
return { versions, stable: tag('latest', false), preview: tag('beta', true) };
|
|
88
|
+
}
|
|
89
|
+
async function publishedVersions() {
|
|
90
|
+
return parseCatalog(await registryJSON(exports.REGISTRY));
|
|
91
|
+
}
|
|
92
|
+
/** No config or credential backups: only local preferences and successful package-change history. */
|
|
93
|
+
class ReleaseManager {
|
|
94
|
+
constructor(env, initiallyPaused = false) {
|
|
95
|
+
this.env = env;
|
|
96
|
+
this.changed$ = new rxjs_1.Subject();
|
|
97
|
+
this.catalog = null;
|
|
98
|
+
this.checking = false;
|
|
99
|
+
this.busy = false;
|
|
100
|
+
this.error = '';
|
|
101
|
+
this.catalogError = '';
|
|
102
|
+
this.notice = '';
|
|
103
|
+
this.storageWarning = '';
|
|
104
|
+
this.checkedAt = null;
|
|
105
|
+
this.lastAttempt = -Infinity;
|
|
106
|
+
this.running = env.running;
|
|
107
|
+
this.now = env.now ?? Date.now;
|
|
108
|
+
this.state = { channel: 'stable', paused: initiallyPaused, rollbackFloor: greatest(BASELINE, env.policy.rollbackFloor) };
|
|
109
|
+
this.syncFromStorage();
|
|
110
|
+
this.ready = this.lock(async () => {
|
|
111
|
+
this.syncFromStorage();
|
|
112
|
+
this.reconcileDisk();
|
|
113
|
+
this.persist();
|
|
114
|
+
}).catch(e => { this.error = this.message(e); }).finally(() => this.changed$.next());
|
|
115
|
+
}
|
|
116
|
+
message(error) { return error instanceof Error ? error.message : 'Use Tabby Plugins to manage this installation.'; }
|
|
117
|
+
async lock(run) {
|
|
118
|
+
if (this.env.withLock) {
|
|
119
|
+
await this.env.withLock(run);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
await run();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Storage events call this too. Reading never clears another window's successful package change. */
|
|
126
|
+
syncFromStorage() {
|
|
127
|
+
if (!this.env.storage) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const saved = JSON.parse(this.env.storage.getItem(exports.RELEASE_STATE_KEY) ?? 'null');
|
|
132
|
+
if (!saved) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.state.channel = saved.channel === 'preview' ? 'preview' : 'stable';
|
|
136
|
+
if (typeof saved.paused === 'boolean') {
|
|
137
|
+
this.state.paused = saved.paused;
|
|
138
|
+
}
|
|
139
|
+
this.state.previous = releaseVersion(saved.previous) ? saved.previous : undefined;
|
|
140
|
+
this.state.pending = releaseVersion(saved.pending) ? saved.pending : undefined;
|
|
141
|
+
this.state.removed = saved.removed === true;
|
|
142
|
+
if (releaseVersion(saved.rollbackFloor)) {
|
|
143
|
+
this.state.rollbackFloor = greatest(this.state.rollbackFloor, saved.rollbackFloor);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch { /* Keep the known state if local storage is temporarily unavailable. */ }
|
|
147
|
+
this.changed$.next();
|
|
148
|
+
}
|
|
149
|
+
/** Under the shared lock, so a second window never observes our installer's intermediate files. */
|
|
150
|
+
reconcileDisk() {
|
|
151
|
+
if (!this.env.diskVersion) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const disk = this.env.diskVersion();
|
|
155
|
+
if (disk !== null && !releaseVersion(disk)) {
|
|
156
|
+
throw new Error('The installed package version cannot be verified. Use Tabby Plugins.');
|
|
157
|
+
}
|
|
158
|
+
if (disk === null) {
|
|
159
|
+
this.state.removed = true;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
if ((this.state.pending && this.state.pending !== disk) || this.state.removed) {
|
|
163
|
+
this.notice = `The package changed outside this page. Tabby is running ${this.running}; the package on disk is ${disk}.`;
|
|
164
|
+
this.state.previous = undefined;
|
|
165
|
+
}
|
|
166
|
+
this.state.pending = disk;
|
|
167
|
+
this.state.removed = false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
get pending() { return !this.removed && this.state.pending && this.state.pending !== this.running ? this.state.pending : null; }
|
|
171
|
+
get removed() { return this.state.removed === true; }
|
|
172
|
+
persist() {
|
|
173
|
+
try {
|
|
174
|
+
if (!this.env.storage) {
|
|
175
|
+
throw new Error('Local storage unavailable');
|
|
176
|
+
}
|
|
177
|
+
this.env.storage.setItem(exports.RELEASE_STATE_KEY, JSON.stringify(this.state));
|
|
178
|
+
this.storageWarning = '';
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
this.storageWarning = 'Version preferences could not be saved on this computer. They apply to this window only. Manage installations in Tabby Plugins.';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async preference(change) {
|
|
185
|
+
if (this.busy) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
await this.ready;
|
|
189
|
+
try {
|
|
190
|
+
await this.lock(async () => {
|
|
191
|
+
this.syncFromStorage();
|
|
192
|
+
Object.assign(this.state, change);
|
|
193
|
+
this.persist();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
this.error = this.message(e);
|
|
198
|
+
}
|
|
199
|
+
this.changed$.next();
|
|
200
|
+
}
|
|
201
|
+
async setChannel(value) {
|
|
202
|
+
if (value === 'stable' || value === 'preview') {
|
|
203
|
+
await this.preference({ channel: value });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async setPaused(paused) { await this.preference({ paused }); }
|
|
207
|
+
get recommended() {
|
|
208
|
+
const stable = this.catalog?.stable ?? null;
|
|
209
|
+
const preview = this.state.channel === 'preview' ? this.catalog?.preview : null;
|
|
210
|
+
return preview && (!stable || newer(preview, stable)) ? preview : stable;
|
|
211
|
+
}
|
|
212
|
+
get available() {
|
|
213
|
+
const version = this.recommended;
|
|
214
|
+
return !this.state.paused && !this.catalogError && !this.pending && !this.removed && version && newer(version, this.running) && !this.reason(version) ? version : null;
|
|
215
|
+
}
|
|
216
|
+
get versions() {
|
|
217
|
+
return (this.catalog?.versions ?? []).filter(p => this.state.channel === 'preview' || !semver.prerelease(p.version));
|
|
218
|
+
}
|
|
219
|
+
/** A reason to refuse a version, independent of whether it is newer. Return-to-stable may downgrade. */
|
|
220
|
+
reason(version) {
|
|
221
|
+
if (!releaseVersion(version)) {
|
|
222
|
+
return 'Invalid version.';
|
|
223
|
+
}
|
|
224
|
+
const p = this.catalog?.versions.find(x => x.version === version);
|
|
225
|
+
if (!p) {
|
|
226
|
+
return 'This version is not in the published version list.';
|
|
227
|
+
}
|
|
228
|
+
if (p.deprecated) {
|
|
229
|
+
return 'This version has been withdrawn from recommendations.';
|
|
230
|
+
}
|
|
231
|
+
if (semver.lt(version, this.state.rollbackFloor)) {
|
|
232
|
+
return `Saved data requires version ${this.state.rollbackFloor} or newer.`;
|
|
233
|
+
}
|
|
234
|
+
if (!p.policy) {
|
|
235
|
+
return 'Compatibility has not been confirmed for this version.';
|
|
236
|
+
}
|
|
237
|
+
if (!semver.valid(this.env.tabby) || semver.lt(this.env.tabby, p.policy.minimumTabbyVersion)) {
|
|
238
|
+
return `Requires Tabby ${p.policy.minimumTabbyVersion} or newer.`;
|
|
239
|
+
}
|
|
240
|
+
if (p.node && (!semver.validRange(p.node) || !semver.satisfies(this.env.node, p.node, { includePrerelease: true }))) {
|
|
241
|
+
return 'Requires a different Node.js version. Update Tabby first.';
|
|
242
|
+
}
|
|
243
|
+
const supports = (list, value) => !list?.length ||
|
|
244
|
+
(!list.includes(`!${value}`) && (list.includes('any') || !list.some(x => !x.startsWith('!')) || list.includes(value)));
|
|
245
|
+
if (!supports(p.os, this.env.os) || !supports(p.cpu, this.env.cpu)) {
|
|
246
|
+
return 'Not available for this computer.';
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
/** One shared request; ordinary reads cache for 15 minutes, explicit checks retry at most every 10 seconds. */
|
|
251
|
+
async refreshCatalog(force = false) {
|
|
252
|
+
if (this.inFlight) {
|
|
253
|
+
return this.inFlight;
|
|
254
|
+
}
|
|
255
|
+
const elapsed = this.now() - this.lastAttempt;
|
|
256
|
+
if (elapsed < (force ? 10000 : CACHE_MS)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
this.lastAttempt = this.now();
|
|
260
|
+
this.checking = true;
|
|
261
|
+
this.error = '';
|
|
262
|
+
this.changed$.next();
|
|
263
|
+
this.inFlight = (async () => {
|
|
264
|
+
try {
|
|
265
|
+
this.catalog = await Promise.resolve().then(() => this.env.fetchCatalog());
|
|
266
|
+
this.catalogError = '';
|
|
267
|
+
this.checkedAt = this.now();
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
this.error = this.catalogError = `Could not check published versions. ${e instanceof Error ? e.message : 'Try again later.'}`;
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
this.checking = false;
|
|
274
|
+
this.inFlight = undefined;
|
|
275
|
+
this.changed$.next();
|
|
276
|
+
}
|
|
277
|
+
})();
|
|
278
|
+
return this.inFlight;
|
|
279
|
+
}
|
|
280
|
+
async install(version, returnToStable = false) {
|
|
281
|
+
if (this.busy) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.busy = true;
|
|
285
|
+
this.error = '';
|
|
286
|
+
this.changed$.next();
|
|
287
|
+
const progress = (phase, message) => this.env.progress?.({ action: 'install', phase, version, message });
|
|
288
|
+
try {
|
|
289
|
+
progress('preparing');
|
|
290
|
+
await this.ready;
|
|
291
|
+
await this.lock(async () => {
|
|
292
|
+
this.syncFromStorage();
|
|
293
|
+
this.reconcileDisk();
|
|
294
|
+
this.persist();
|
|
295
|
+
if (this.storageWarning) {
|
|
296
|
+
throw new Error('Version history cannot be saved. Use Tabby Plugins to install.');
|
|
297
|
+
}
|
|
298
|
+
if (this.pending) {
|
|
299
|
+
progress('success', `tabby-rdp ${this.pending} is already installed. Restart Tabby to use it.`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (this.removed) {
|
|
303
|
+
throw new Error('The plugin has been uninstalled. Restart Tabby before changing versions.');
|
|
304
|
+
}
|
|
305
|
+
await this.refreshCatalog();
|
|
306
|
+
if (this.catalogError) {
|
|
307
|
+
throw new Error('Check published versions successfully before installing.');
|
|
308
|
+
}
|
|
309
|
+
const reason = this.reason(version);
|
|
310
|
+
if (reason) {
|
|
311
|
+
throw new Error(reason);
|
|
312
|
+
}
|
|
313
|
+
const targetFloor = this.catalog.versions.find(p => p.version === version).policy.rollbackFloor;
|
|
314
|
+
if (version === this.running) {
|
|
315
|
+
progress('success', `tabby-rdp ${version} is already running.`);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (semver.prerelease(version) && this.state.channel !== 'preview') {
|
|
319
|
+
throw new Error('Choose Preview before installing a preview version.');
|
|
320
|
+
}
|
|
321
|
+
if (returnToStable && version !== this.catalog?.stable) {
|
|
322
|
+
throw new Error('Check the current stable version again.');
|
|
323
|
+
}
|
|
324
|
+
const downgrade = semver.lt(version, this.running);
|
|
325
|
+
if (!await this.env.confirm(`Install tabby-rdp ${version}?`, `Currently running ${this.running}. ${downgrade ? 'This is a downgrade. ' : ''}Close remote desktops and restart Tabby after installation.`, 'Install')) {
|
|
326
|
+
progress('cancelled');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
progress('installing');
|
|
330
|
+
await this.env.install('tabby-rdp', version);
|
|
331
|
+
progress('verifying');
|
|
332
|
+
if (this.env.diskVersion) {
|
|
333
|
+
const installed = this.env.diskVersion();
|
|
334
|
+
if (installed !== version) {
|
|
335
|
+
throw new Error(installed === null ? 'The installed plugin package is missing.' :
|
|
336
|
+
`The installed version is ${installed}; expected ${version}.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// History is committed only after the supported installer succeeds.
|
|
340
|
+
this.state.previous = this.running;
|
|
341
|
+
this.state.pending = version;
|
|
342
|
+
this.state.removed = false;
|
|
343
|
+
// Record the target's data boundary before restart, while older windows may still be running.
|
|
344
|
+
this.state.rollbackFloor = greatest(this.state.rollbackFloor, targetFloor);
|
|
345
|
+
if (returnToStable) {
|
|
346
|
+
this.state.channel = 'stable';
|
|
347
|
+
}
|
|
348
|
+
this.persist();
|
|
349
|
+
progress('success');
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
catch (e) {
|
|
353
|
+
this.error = `Installation did not complete. ${this.message(e)}`;
|
|
354
|
+
progress('error', this.error);
|
|
355
|
+
}
|
|
356
|
+
finally {
|
|
357
|
+
this.busy = false;
|
|
358
|
+
this.changed$.next();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async uninstall() {
|
|
362
|
+
if (this.busy) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
this.busy = true;
|
|
366
|
+
this.error = '';
|
|
367
|
+
this.changed$.next();
|
|
368
|
+
const progress = (phase, message) => this.env.progress?.({ action: 'uninstall', phase, message });
|
|
369
|
+
try {
|
|
370
|
+
progress('preparing');
|
|
371
|
+
await this.ready;
|
|
372
|
+
await this.lock(async () => {
|
|
373
|
+
this.syncFromStorage();
|
|
374
|
+
this.reconcileDisk();
|
|
375
|
+
this.persist();
|
|
376
|
+
if (this.storageWarning) {
|
|
377
|
+
throw new Error('Version history cannot be saved. Use Tabby Plugins to uninstall.');
|
|
378
|
+
}
|
|
379
|
+
if (this.pending) {
|
|
380
|
+
throw new Error('A version change is pending. Restart Tabby before uninstalling.');
|
|
381
|
+
}
|
|
382
|
+
if (this.removed) {
|
|
383
|
+
progress('success', 'tabby-rdp is already uninstalled. Restart Tabby to finish.');
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (!await this.env.confirm('Uninstall tabby-rdp?', 'Close remote desktops before uninstalling, then restart Tabby. Saved connections, settings, passwords and remote setup are kept.', 'Uninstall')) {
|
|
387
|
+
progress('cancelled');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
progress('uninstalling');
|
|
391
|
+
await this.env.uninstall('tabby-rdp');
|
|
392
|
+
progress('verifying');
|
|
393
|
+
if (this.env.diskVersion && this.env.diskVersion() !== null) {
|
|
394
|
+
throw new Error('The plugin package is still installed.');
|
|
395
|
+
}
|
|
396
|
+
this.state.removed = true;
|
|
397
|
+
this.persist();
|
|
398
|
+
progress('success');
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
catch (e) {
|
|
402
|
+
this.error = `Uninstall did not complete. ${this.message(e)}`;
|
|
403
|
+
progress('error', this.error);
|
|
404
|
+
}
|
|
405
|
+
finally {
|
|
406
|
+
this.busy = false;
|
|
407
|
+
this.changed$.next();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
exports.ReleaseManager = ReleaseManager;
|