@vnejs/desktop 0.2.1 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vnejs/desktop",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Tools to run desktop app of @vnejs",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/electron.js CHANGED
@@ -27,6 +27,9 @@ const createWindow = (options) => {
27
27
  };
28
28
 
29
29
  const runElectron = (options) => {
30
+ // Allow unmuted <video> autoplay in packaged builds (Steam / Electron).
31
+ app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
32
+
30
33
  app.whenReady().then(() => {
31
34
  protocol.handle("vnejs", (request) => {
32
35
  const url = new URL(request.url);
package/src/server.js CHANGED
@@ -3,21 +3,37 @@ const os = require("os");
3
3
  const fastify = require("fastify");
4
4
  const fastifyCors = require("@fastify/cors");
5
5
  const fastifyStatic = require("@fastify/static");
6
- const { readFile, readdir, writeFile, appendFile, mkdir, unlink, access } = require("fs/promises");
6
+ const { readFile, readdir, writeFile, appendFile, mkdir, unlink, rmdir, access } = require("fs/promises");
7
7
  const { default: getPort } = require("get-port");
8
8
 
9
- const getPrefix = (key) => {
10
- const dot = key.indexOf(".");
9
+ const STORAGE_DEFAULT_GROUP = "_";
11
10
 
12
- return dot === -1 ? key : key.slice(0, dot);
11
+ const toPosixPath = (value) => value.split(path.sep).join("/");
12
+
13
+ const getStateRelPath = (key) => {
14
+ const first = key.indexOf("/");
15
+
16
+ if (first === -1) return `${key}.json`;
17
+
18
+ const module = key.slice(0, first);
19
+ const afterModule = key.slice(first + 1);
20
+ const second = afterModule.indexOf("/");
21
+
22
+ if (second === -1) return `${module}.json`;
23
+
24
+ const group = afterModule.slice(0, second);
25
+
26
+ if (!group || group === STORAGE_DEFAULT_GROUP) return `${module}.json`;
27
+
28
+ return toPosixPath(path.join(module, `${group}.json`));
13
29
  };
14
30
 
15
- const splitStateByPrefix = (state) =>
31
+ const splitStateByPath = (state) =>
16
32
  Object.entries(state).reduce((groups, [key, value]) => {
17
- const prefix = getPrefix(key);
33
+ const relPath = getStateRelPath(key);
18
34
 
19
- groups[prefix] ??= {};
20
- groups[prefix][key] = value;
35
+ groups[relPath] ??= {};
36
+ groups[relPath][key] = value;
21
37
 
22
38
  return groups;
23
39
  }, {});
@@ -99,31 +115,75 @@ const runServer = async ({
99
115
 
100
116
  const ensureStateDir = () => mkdir(resolvedStateDir, { recursive: true });
101
117
 
102
- const getStateFiles = async () => {
118
+ const listStateJsonFiles = async (dir = resolvedStateDir) => {
103
119
  try {
104
- const files = await readdir(resolvedStateDir);
120
+ const entries = await readdir(dir, { withFileTypes: true });
121
+ const files = await Promise.all(
122
+ entries.map(async (entry) => {
123
+ const fullPath = path.join(dir, entry.name);
124
+
125
+ if (entry.isDirectory()) return listStateJsonFiles(fullPath);
126
+
127
+ if (entry.isFile() && entry.name.endsWith(".json")) return [toPosixPath(path.relative(resolvedStateDir, fullPath))];
128
+
129
+ return [];
130
+ }),
131
+ );
105
132
 
106
- return files.filter((file) => file.endsWith(".json"));
133
+ return files.flat();
107
134
  } catch {
108
135
  return [];
109
136
  }
110
137
  };
111
138
 
139
+ const removeEmptyStateDirs = async (dir = resolvedStateDir) => {
140
+ let entries;
141
+
142
+ try {
143
+ entries = await readdir(dir, { withFileTypes: true });
144
+ } catch {
145
+ return;
146
+ }
147
+
148
+ await Promise.all(
149
+ entries
150
+ .filter((entry) => entry.isDirectory())
151
+ .map(async (entry) => {
152
+ const fullPath = path.join(dir, entry.name);
153
+
154
+ await removeEmptyStateDirs(fullPath);
155
+
156
+ try {
157
+ await rmdir(fullPath);
158
+ } catch {
159
+ // directory is not empty
160
+ }
161
+ }),
162
+ );
163
+ };
164
+
112
165
  const writeStateToDisk = async (state) => {
113
166
  await ensureStateDir();
114
167
 
115
- const groups = splitStateByPrefix(state);
116
- const activePrefixes = new Set(Object.keys(groups));
117
- const stalePrefixes = (await getStateFiles()).map((file) => file.slice(0, -".json".length)).filter((prefix) => !activePrefixes.has(prefix));
168
+ const groups = splitStateByPath(state);
169
+ const activePaths = new Set(Object.keys(groups));
170
+ const existingPaths = await listStateJsonFiles();
118
171
 
119
172
  await Promise.all([
120
- ...Object.entries(groups).map(([prefix, groupState]) => writeFile(path.join(resolvedStateDir, `${prefix}.json`), JSON.stringify(groupState))),
121
- ...stalePrefixes.map((prefix) => unlink(path.join(resolvedStateDir, `${prefix}.json`))),
173
+ ...Object.entries(groups).map(async ([relPath, groupState]) => {
174
+ const fullPath = path.join(resolvedStateDir, relPath);
175
+
176
+ await mkdir(path.dirname(fullPath), { recursive: true });
177
+ await writeFile(fullPath, JSON.stringify(groupState));
178
+ }),
179
+ ...existingPaths.filter((relPath) => !activePaths.has(relPath)).map((relPath) => unlink(path.join(resolvedStateDir, relPath))),
122
180
  ]);
181
+
182
+ await removeEmptyStateDirs();
123
183
  };
124
184
 
125
185
  const readStateFromDisk = async () => {
126
- const stateFiles = await getStateFiles();
186
+ const stateFiles = await listStateJsonFiles();
127
187
 
128
188
  if (!stateFiles.length) return {};
129
189