@tutti-os/desktop-update-admission 0.0.266

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +112 -0
  3. package/dist/chunk-GONYAEKF.js +139 -0
  4. package/dist/chunk-GONYAEKF.js.map +1 -0
  5. package/dist/chunk-LV67FBCG.js +112 -0
  6. package/dist/chunk-LV67FBCG.js.map +1 -0
  7. package/dist/chunk-TRALK3GT.js +33 -0
  8. package/dist/chunk-TRALK3GT.js.map +1 -0
  9. package/dist/chunk-X4U7GODT.js +134 -0
  10. package/dist/chunk-X4U7GODT.js.map +1 -0
  11. package/dist/chunk-ZQTIYDGZ.js +603 -0
  12. package/dist/chunk-ZQTIYDGZ.js.map +1 -0
  13. package/dist/contracts/index.d.ts +97 -0
  14. package/dist/contracts/index.js +15 -0
  15. package/dist/contracts/index.js.map +1 -0
  16. package/dist/core/index.d.ts +19 -0
  17. package/dist/core/index.js +13 -0
  18. package/dist/core/index.js.map +1 -0
  19. package/dist/development/index.d.ts +133 -0
  20. package/dist/development/index.js +289 -0
  21. package/dist/development/index.js.map +1 -0
  22. package/dist/development/mock-server-cli.d.ts +1 -0
  23. package/dist/development/mock-server-cli.js +67 -0
  24. package/dist/development/mock-server-cli.js.map +1 -0
  25. package/dist/electron-main/index.d.ts +38 -0
  26. package/dist/electron-main/index.js +498 -0
  27. package/dist/electron-main/index.js.map +1 -0
  28. package/dist/i18n/index.d.ts +64 -0
  29. package/dist/i18n/index.js +74 -0
  30. package/dist/i18n/index.js.map +1 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +25 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/mandatory-updater/index.d.ts +23 -0
  35. package/dist/mandatory-updater/index.js +10 -0
  36. package/dist/mandatory-updater/index.js.map +1 -0
  37. package/dist/preload/index.d.ts +9 -0
  38. package/dist/preload/index.js +30 -0
  39. package/dist/preload/index.js.map +1 -0
  40. package/dist/react/index.d.ts +13 -0
  41. package/dist/react/index.js +116 -0
  42. package/dist/react/index.js.map +1 -0
  43. package/package.json +98 -0
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveDesktopUpdateDevelopmentPolicyScenario,
4
+ startDesktopUpdateDevelopmentMockServer
5
+ } from "../chunk-ZQTIYDGZ.js";
6
+ import "../chunk-LV67FBCG.js";
7
+
8
+ // src/development/mockServerCli.ts
9
+ function log(level, details) {
10
+ const output = `[minimum-version-check] ${JSON.stringify(details)}`;
11
+ if (level === "error") {
12
+ process.stderr.write(`${output}
13
+ `);
14
+ } else {
15
+ process.stdout.write(`${output}
16
+ `);
17
+ }
18
+ }
19
+ async function main() {
20
+ const policy = resolveDesktopUpdateDevelopmentPolicyScenario({
21
+ env: process.env
22
+ });
23
+ if (!policy) {
24
+ throw new Error(
25
+ "DESKTOP_UPDATE_ADMISSION_DEV=1 is required to start the mock server"
26
+ );
27
+ }
28
+ const rawPort = process.env.DESKTOP_UPDATE_ADMISSION_MOCK_SERVER_PORT?.trim();
29
+ const port = rawPort ? Number(rawPort) : void 0;
30
+ const server = await startDesktopUpdateDevelopmentMockServer({
31
+ port,
32
+ policy
33
+ });
34
+ log("info", {
35
+ baseUrl: server.baseUrl,
36
+ result: "listening",
37
+ stage: "development-mock-server"
38
+ });
39
+ let closing = false;
40
+ const close = async (signal) => {
41
+ if (closing) {
42
+ return;
43
+ }
44
+ closing = true;
45
+ await server.close();
46
+ log("info", {
47
+ result: "stopped",
48
+ signal,
49
+ stage: "development-mock-server"
50
+ });
51
+ };
52
+ process.once("SIGINT", () => {
53
+ void close("SIGINT");
54
+ });
55
+ process.once("SIGTERM", () => {
56
+ void close("SIGTERM");
57
+ });
58
+ }
59
+ void main().catch((error) => {
60
+ log("error", {
61
+ error: error instanceof Error ? error.message : String(error),
62
+ result: "failure",
63
+ stage: "development-mock-server"
64
+ });
65
+ process.exitCode = 1;
66
+ });
67
+ //# sourceMappingURL=mock-server-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/development/mockServerCli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { resolveDesktopUpdateDevelopmentPolicyScenario } from \"./policyScenario.ts\";\nimport { startDesktopUpdateDevelopmentMockServer } from \"./mockServer.ts\";\n\nfunction log(level: \"info\" | \"error\", details: Record<string, unknown>): void {\n const output = `[minimum-version-check] ${JSON.stringify(details)}`;\n if (level === \"error\") {\n process.stderr.write(`${output}\\n`);\n } else {\n process.stdout.write(`${output}\\n`);\n }\n}\n\nasync function main(): Promise<void> {\n const policy = resolveDesktopUpdateDevelopmentPolicyScenario({\n env: process.env\n });\n if (!policy) {\n throw new Error(\n \"DESKTOP_UPDATE_ADMISSION_DEV=1 is required to start the mock server\"\n );\n }\n const rawPort = process.env.DESKTOP_UPDATE_ADMISSION_MOCK_SERVER_PORT?.trim();\n const port = rawPort ? Number(rawPort) : undefined;\n const server = await startDesktopUpdateDevelopmentMockServer({\n port,\n policy\n });\n log(\"info\", {\n baseUrl: server.baseUrl,\n result: \"listening\",\n stage: \"development-mock-server\"\n });\n let closing = false;\n const close = async (signal: string): Promise<void> => {\n if (closing) {\n return;\n }\n closing = true;\n await server.close();\n log(\"info\", {\n result: \"stopped\",\n signal,\n stage: \"development-mock-server\"\n });\n };\n process.once(\"SIGINT\", () => {\n void close(\"SIGINT\");\n });\n process.once(\"SIGTERM\", () => {\n void close(\"SIGTERM\");\n });\n}\n\nvoid main().catch((error) => {\n log(\"error\", {\n error: error instanceof Error ? error.message : String(error),\n result: \"failure\",\n stage: \"development-mock-server\"\n });\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;;AAKA,SAAS,IAAI,OAAyB,SAAwC;AAC5E,QAAM,SAAS,2BAA2B,KAAK,UAAU,OAAO,CAAC;AACjE,MAAI,UAAU,SAAS;AACrB,YAAQ,OAAO,MAAM,GAAG,MAAM;AAAA,CAAI;AAAA,EACpC,OAAO;AACL,YAAQ,OAAO,MAAM,GAAG,MAAM;AAAA,CAAI;AAAA,EACpC;AACF;AAEA,eAAe,OAAsB;AACnC,QAAM,SAAS,8CAA8C;AAAA,IAC3D,KAAK,QAAQ;AAAA,EACf,CAAC;AACD,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,QAAQ,IAAI,2CAA2C,KAAK;AAC5E,QAAM,OAAO,UAAU,OAAO,OAAO,IAAI;AACzC,QAAM,SAAS,MAAM,wCAAwC;AAAA,IAC3D;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,QAAQ;AAAA,IACV,SAAS,OAAO;AAAA,IAChB,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,CAAC;AACD,MAAI,UAAU;AACd,QAAM,QAAQ,OAAO,WAAkC;AACrD,QAAI,SAAS;AACX;AAAA,IACF;AACA,cAAU;AACV,UAAM,OAAO,MAAM;AACnB,QAAI,QAAQ;AAAA,MACV,QAAQ;AAAA,MACR;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,UAAQ,KAAK,UAAU,MAAM;AAC3B,SAAK,MAAM,QAAQ;AAAA,EACrB,CAAC;AACD,UAAQ,KAAK,WAAW,MAAM;AAC5B,SAAK,MAAM,SAAS;AAAA,EACtB,CAAC;AACH;AAEA,KAAK,KAAK,EAAE,MAAM,CAAC,UAAU;AAC3B,MAAI,SAAS;AAAA,IACX,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC5D,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,CAAC;AACD,UAAQ,WAAW;AACrB,CAAC;","names":[]}
@@ -0,0 +1,38 @@
1
+ import * as electron from 'electron';
2
+ import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron';
3
+ import { DesktopProduct, DesktopUpdateAdmissionRuntime, MinimumVersionCheckRequest, MinimumVersionAppUpdateService, MinimumVersionCheckResponse } from '../contracts/index.js';
4
+
5
+ interface DesktopUpdateAdmissionElectronRuntime {
6
+ app: typeof electron.app;
7
+ BrowserWindow: typeof electron.BrowserWindow;
8
+ ipcMain: typeof electron.ipcMain;
9
+ shell: typeof electron.shell;
10
+ }
11
+ interface DesktopUpdateAdmissionLogger {
12
+ info(message: string): void;
13
+ error(message: string): void;
14
+ }
15
+ interface DesktopUpdateAdmissionController {
16
+ runStartupCheck(): Promise<boolean>;
17
+ checkAfterForegroundRestore(): Promise<void>;
18
+ dispose(): void;
19
+ }
20
+ interface DesktopUpdateAdmissionControllerOptions<TProduct extends DesktopProduct> {
21
+ product: TProduct;
22
+ runtime: DesktopUpdateAdmissionRuntime;
23
+ electron: DesktopUpdateAdmissionElectronRuntime;
24
+ checkMinimumVersion(request: MinimumVersionCheckRequest<TProduct>, signal: AbortSignal): Promise<unknown>;
25
+ updateService: MinimumVersionAppUpdateService;
26
+ logger: DesktopUpdateAdmissionLogger;
27
+ onPolicyReleased(): void | Promise<void>;
28
+ preloadPath: string;
29
+ rendererFilePath: string;
30
+ rendererUrl?: string;
31
+ manualDownloadUrl(response: MinimumVersionCheckResponse<TProduct>): string;
32
+ listBusinessWindows(): BrowserWindow[];
33
+ icon?: BrowserWindowConstructorOptions["icon"];
34
+ now?: () => number;
35
+ }
36
+ declare function createDesktopUpdateAdmissionController<TProduct extends DesktopProduct>(options: DesktopUpdateAdmissionControllerOptions<TProduct>): DesktopUpdateAdmissionController;
37
+
38
+ export { type DesktopUpdateAdmissionController, type DesktopUpdateAdmissionControllerOptions, type DesktopUpdateAdmissionLogger, createDesktopUpdateAdmissionController };
@@ -0,0 +1,498 @@
1
+ import {
2
+ desktopUpdateAdmissionIpcChannels
3
+ } from "../chunk-TRALK3GT.js";
4
+ import {
5
+ DevelopmentInstallSuppressedError
6
+ } from "../chunk-X4U7GODT.js";
7
+ import {
8
+ MandatoryUpdateTargetError
9
+ } from "../chunk-GONYAEKF.js";
10
+ import {
11
+ resolveMinimumVersionRuntimeTarget,
12
+ shouldCheckMinimumVersionAfterForeground,
13
+ validateMinimumVersionResponse
14
+ } from "../chunk-LV67FBCG.js";
15
+
16
+ // src/electron-main/createDesktopUpdateAdmissionController.ts
17
+ var startupCheckTimeoutMs = 3e3;
18
+ var foregroundCheckTimeoutMs = 1e4;
19
+ function logMinimumVersionCheck(logger, level, details) {
20
+ logger[level](`[minimum-version-check] ${JSON.stringify(details)}`);
21
+ }
22
+ function createDesktopUpdateAdmissionController(options) {
23
+ const { app, BrowserWindow, ipcMain, shell } = options.electron;
24
+ const now = options.now ?? Date.now;
25
+ let lastCheckAt = 0;
26
+ let foregroundPrompted = false;
27
+ let state = null;
28
+ let upgradeWindow = null;
29
+ let mode = "startup";
30
+ let forcedFlowStarted = false;
31
+ let installRequested = false;
32
+ let disposed = false;
33
+ let activeCheck = null;
34
+ let activeForcedFlow = null;
35
+ let appQuitStarted = false;
36
+ let mandatoryUpdateSession = null;
37
+ let isolatedBusinessWindows = null;
38
+ const lifecycleAbort = new AbortController();
39
+ const handleBeforeQuit = () => {
40
+ appQuitStarted = true;
41
+ };
42
+ app.on("before-quit", handleBeforeQuit);
43
+ const emitState = () => {
44
+ if (state && upgradeWindow && !upgradeWindow.isDestroyed()) {
45
+ upgradeWindow.webContents.send(
46
+ desktopUpdateAdmissionIpcChannels.state,
47
+ state
48
+ );
49
+ }
50
+ };
51
+ const applyState = (phase, update = options.updateService.getState(), message = null) => {
52
+ if (!state) {
53
+ return;
54
+ }
55
+ state = { ...state, message, phase, update };
56
+ emitState();
57
+ };
58
+ const closeUpgradeWindow = () => {
59
+ const currentWindow = upgradeWindow;
60
+ upgradeWindow = null;
61
+ if (currentWindow && !currentWindow.isDestroyed()) {
62
+ currentWindow.destroy();
63
+ }
64
+ };
65
+ const isolateBusinessWindows = () => {
66
+ if (isolatedBusinessWindows) {
67
+ return;
68
+ }
69
+ isolatedBusinessWindows = options.listBusinessWindows().filter(
70
+ (candidate) => candidate !== upgradeWindow && !candidate.isDestroyed()
71
+ ).map((candidate) => ({
72
+ wasFocused: candidate.isFocused(),
73
+ wasMinimized: candidate.isMinimized(),
74
+ wasVisible: candidate.isVisible(),
75
+ window: candidate
76
+ }));
77
+ for (const snapshot of isolatedBusinessWindows) {
78
+ snapshot.window.hide();
79
+ }
80
+ };
81
+ const restoreBusinessWindows = () => {
82
+ const snapshots = isolatedBusinessWindows;
83
+ isolatedBusinessWindows = null;
84
+ if (!snapshots) {
85
+ return;
86
+ }
87
+ let focusedWindow = null;
88
+ for (const snapshot of snapshots) {
89
+ if (snapshot.window.isDestroyed()) {
90
+ continue;
91
+ }
92
+ if (snapshot.wasMinimized) {
93
+ snapshot.window.show();
94
+ snapshot.window.minimize();
95
+ } else if (snapshot.wasVisible) {
96
+ snapshot.window.show();
97
+ }
98
+ if (snapshot.wasFocused) {
99
+ focusedWindow = snapshot.window;
100
+ }
101
+ }
102
+ if (focusedWindow && !focusedWindow.isDestroyed() && !focusedWindow.isMinimized()) {
103
+ focusedWindow.focus();
104
+ }
105
+ };
106
+ const openUpgradeWindow = (nextMode) => {
107
+ if (upgradeWindow && !upgradeWindow.isDestroyed()) {
108
+ upgradeWindow.show();
109
+ upgradeWindow.focus();
110
+ return;
111
+ }
112
+ mode = nextMode;
113
+ const window = new BrowserWindow({
114
+ autoHideMenuBar: true,
115
+ fullscreenable: false,
116
+ height: 420,
117
+ icon: options.icon,
118
+ maximizable: false,
119
+ minHeight: 380,
120
+ minWidth: 480,
121
+ resizable: false,
122
+ show: false,
123
+ webPreferences: {
124
+ contextIsolation: true,
125
+ nodeIntegration: false,
126
+ preload: options.preloadPath,
127
+ sandbox: true
128
+ },
129
+ width: 520
130
+ });
131
+ upgradeWindow = window;
132
+ window.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
133
+ window.webContents.on("will-navigate", (event) => event.preventDefault());
134
+ window.on("close", (event) => {
135
+ if (!appQuitStarted && (mode === "startup" || forcedFlowStarted)) {
136
+ event.preventDefault();
137
+ app.quit();
138
+ }
139
+ });
140
+ window.on("closed", () => {
141
+ if (upgradeWindow === window) {
142
+ upgradeWindow = null;
143
+ }
144
+ });
145
+ window.once("ready-to-show", () => {
146
+ if (!window.isDestroyed()) {
147
+ window.show();
148
+ }
149
+ });
150
+ const search = `mode=${nextMode}`;
151
+ if (options.rendererUrl) {
152
+ void window.loadURL(`${options.rendererUrl}?${search}`);
153
+ } else {
154
+ void window.loadFile(options.rendererFilePath, { search });
155
+ }
156
+ };
157
+ const requestPayload = () => {
158
+ const target = resolveMinimumVersionRuntimeTarget(
159
+ process.platform,
160
+ process.arch
161
+ );
162
+ if (!target) {
163
+ return null;
164
+ }
165
+ return {
166
+ ...target,
167
+ currentVersion: options.runtime.currentVersion,
168
+ product: options.product
169
+ };
170
+ };
171
+ const runPolicyCheck = async (bounded) => {
172
+ const request = requestPayload();
173
+ if (!request) {
174
+ lastCheckAt = now();
175
+ logMinimumVersionCheck(options.logger, "info", {
176
+ architecture: process.arch,
177
+ decision: "notApplicable",
178
+ platform: process.platform,
179
+ reason: "unsupportedRuntime",
180
+ result: "success",
181
+ stage: bounded ? "startup" : "foreground"
182
+ });
183
+ return null;
184
+ }
185
+ const startedAt = now();
186
+ const controller = new AbortController();
187
+ const abortForLifecycle = () => controller.abort();
188
+ lifecycleAbort.signal.addEventListener("abort", abortForLifecycle, {
189
+ once: true
190
+ });
191
+ let timer = null;
192
+ try {
193
+ const timeoutMs = bounded ? startupCheckTimeoutMs : foregroundCheckTimeoutMs;
194
+ const response = await Promise.race([
195
+ options.checkMinimumVersion(request, controller.signal),
196
+ new Promise((_resolve, reject) => {
197
+ timer = setTimeout(() => {
198
+ controller.abort();
199
+ reject(new Error(`minimum version check exceeded ${timeoutMs}ms`));
200
+ }, timeoutMs);
201
+ })
202
+ ]);
203
+ const validated = validateMinimumVersionResponse(response, request);
204
+ lastCheckAt = now();
205
+ logMinimumVersionCheck(options.logger, "info", {
206
+ currentVersion: validated.currentVersion,
207
+ decision: validated.decision,
208
+ elapsedMs: now() - startedAt,
209
+ minimumVersion: validated.minimumVersion,
210
+ policyRevision: validated.policyRevision,
211
+ reason: validated.reason,
212
+ result: "success",
213
+ stage: bounded ? "startup" : "foreground"
214
+ });
215
+ return validated;
216
+ } catch (error) {
217
+ lastCheckAt = now();
218
+ logMinimumVersionCheck(options.logger, "error", {
219
+ elapsedMs: now() - startedAt,
220
+ error: error instanceof Error ? error.message : String(error),
221
+ result: "failure",
222
+ stage: bounded ? "startup" : "foreground"
223
+ });
224
+ return null;
225
+ } finally {
226
+ if (timer) {
227
+ clearTimeout(timer);
228
+ }
229
+ lifecycleAbort.signal.removeEventListener("abort", abortForLifecycle);
230
+ }
231
+ };
232
+ const checkPolicy = async (bounded) => {
233
+ if (activeCheck) {
234
+ return activeCheck;
235
+ }
236
+ const pendingCheck = runPolicyCheck(bounded);
237
+ activeCheck = pendingCheck;
238
+ try {
239
+ return await pendingCheck;
240
+ } finally {
241
+ if (activeCheck === pendingCheck) {
242
+ activeCheck = null;
243
+ }
244
+ }
245
+ };
246
+ const releaseMandatoryUpdater = async (restoreNormal = true) => {
247
+ const session = mandatoryUpdateSession;
248
+ mandatoryUpdateSession = null;
249
+ try {
250
+ await session?.release({ restoreNormal });
251
+ } catch (error) {
252
+ logMinimumVersionCheck(options.logger, "error", {
253
+ error: error instanceof Error ? error.message : String(error),
254
+ result: "failure",
255
+ stage: "normal-update-restore"
256
+ });
257
+ }
258
+ };
259
+ const releasePolicyBlock = async () => {
260
+ applyState("released");
261
+ forcedFlowStarted = false;
262
+ installRequested = false;
263
+ await releaseMandatoryUpdater();
264
+ closeUpgradeWindow();
265
+ restoreBusinessWindows();
266
+ await options.onPolicyReleased();
267
+ };
268
+ const installForcedUpdate = async () => {
269
+ if (installRequested) {
270
+ return;
271
+ }
272
+ installRequested = true;
273
+ try {
274
+ if (!mandatoryUpdateSession) {
275
+ throw new Error("mandatory update session is unavailable");
276
+ }
277
+ await mandatoryUpdateSession.installUpdate();
278
+ } catch (error) {
279
+ installRequested = false;
280
+ if (error instanceof DevelopmentInstallSuppressedError) {
281
+ logMinimumVersionCheck(options.logger, "info", {
282
+ result: "simulated",
283
+ stage: "install"
284
+ });
285
+ applyState("simulationComplete", options.updateService.getState());
286
+ return;
287
+ }
288
+ logMinimumVersionCheck(options.logger, "error", {
289
+ error: error instanceof Error ? error.message : String(error),
290
+ result: "failure",
291
+ stage: "install"
292
+ });
293
+ applyState("error", options.updateService.getState(), "installFailed");
294
+ }
295
+ };
296
+ const prepareAndDownloadForcedUpdate = async () => {
297
+ if (!state) {
298
+ return;
299
+ }
300
+ try {
301
+ forcedFlowStarted = true;
302
+ applyState("checking");
303
+ const target = {
304
+ channel: state.check.channel === "rc" ? "rc" : "stable",
305
+ minimumVersion: state.check.minimumVersion,
306
+ policyRevision: state.check.policyRevision
307
+ };
308
+ if (mandatoryUpdateSession) {
309
+ mandatoryUpdateSession.retarget(target);
310
+ } else {
311
+ const acquiredSession = await options.updateService.acquireMandatorySession(target);
312
+ if (disposed) {
313
+ await acquiredSession.release({ restoreNormal: false });
314
+ return;
315
+ }
316
+ mandatoryUpdateSession = acquiredSession;
317
+ }
318
+ const prepared = await mandatoryUpdateSession.prepare();
319
+ if (prepared.status === "downloaded") {
320
+ applyState("downloaded", prepared);
321
+ await installForcedUpdate();
322
+ return;
323
+ }
324
+ if (prepared.status !== "available") {
325
+ applyState("error", prepared, "updateUnavailable");
326
+ return;
327
+ }
328
+ applyState("ready", prepared);
329
+ const downloaded = await mandatoryUpdateSession.downloadUpdate();
330
+ if (downloaded.status !== "downloaded") {
331
+ applyState("error", downloaded, "updateFailed");
332
+ return;
333
+ }
334
+ applyState("downloaded", downloaded);
335
+ await installForcedUpdate();
336
+ } catch (error) {
337
+ logMinimumVersionCheck(options.logger, "error", {
338
+ error: error instanceof Error ? error.message : String(error),
339
+ result: "failure",
340
+ stage: "forced-update"
341
+ });
342
+ applyState(
343
+ "error",
344
+ options.updateService.getState(),
345
+ error instanceof MandatoryUpdateTargetError ? "releaseBelowMinimum" : "updateFailed"
346
+ );
347
+ }
348
+ };
349
+ const runForcedUpdateFlow = () => {
350
+ if (activeForcedFlow) {
351
+ return activeForcedFlow;
352
+ }
353
+ const pending = prepareAndDownloadForcedUpdate();
354
+ activeForcedFlow = pending;
355
+ void pending.then(
356
+ () => {
357
+ if (activeForcedFlow === pending) {
358
+ activeForcedFlow = null;
359
+ }
360
+ },
361
+ () => {
362
+ if (activeForcedFlow === pending) {
363
+ activeForcedFlow = null;
364
+ }
365
+ }
366
+ );
367
+ return pending;
368
+ };
369
+ const unsubscribeUpdate = options.updateService.subscribe((update) => {
370
+ if (!state || !forcedFlowStarted) {
371
+ return;
372
+ }
373
+ if (update.status === "downloading") {
374
+ applyState("downloading", update);
375
+ } else if (update.status === "error") {
376
+ installRequested = false;
377
+ applyState("error", update, "updateFailed");
378
+ }
379
+ });
380
+ const assertUpgradeWindowSender = (senderId) => {
381
+ if (!upgradeWindow || upgradeWindow.isDestroyed() || upgradeWindow.webContents.id !== senderId) {
382
+ throw new Error(
383
+ "desktop update admission IPC is restricted to the upgrade window"
384
+ );
385
+ }
386
+ };
387
+ ipcMain.handle(desktopUpdateAdmissionIpcChannels.getState, (event) => {
388
+ assertUpgradeWindowSender(event.sender.id);
389
+ return state;
390
+ });
391
+ ipcMain.handle(desktopUpdateAdmissionIpcChannels.start, async (event) => {
392
+ assertUpgradeWindowSender(event.sender.id);
393
+ if (mode === "foreground") {
394
+ isolateBusinessWindows();
395
+ }
396
+ await runForcedUpdateFlow();
397
+ return state;
398
+ });
399
+ ipcMain.handle(desktopUpdateAdmissionIpcChannels.retry, async (event) => {
400
+ assertUpgradeWindowSender(event.sender.id);
401
+ await activeForcedFlow;
402
+ const response = await checkPolicy(false);
403
+ if (!response) {
404
+ applyState(
405
+ "error",
406
+ options.updateService.getState(),
407
+ "policyCheckFailed"
408
+ );
409
+ } else if (response.decision !== "upgradeRequired") {
410
+ await releasePolicyBlock();
411
+ } else if (state) {
412
+ state = { ...state, check: response };
413
+ await runForcedUpdateFlow();
414
+ }
415
+ return state;
416
+ });
417
+ ipcMain.handle(desktopUpdateAdmissionIpcChannels.later, (event) => {
418
+ assertUpgradeWindowSender(event.sender.id);
419
+ if (mode === "foreground" && !forcedFlowStarted) {
420
+ closeUpgradeWindow();
421
+ }
422
+ });
423
+ ipcMain.handle(
424
+ desktopUpdateAdmissionIpcChannels.manualDownload,
425
+ async (event) => {
426
+ assertUpgradeWindowSender(event.sender.id);
427
+ if (!state) {
428
+ return;
429
+ }
430
+ await shell.openExternal(options.manualDownloadUrl(state.check));
431
+ }
432
+ );
433
+ ipcMain.handle(desktopUpdateAdmissionIpcChannels.exit, (event) => {
434
+ assertUpgradeWindowSender(event.sender.id);
435
+ app.quit();
436
+ });
437
+ return {
438
+ async runStartupCheck() {
439
+ if (!options.runtime.checksEnabled) {
440
+ return false;
441
+ }
442
+ const response = await checkPolicy(true);
443
+ if (!response || response.decision !== "upgradeRequired") {
444
+ return false;
445
+ }
446
+ state = {
447
+ check: response,
448
+ message: null,
449
+ phase: "blocked",
450
+ update: options.updateService.getState()
451
+ };
452
+ openUpgradeWindow("startup");
453
+ return true;
454
+ },
455
+ async checkAfterForegroundRestore() {
456
+ if (!shouldCheckMinimumVersionAfterForeground({
457
+ checksEnabled: options.runtime.checksEnabled,
458
+ disposed,
459
+ foregroundCheckIntervalMs: options.runtime.foregroundCheckIntervalMs,
460
+ foregroundPrompted,
461
+ lastCheckAt,
462
+ now: now(),
463
+ startupBlocked: mode === "startup" && upgradeWindow !== null && !upgradeWindow.isDestroyed()
464
+ })) {
465
+ return;
466
+ }
467
+ const response = await checkPolicy(false);
468
+ if (disposed || !response || response.decision !== "upgradeRequired") {
469
+ return;
470
+ }
471
+ foregroundPrompted = true;
472
+ state = {
473
+ check: response,
474
+ message: null,
475
+ phase: "blocked",
476
+ update: options.updateService.getState()
477
+ };
478
+ openUpgradeWindow("foreground");
479
+ },
480
+ dispose() {
481
+ disposed = true;
482
+ lifecycleAbort.abort();
483
+ void releaseMandatoryUpdater(false);
484
+ app.removeListener("before-quit", handleBeforeQuit);
485
+ unsubscribeUpdate();
486
+ closeUpgradeWindow();
487
+ for (const channel of Object.values(desktopUpdateAdmissionIpcChannels)) {
488
+ if (channel !== desktopUpdateAdmissionIpcChannels.state) {
489
+ ipcMain.removeHandler(channel);
490
+ }
491
+ }
492
+ }
493
+ };
494
+ }
495
+ export {
496
+ createDesktopUpdateAdmissionController
497
+ };
498
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/electron-main/createDesktopUpdateAdmissionController.ts"],"sourcesContent":["import type {\n BrowserWindow as ElectronBrowserWindow,\n BrowserWindowConstructorOptions\n} from \"electron\";\nimport {\n desktopUpdateAdmissionIpcChannels,\n type DesktopUpdateAdmissionRuntime,\n type DesktopProduct,\n type MinimumVersionAppUpdateService,\n type MinimumVersionCheckRequest,\n type MinimumVersionCheckResponse,\n type MinimumVersionUpgradeError,\n type MinimumVersionUpgradeState,\n type MandatoryDesktopUpdateSession,\n type MandatoryDesktopUpdateTarget\n} from \"../contracts/index.ts\";\nimport {\n resolveMinimumVersionRuntimeTarget,\n shouldCheckMinimumVersionAfterForeground,\n validateMinimumVersionResponse\n} from \"../core/index.ts\";\nimport { DevelopmentInstallSuppressedError } from \"../development/updaterDriver.ts\";\nimport { MandatoryUpdateTargetError } from \"../mandatory-updater/index.ts\";\n\nconst startupCheckTimeoutMs = 3_000;\nconst foregroundCheckTimeoutMs = 10_000;\n\nexport interface DesktopUpdateAdmissionElectronRuntime {\n app: typeof import(\"electron\").app;\n BrowserWindow: typeof import(\"electron\").BrowserWindow;\n ipcMain: typeof import(\"electron\").ipcMain;\n shell: typeof import(\"electron\").shell;\n}\n\nexport interface DesktopUpdateAdmissionLogger {\n info(message: string): void;\n error(message: string): void;\n}\n\nexport interface DesktopUpdateAdmissionController {\n runStartupCheck(): Promise<boolean>;\n checkAfterForegroundRestore(): Promise<void>;\n dispose(): void;\n}\n\nexport interface DesktopUpdateAdmissionControllerOptions<\n TProduct extends DesktopProduct\n> {\n product: TProduct;\n runtime: DesktopUpdateAdmissionRuntime;\n electron: DesktopUpdateAdmissionElectronRuntime;\n checkMinimumVersion(\n request: MinimumVersionCheckRequest<TProduct>,\n signal: AbortSignal\n ): Promise<unknown>;\n updateService: MinimumVersionAppUpdateService;\n logger: DesktopUpdateAdmissionLogger;\n onPolicyReleased(): void | Promise<void>;\n preloadPath: string;\n rendererFilePath: string;\n rendererUrl?: string;\n manualDownloadUrl(response: MinimumVersionCheckResponse<TProduct>): string;\n listBusinessWindows(): ElectronBrowserWindow[];\n icon?: BrowserWindowConstructorOptions[\"icon\"];\n now?: () => number;\n}\n\nfunction logMinimumVersionCheck(\n logger: DesktopUpdateAdmissionLogger,\n level: \"info\" | \"error\",\n details: Record<string, unknown>\n): void {\n logger[level](`[minimum-version-check] ${JSON.stringify(details)}`);\n}\n\nexport function createDesktopUpdateAdmissionController<\n TProduct extends DesktopProduct\n>(\n options: DesktopUpdateAdmissionControllerOptions<TProduct>\n): DesktopUpdateAdmissionController {\n const { app, BrowserWindow, ipcMain, shell } = options.electron;\n const now = options.now ?? Date.now;\n let lastCheckAt = 0;\n let foregroundPrompted = false;\n let state: MinimumVersionUpgradeState<TProduct> | null = null;\n let upgradeWindow: ElectronBrowserWindow | null = null;\n let mode: \"startup\" | \"foreground\" = \"startup\";\n let forcedFlowStarted = false;\n let installRequested = false;\n let disposed = false;\n let activeCheck: Promise<MinimumVersionCheckResponse<TProduct> | null> | null =\n null;\n let activeForcedFlow: Promise<void> | null = null;\n let appQuitStarted = false;\n let mandatoryUpdateSession: MandatoryDesktopUpdateSession | null = null;\n let isolatedBusinessWindows: Array<{\n window: ElectronBrowserWindow;\n wasFocused: boolean;\n wasMinimized: boolean;\n wasVisible: boolean;\n }> | null = null;\n const lifecycleAbort = new AbortController();\n\n const handleBeforeQuit = (): void => {\n appQuitStarted = true;\n };\n app.on(\"before-quit\", handleBeforeQuit);\n\n const emitState = (): void => {\n if (state && upgradeWindow && !upgradeWindow.isDestroyed()) {\n upgradeWindow.webContents.send(\n desktopUpdateAdmissionIpcChannels.state,\n state\n );\n }\n };\n\n const applyState = (\n phase: MinimumVersionUpgradeState[\"phase\"],\n update = options.updateService.getState(),\n message: MinimumVersionUpgradeError | null = null\n ): void => {\n if (!state) {\n return;\n }\n state = { ...state, message, phase, update };\n emitState();\n };\n\n const closeUpgradeWindow = (): void => {\n const currentWindow = upgradeWindow;\n upgradeWindow = null;\n if (currentWindow && !currentWindow.isDestroyed()) {\n currentWindow.destroy();\n }\n };\n\n const isolateBusinessWindows = (): void => {\n if (isolatedBusinessWindows) {\n return;\n }\n isolatedBusinessWindows = options\n .listBusinessWindows()\n .filter(\n (candidate) => candidate !== upgradeWindow && !candidate.isDestroyed()\n )\n .map((candidate) => ({\n wasFocused: candidate.isFocused(),\n wasMinimized: candidate.isMinimized(),\n wasVisible: candidate.isVisible(),\n window: candidate\n }));\n for (const snapshot of isolatedBusinessWindows) {\n snapshot.window.hide();\n }\n };\n\n const restoreBusinessWindows = (): void => {\n const snapshots = isolatedBusinessWindows;\n isolatedBusinessWindows = null;\n if (!snapshots) {\n return;\n }\n let focusedWindow: ElectronBrowserWindow | null = null;\n for (const snapshot of snapshots) {\n if (snapshot.window.isDestroyed()) {\n continue;\n }\n if (snapshot.wasMinimized) {\n snapshot.window.show();\n snapshot.window.minimize();\n } else if (snapshot.wasVisible) {\n snapshot.window.show();\n }\n if (snapshot.wasFocused) {\n focusedWindow = snapshot.window;\n }\n }\n if (\n focusedWindow &&\n !focusedWindow.isDestroyed() &&\n !focusedWindow.isMinimized()\n ) {\n focusedWindow.focus();\n }\n };\n\n const openUpgradeWindow = (nextMode: \"startup\" | \"foreground\"): void => {\n if (upgradeWindow && !upgradeWindow.isDestroyed()) {\n upgradeWindow.show();\n upgradeWindow.focus();\n return;\n }\n mode = nextMode;\n const window = new BrowserWindow({\n autoHideMenuBar: true,\n fullscreenable: false,\n height: 420,\n icon: options.icon,\n maximizable: false,\n minHeight: 380,\n minWidth: 480,\n resizable: false,\n show: false,\n webPreferences: {\n contextIsolation: true,\n nodeIntegration: false,\n preload: options.preloadPath,\n sandbox: true\n },\n width: 520\n });\n upgradeWindow = window;\n window.webContents.setWindowOpenHandler(() => ({ action: \"deny\" }));\n window.webContents.on(\"will-navigate\", (event) => event.preventDefault());\n window.on(\"close\", (event) => {\n if (!appQuitStarted && (mode === \"startup\" || forcedFlowStarted)) {\n event.preventDefault();\n app.quit();\n }\n });\n window.on(\"closed\", () => {\n if (upgradeWindow === window) {\n upgradeWindow = null;\n }\n });\n window.once(\"ready-to-show\", () => {\n if (!window.isDestroyed()) {\n window.show();\n }\n });\n const search = `mode=${nextMode}`;\n if (options.rendererUrl) {\n void window.loadURL(`${options.rendererUrl}?${search}`);\n } else {\n void window.loadFile(options.rendererFilePath, { search });\n }\n };\n\n const requestPayload = (): MinimumVersionCheckRequest<TProduct> | null => {\n const target = resolveMinimumVersionRuntimeTarget(\n process.platform,\n process.arch\n );\n if (!target) {\n return null;\n }\n return {\n ...target,\n currentVersion: options.runtime.currentVersion,\n product: options.product\n };\n };\n\n const runPolicyCheck = async (\n bounded: boolean\n ): Promise<MinimumVersionCheckResponse<TProduct> | null> => {\n const request = requestPayload();\n if (!request) {\n lastCheckAt = now();\n logMinimumVersionCheck(options.logger, \"info\", {\n architecture: process.arch,\n decision: \"notApplicable\",\n platform: process.platform,\n reason: \"unsupportedRuntime\",\n result: \"success\",\n stage: bounded ? \"startup\" : \"foreground\"\n });\n return null;\n }\n const startedAt = now();\n const controller = new AbortController();\n const abortForLifecycle = (): void => controller.abort();\n lifecycleAbort.signal.addEventListener(\"abort\", abortForLifecycle, {\n once: true\n });\n let timer: ReturnType<typeof setTimeout> | null = null;\n try {\n const timeoutMs = bounded\n ? startupCheckTimeoutMs\n : foregroundCheckTimeoutMs;\n const response = await Promise.race([\n options.checkMinimumVersion(request, controller.signal),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n controller.abort();\n reject(new Error(`minimum version check exceeded ${timeoutMs}ms`));\n }, timeoutMs);\n })\n ]);\n const validated = validateMinimumVersionResponse(response, request);\n lastCheckAt = now();\n logMinimumVersionCheck(options.logger, \"info\", {\n currentVersion: validated.currentVersion,\n decision: validated.decision,\n elapsedMs: now() - startedAt,\n minimumVersion: validated.minimumVersion,\n policyRevision: validated.policyRevision,\n reason: validated.reason,\n result: \"success\",\n stage: bounded ? \"startup\" : \"foreground\"\n });\n return validated;\n } catch (error) {\n lastCheckAt = now();\n logMinimumVersionCheck(options.logger, \"error\", {\n elapsedMs: now() - startedAt,\n error: error instanceof Error ? error.message : String(error),\n result: \"failure\",\n stage: bounded ? \"startup\" : \"foreground\"\n });\n return null;\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n lifecycleAbort.signal.removeEventListener(\"abort\", abortForLifecycle);\n }\n };\n\n const checkPolicy = async (\n bounded: boolean\n ): Promise<MinimumVersionCheckResponse<TProduct> | null> => {\n if (activeCheck) {\n return activeCheck;\n }\n const pendingCheck = runPolicyCheck(bounded);\n activeCheck = pendingCheck;\n try {\n return await pendingCheck;\n } finally {\n if (activeCheck === pendingCheck) {\n activeCheck = null;\n }\n }\n };\n\n const releaseMandatoryUpdater = async (\n restoreNormal = true\n ): Promise<void> => {\n const session = mandatoryUpdateSession;\n mandatoryUpdateSession = null;\n try {\n await session?.release({ restoreNormal });\n } catch (error) {\n logMinimumVersionCheck(options.logger, \"error\", {\n error: error instanceof Error ? error.message : String(error),\n result: \"failure\",\n stage: \"normal-update-restore\"\n });\n }\n };\n\n const releasePolicyBlock = async (): Promise<void> => {\n applyState(\"released\");\n forcedFlowStarted = false;\n installRequested = false;\n await releaseMandatoryUpdater();\n closeUpgradeWindow();\n restoreBusinessWindows();\n await options.onPolicyReleased();\n };\n\n const installForcedUpdate = async (): Promise<void> => {\n if (installRequested) {\n return;\n }\n installRequested = true;\n try {\n if (!mandatoryUpdateSession) {\n throw new Error(\"mandatory update session is unavailable\");\n }\n await mandatoryUpdateSession.installUpdate();\n } catch (error) {\n installRequested = false;\n if (error instanceof DevelopmentInstallSuppressedError) {\n logMinimumVersionCheck(options.logger, \"info\", {\n result: \"simulated\",\n stage: \"install\"\n });\n applyState(\"simulationComplete\", options.updateService.getState());\n return;\n }\n logMinimumVersionCheck(options.logger, \"error\", {\n error: error instanceof Error ? error.message : String(error),\n result: \"failure\",\n stage: \"install\"\n });\n applyState(\"error\", options.updateService.getState(), \"installFailed\");\n }\n };\n\n const prepareAndDownloadForcedUpdate = async (): Promise<void> => {\n if (!state) {\n return;\n }\n try {\n forcedFlowStarted = true;\n applyState(\"checking\");\n const target: MandatoryDesktopUpdateTarget = {\n channel: state.check.channel === \"rc\" ? \"rc\" : \"stable\",\n minimumVersion: state.check.minimumVersion,\n policyRevision: state.check.policyRevision\n };\n if (mandatoryUpdateSession) {\n mandatoryUpdateSession.retarget(target);\n } else {\n const acquiredSession =\n await options.updateService.acquireMandatorySession(target);\n if (disposed) {\n await acquiredSession.release({ restoreNormal: false });\n return;\n }\n mandatoryUpdateSession = acquiredSession;\n }\n const prepared = await mandatoryUpdateSession.prepare();\n if (prepared.status === \"downloaded\") {\n applyState(\"downloaded\", prepared);\n await installForcedUpdate();\n return;\n }\n if (prepared.status !== \"available\") {\n applyState(\"error\", prepared, \"updateUnavailable\");\n return;\n }\n applyState(\"ready\", prepared);\n const downloaded = await mandatoryUpdateSession.downloadUpdate();\n if (downloaded.status !== \"downloaded\") {\n applyState(\"error\", downloaded, \"updateFailed\");\n return;\n }\n applyState(\"downloaded\", downloaded);\n await installForcedUpdate();\n } catch (error) {\n logMinimumVersionCheck(options.logger, \"error\", {\n error: error instanceof Error ? error.message : String(error),\n result: \"failure\",\n stage: \"forced-update\"\n });\n applyState(\n \"error\",\n options.updateService.getState(),\n error instanceof MandatoryUpdateTargetError\n ? \"releaseBelowMinimum\"\n : \"updateFailed\"\n );\n }\n };\n\n const runForcedUpdateFlow = (): Promise<void> => {\n if (activeForcedFlow) {\n return activeForcedFlow;\n }\n const pending = prepareAndDownloadForcedUpdate();\n activeForcedFlow = pending;\n void pending.then(\n () => {\n if (activeForcedFlow === pending) {\n activeForcedFlow = null;\n }\n },\n () => {\n if (activeForcedFlow === pending) {\n activeForcedFlow = null;\n }\n }\n );\n return pending;\n };\n\n const unsubscribeUpdate = options.updateService.subscribe((update) => {\n if (!state || !forcedFlowStarted) {\n return;\n }\n if (update.status === \"downloading\") {\n applyState(\"downloading\", update);\n } else if (update.status === \"error\") {\n installRequested = false;\n applyState(\"error\", update, \"updateFailed\");\n }\n });\n\n const assertUpgradeWindowSender = (senderId: number): void => {\n if (\n !upgradeWindow ||\n upgradeWindow.isDestroyed() ||\n upgradeWindow.webContents.id !== senderId\n ) {\n throw new Error(\n \"desktop update admission IPC is restricted to the upgrade window\"\n );\n }\n };\n\n ipcMain.handle(desktopUpdateAdmissionIpcChannels.getState, (event) => {\n assertUpgradeWindowSender(event.sender.id);\n return state;\n });\n ipcMain.handle(desktopUpdateAdmissionIpcChannels.start, async (event) => {\n assertUpgradeWindowSender(event.sender.id);\n if (mode === \"foreground\") {\n isolateBusinessWindows();\n }\n await runForcedUpdateFlow();\n return state;\n });\n ipcMain.handle(desktopUpdateAdmissionIpcChannels.retry, async (event) => {\n assertUpgradeWindowSender(event.sender.id);\n await activeForcedFlow;\n const response = await checkPolicy(false);\n if (!response) {\n applyState(\n \"error\",\n options.updateService.getState(),\n \"policyCheckFailed\"\n );\n } else if (response.decision !== \"upgradeRequired\") {\n await releasePolicyBlock();\n } else if (state) {\n state = { ...state, check: response };\n await runForcedUpdateFlow();\n }\n return state;\n });\n ipcMain.handle(desktopUpdateAdmissionIpcChannels.later, (event) => {\n assertUpgradeWindowSender(event.sender.id);\n if (mode === \"foreground\" && !forcedFlowStarted) {\n closeUpgradeWindow();\n }\n });\n ipcMain.handle(\n desktopUpdateAdmissionIpcChannels.manualDownload,\n async (event) => {\n assertUpgradeWindowSender(event.sender.id);\n if (!state) {\n return;\n }\n await shell.openExternal(options.manualDownloadUrl(state.check));\n }\n );\n ipcMain.handle(desktopUpdateAdmissionIpcChannels.exit, (event) => {\n assertUpgradeWindowSender(event.sender.id);\n app.quit();\n });\n\n return {\n async runStartupCheck() {\n if (!options.runtime.checksEnabled) {\n return false;\n }\n const response = await checkPolicy(true);\n if (!response || response.decision !== \"upgradeRequired\") {\n return false;\n }\n state = {\n check: response,\n message: null,\n phase: \"blocked\",\n update: options.updateService.getState()\n };\n openUpgradeWindow(\"startup\");\n return true;\n },\n async checkAfterForegroundRestore() {\n if (\n !shouldCheckMinimumVersionAfterForeground({\n checksEnabled: options.runtime.checksEnabled,\n disposed,\n foregroundCheckIntervalMs: options.runtime.foregroundCheckIntervalMs,\n foregroundPrompted,\n lastCheckAt,\n now: now(),\n startupBlocked:\n mode === \"startup\" &&\n upgradeWindow !== null &&\n !upgradeWindow.isDestroyed()\n })\n ) {\n return;\n }\n const response = await checkPolicy(false);\n if (disposed || !response || response.decision !== \"upgradeRequired\") {\n return;\n }\n foregroundPrompted = true;\n state = {\n check: response,\n message: null,\n phase: \"blocked\",\n update: options.updateService.getState()\n };\n openUpgradeWindow(\"foreground\");\n },\n dispose() {\n disposed = true;\n lifecycleAbort.abort();\n void releaseMandatoryUpdater(false);\n app.removeListener(\"before-quit\", handleBeforeQuit);\n unsubscribeUpdate();\n closeUpgradeWindow();\n for (const channel of Object.values(desktopUpdateAdmissionIpcChannels)) {\n if (channel !== desktopUpdateAdmissionIpcChannels.state) {\n ipcMain.removeHandler(channel);\n }\n }\n }\n };\n}\n"],"mappings":"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","names":[]}