@adecore/shell 0.49.3 → 0.49.4

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/dist/updater.d.ts CHANGED
@@ -21,6 +21,7 @@ export declare const createUpdater: (options: UpdaterOptions) => {
21
21
  start: () => void;
22
22
  configure: (autoDownload: unknown) => void;
23
23
  check: () => Promise<void>;
24
+ setAllowPrerelease: (allow: boolean) => void;
24
25
  download: () => Promise<void>;
25
26
  install: () => boolean;
26
27
  };
package/dist/updater.js CHANGED
@@ -26,6 +26,10 @@ export const createUpdater = (options) => {
26
26
  let updater = null;
27
27
  let timer = null;
28
28
  let installing = false;
29
+ let allowPrerelease = null;
30
+ // What stops the download a check offered, also one it started by itself.
31
+ let download = null;
32
+ let checkAgain = false;
29
33
  const setState = (patch) => {
30
34
  state = { ...state, ...patch };
31
35
  options.publish(state);
@@ -36,6 +40,16 @@ export const createUpdater = (options) => {
36
40
  options.onQuit?.(false);
37
41
  }
38
42
  };
43
+ const refused = (version) => allowPrerelease === false && version?.includes('-') === true;
44
+ /* A prerelease the person no longer follows: nothing may finish it or install it, also not a quit, which installs what is downloaded. */
45
+ const discard = () => {
46
+ download?.cancel();
47
+ download = null;
48
+ if (updater) {
49
+ updater.autoInstallOnAppQuit = false;
50
+ }
51
+ setState({ status: 'idle', version: undefined, percent: undefined, error: null, errorCode: null });
52
+ };
39
53
  const check = async () => {
40
54
  // Nothing to learn while a check or a download runs, or while a build waits to be installed.
41
55
  if (!updater || state.status === 'checking' || state.status === 'downloading' || state.status === 'ready') {
@@ -43,12 +57,22 @@ export const createUpdater = (options) => {
43
57
  }
44
58
  options.beforeCheck?.();
45
59
  try {
46
- await updater.checkForUpdates();
60
+ const result = await updater.checkForUpdates();
61
+ download = result?.cancellationToken ?? null;
62
+ // The track changed under this check, and with autoDownload its download already runs.
63
+ if (refused(result?.updateInfo.version)) {
64
+ download?.cancel();
65
+ download = null;
66
+ }
47
67
  }
48
68
  catch (e) {
49
69
  // checkForUpdates rejects as well as emitting `error`; the state is already set there.
50
70
  log('Update check failed', e);
51
71
  }
72
+ if (checkAgain) {
73
+ checkAgain = false;
74
+ await check();
75
+ }
52
76
  };
53
77
  return {
54
78
  state: () => state,
@@ -60,11 +84,33 @@ export const createUpdater = (options) => {
60
84
  const loaded = options.load();
61
85
  // The page sends its preference before the first check, so nothing downloads for someone who turned it off.
62
86
  loaded.autoDownload = false;
87
+ if (allowPrerelease !== null) {
88
+ loaded.allowPrerelease = allowPrerelease;
89
+ }
63
90
  loaded.on('checking-for-update', () => setState({ status: 'checking', error: null, errorCode: null }));
64
- loaded.on('update-available', (info) => setState({ status: 'available', version: info.version, error: null, errorCode: null }));
91
+ loaded.on('update-available', (info) => {
92
+ if (refused(info.version)) {
93
+ discard();
94
+ return;
95
+ }
96
+ setState({ status: 'available', version: info.version, error: null, errorCode: null });
97
+ });
65
98
  loaded.on('update-not-available', () => setState({ status: 'current', version: undefined, error: null, errorCode: null }));
66
- loaded.on('download-progress', (progress) => setState({ status: 'downloading', percent: progress.percent }));
67
- loaded.on('update-downloaded', (info) => setState({ status: 'ready', version: info.version, percent: 100 }));
99
+ loaded.on('download-progress', (progress) => {
100
+ // What is left of a download `discard` stopped, which has no version to tell it by.
101
+ if (state.version !== undefined) {
102
+ setState({ status: 'downloading', percent: progress.percent });
103
+ }
104
+ });
105
+ loaded.on('update-downloaded', (info) => {
106
+ if (refused(info.version)) {
107
+ discard();
108
+ return;
109
+ }
110
+ loaded.autoInstallOnAppQuit = true;
111
+ download = null;
112
+ setState({ status: 'ready', version: info.version, percent: 100 });
113
+ });
68
114
  loaded.on('error', (e) => {
69
115
  installFailed();
70
116
  const { code, detail } = describeUpdateError(e.message);
@@ -87,12 +133,30 @@ export const createUpdater = (options) => {
87
133
  timer ??= every(() => void check(), UPDATE_INTERVAL_MS);
88
134
  },
89
135
  check,
136
+ /*
137
+ * Which releases the updater offers. Without this call it follows the running version. Leaving
138
+ * prereleases also drops one that is offered, downloading or waiting to install; a check that runs
139
+ * under the old answer is asked again once it ends.
140
+ */
141
+ setAllowPrerelease: (allow) => {
142
+ allowPrerelease = allow;
143
+ if (!updater) {
144
+ return;
145
+ }
146
+ updater.allowPrerelease = allow;
147
+ if (refused(state.version)) {
148
+ discard();
149
+ }
150
+ else if (state.status === 'checking') {
151
+ checkAgain = true;
152
+ }
153
+ },
90
154
  download: async () => {
91
155
  if (!updater) {
92
156
  return;
93
157
  }
94
158
  try {
95
- await updater.downloadUpdate();
159
+ await updater.downloadUpdate(download ?? undefined);
96
160
  }
97
161
  catch (e) {
98
162
  log('Update download failed', e);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/shell",
3
- "version": "0.49.3",
3
+ "version": "0.49.4",
4
4
  "description": "The main process of an Electron app: its menu, its updater and the guards around its page.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "Bas Milius",
package/src/updater.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AppUpdater } from 'electron-updater';
1
+ import type { AppUpdater, UpdateCheckResult } from 'electron-updater';
2
2
  import type { UpdateErrorCode, UpdateState } from './bridge/update.ts';
3
3
 
4
4
  /* Often enough that a release lands the same day, rarely enough to be invisible. */
@@ -55,6 +55,10 @@ export const createUpdater = (options: UpdaterOptions) => {
55
55
  let updater: AppUpdater | null = null;
56
56
  let timer: unknown = null;
57
57
  let installing = false;
58
+ let allowPrerelease: boolean | null = null;
59
+ // What stops the download a check offered, also one it started by itself.
60
+ let download: UpdateCheckResult['cancellationToken'] | null = null;
61
+ let checkAgain = false;
58
62
 
59
63
  const setState = (patch: Partial<UpdateState>): void => {
60
64
  state = { ...state, ...patch };
@@ -68,6 +72,20 @@ export const createUpdater = (options: UpdaterOptions) => {
68
72
  }
69
73
  };
70
74
 
75
+ const refused = (version: string | undefined): boolean => allowPrerelease === false && version?.includes('-') === true;
76
+
77
+ /* A prerelease the person no longer follows: nothing may finish it or install it, also not a quit, which installs what is downloaded. */
78
+ const discard = (): void => {
79
+ download?.cancel();
80
+ download = null;
81
+
82
+ if (updater) {
83
+ updater.autoInstallOnAppQuit = false;
84
+ }
85
+
86
+ setState({ status: 'idle', version: undefined, percent: undefined, error: null, errorCode: null });
87
+ };
88
+
71
89
  const check = async (): Promise<void> => {
72
90
  // Nothing to learn while a check or a download runs, or while a build waits to be installed.
73
91
  if (!updater || state.status === 'checking' || state.status === 'downloading' || state.status === 'ready') {
@@ -77,11 +95,23 @@ export const createUpdater = (options: UpdaterOptions) => {
77
95
  options.beforeCheck?.();
78
96
 
79
97
  try {
80
- await updater.checkForUpdates();
98
+ const result = await updater.checkForUpdates();
99
+ download = result?.cancellationToken ?? null;
100
+
101
+ // The track changed under this check, and with autoDownload its download already runs.
102
+ if (refused(result?.updateInfo.version)) {
103
+ download?.cancel();
104
+ download = null;
105
+ }
81
106
  } catch (e) {
82
107
  // checkForUpdates rejects as well as emitting `error`; the state is already set there.
83
108
  log('Update check failed', e);
84
109
  }
110
+
111
+ if (checkAgain) {
112
+ checkAgain = false;
113
+ await check();
114
+ }
85
115
  };
86
116
 
87
117
  return {
@@ -96,11 +126,43 @@ export const createUpdater = (options: UpdaterOptions) => {
96
126
  const loaded = options.load();
97
127
  // The page sends its preference before the first check, so nothing downloads for someone who turned it off.
98
128
  loaded.autoDownload = false;
129
+
130
+ if (allowPrerelease !== null) {
131
+ loaded.allowPrerelease = allowPrerelease;
132
+ }
133
+
99
134
  loaded.on('checking-for-update', () => setState({ status: 'checking', error: null, errorCode: null }));
100
- loaded.on('update-available', (info) => setState({ status: 'available', version: info.version, error: null, errorCode: null }));
135
+
136
+ loaded.on('update-available', (info) => {
137
+ if (refused(info.version)) {
138
+ discard();
139
+
140
+ return;
141
+ }
142
+
143
+ setState({ status: 'available', version: info.version, error: null, errorCode: null });
144
+ });
145
+
101
146
  loaded.on('update-not-available', () => setState({ status: 'current', version: undefined, error: null, errorCode: null }));
102
- loaded.on('download-progress', (progress) => setState({ status: 'downloading', percent: progress.percent }));
103
- loaded.on('update-downloaded', (info) => setState({ status: 'ready', version: info.version, percent: 100 }));
147
+
148
+ loaded.on('download-progress', (progress) => {
149
+ // What is left of a download `discard` stopped, which has no version to tell it by.
150
+ if (state.version !== undefined) {
151
+ setState({ status: 'downloading', percent: progress.percent });
152
+ }
153
+ });
154
+
155
+ loaded.on('update-downloaded', (info) => {
156
+ if (refused(info.version)) {
157
+ discard();
158
+
159
+ return;
160
+ }
161
+
162
+ loaded.autoInstallOnAppQuit = true;
163
+ download = null;
164
+ setState({ status: 'ready', version: info.version, percent: 100 });
165
+ });
104
166
 
105
167
  loaded.on('error', (e) => {
106
168
  installFailed();
@@ -128,13 +190,34 @@ export const createUpdater = (options: UpdaterOptions) => {
128
190
 
129
191
  check,
130
192
 
193
+ /*
194
+ * Which releases the updater offers. Without this call it follows the running version. Leaving
195
+ * prereleases also drops one that is offered, downloading or waiting to install; a check that runs
196
+ * under the old answer is asked again once it ends.
197
+ */
198
+ setAllowPrerelease: (allow: boolean): void => {
199
+ allowPrerelease = allow;
200
+
201
+ if (!updater) {
202
+ return;
203
+ }
204
+
205
+ updater.allowPrerelease = allow;
206
+
207
+ if (refused(state.version)) {
208
+ discard();
209
+ } else if (state.status === 'checking') {
210
+ checkAgain = true;
211
+ }
212
+ },
213
+
131
214
  download: async (): Promise<void> => {
132
215
  if (!updater) {
133
216
  return;
134
217
  }
135
218
 
136
219
  try {
137
- await updater.downloadUpdate();
220
+ await updater.downloadUpdate(download ?? undefined);
138
221
  } catch (e) {
139
222
  log('Update download failed', e);
140
223
  }