@adecore/shell 0.29.1 → 0.29.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.
@@ -24,6 +24,7 @@ export interface WindowStateStorage {
24
24
  read(): string | null;
25
25
  write(text: string): void;
26
26
  }
27
+ export declare function renameRetrying(from: string, to: string, rename?: (from: string, to: string) => void): void;
27
28
  export declare const fileStorage: (path: string) => WindowStateStorage;
28
29
  export interface WindowStateOptions {
29
30
  storage: WindowStateStorage;
@@ -31,6 +31,27 @@ export const fitBounds = (saved, displays, defaults) => {
31
31
  }
32
32
  return { width: defaults.width, height: defaults.height };
33
33
  };
34
+ // Windows refuses a rename while an indexer or a virus scanner holds the target; a few short retries cover it.
35
+ const RENAME_RETRIES = 5;
36
+ const RENAME_RETRY_MS = 40;
37
+ function isTransient(e) {
38
+ return typeof e === 'object' && e !== null && 'code' in e && (e.code === 'EPERM' || e.code === 'EBUSY' || e.code === 'EACCES');
39
+ }
40
+ export function renameRetrying(from, to, rename = renameSync) {
41
+ for (let attempt = 0;; attempt++) {
42
+ try {
43
+ rename(from, to);
44
+ return;
45
+ }
46
+ catch (e) {
47
+ if (!isTransient(e) || attempt >= RENAME_RETRIES) {
48
+ throw e;
49
+ }
50
+ // A synchronous wait: the write runs in a timer of the main process and has no promise to wait on.
51
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, RENAME_RETRY_MS);
52
+ }
53
+ }
54
+ }
34
55
  /* A JSON file, written through a file beside it so a crash halfway leaves the last whole one. */
35
56
  export const fileStorage = (path) => ({
36
57
  read: () => {
@@ -43,7 +64,7 @@ export const fileStorage = (path) => ({
43
64
  },
44
65
  write: (text) => {
45
66
  writeFileSync(`${path}.tmp`, text);
46
- renameSync(`${path}.tmp`, path);
67
+ renameRetrying(`${path}.tmp`, path);
47
68
  }
48
69
  });
49
70
  const FILE_VERSION = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/shell",
3
- "version": "0.29.1",
3
+ "version": "0.29.3",
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",
@@ -67,6 +67,29 @@ export interface WindowStateStorage {
67
67
  write(text: string): void;
68
68
  }
69
69
 
70
+ // Windows refuses a rename while an indexer or a virus scanner holds the target; a few short retries cover it.
71
+ const RENAME_RETRIES = 5;
72
+ const RENAME_RETRY_MS = 40;
73
+
74
+ function isTransient(e: unknown): boolean {
75
+ return typeof e === 'object' && e !== null && 'code' in e && (e.code === 'EPERM' || e.code === 'EBUSY' || e.code === 'EACCES');
76
+ }
77
+
78
+ export function renameRetrying(from: string, to: string, rename: (from: string, to: string) => void = renameSync): void {
79
+ for (let attempt = 0; ; attempt++) {
80
+ try {
81
+ rename(from, to);
82
+ return;
83
+ } catch (e) {
84
+ if (!isTransient(e) || attempt >= RENAME_RETRIES) {
85
+ throw e;
86
+ }
87
+ // A synchronous wait: the write runs in a timer of the main process and has no promise to wait on.
88
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, RENAME_RETRY_MS);
89
+ }
90
+ }
91
+ }
92
+
70
93
  /* A JSON file, written through a file beside it so a crash halfway leaves the last whole one. */
71
94
  export const fileStorage = (path: string): WindowStateStorage => ({
72
95
  read: () => {
@@ -78,7 +101,7 @@ export const fileStorage = (path: string): WindowStateStorage => ({
78
101
  },
79
102
  write: (text) => {
80
103
  writeFileSync(`${path}.tmp`, text);
81
- renameSync(`${path}.tmp`, path);
104
+ renameRetrying(`${path}.tmp`, path);
82
105
  }
83
106
  });
84
107