sqlite-hub 0.1.3 → 0.2.0
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/README.md +2 -0
- package/bin/sqlite-hub.js +85 -78
- package/changelog.md +4 -0
- package/package.json +1 -1
- package/server/server.js +8 -2
- package/server/services/storage/appStateStore.js +128 -6
- package/server/utils/appPaths.js +131 -0
package/README.md
CHANGED
|
@@ -44,3 +44,5 @@ After linking or installing globally, the binary can also be called directly:
|
|
|
44
44
|
```bash
|
|
45
45
|
sqlite-hub --port:1203
|
|
46
46
|
```
|
|
47
|
+
|
|
48
|
+
App state such as recent connections, SQL history, and local settings is stored in the user profile instead of the install directory. On macOS this lives under `~/Library/Application Support/sqlite-hub/`, so Homebrew upgrades keep the internal state across versions.
|
package/bin/sqlite-hub.js
CHANGED
|
@@ -1,116 +1,123 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { spawn } = require(
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
4
|
|
|
5
5
|
const DEFAULT_PORT = 4173;
|
|
6
6
|
|
|
7
7
|
function printHelp() {
|
|
8
|
-
|
|
8
|
+
console.log(`SQLite Hub CLI
|
|
9
9
|
|
|
10
10
|
Usage:
|
|
11
11
|
sqlite-hub [--port:4173]
|
|
12
12
|
|
|
13
13
|
Options:
|
|
14
|
-
--port:PORT Start the server on a custom port.
|
|
15
14
|
--help Show this help text.
|
|
15
|
+
--port:PORT Start the server on a custom port.
|
|
16
|
+
--version Show the version number.
|
|
16
17
|
`);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
function parsePort(rawValue) {
|
|
20
|
-
|
|
21
|
+
const port = Number(rawValue);
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
24
|
+
throw new Error(`Invalid port: ${rawValue}`);
|
|
25
|
+
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
return port;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
function parseCliArguments(argv) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
let port;
|
|
32
|
+
|
|
33
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
34
|
+
const argument = argv[index];
|
|
35
|
+
|
|
36
|
+
if (argument === '--help' || argument === '-h') {
|
|
37
|
+
return { help: true };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (argument.startsWith('--port:')) {
|
|
41
|
+
port = parsePort(argument.slice('--port:'.length));
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (argument.startsWith('--port=')) {
|
|
46
|
+
port = parsePort(argument.slice('--port='.length));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (argument === '--port') {
|
|
51
|
+
port = parsePort(argv[index + 1]);
|
|
52
|
+
index += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (argument === '--version' || argument === '-v') {
|
|
57
|
+
const { version } = require('../package.json');
|
|
58
|
+
console.log(`SQLite Hub CLI version ${version}`);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new Error(`Unknown argument: ${argument}`);
|
|
42
63
|
}
|
|
43
64
|
|
|
44
|
-
|
|
45
|
-
port = parsePort(argument.slice("--port=".length));
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (argument === "--port") {
|
|
50
|
-
port = parsePort(argv[index + 1]);
|
|
51
|
-
index += 1;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
throw new Error(`Unknown argument: ${argument}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return { help: false, port };
|
|
65
|
+
return { help: false, port };
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
function openInDefaultBrowser(url) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
69
|
+
const openers = {
|
|
70
|
+
darwin: {
|
|
71
|
+
command: 'open',
|
|
72
|
+
args: [url],
|
|
73
|
+
},
|
|
74
|
+
win32: {
|
|
75
|
+
command: 'cmd',
|
|
76
|
+
args: ['/c', 'start', '', url],
|
|
77
|
+
options: { windowsHide: true },
|
|
78
|
+
},
|
|
79
|
+
default: {
|
|
80
|
+
command: 'xdg-open',
|
|
81
|
+
args: [url],
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const opener = openers[process.platform] || openers.default;
|
|
86
|
+
const child = spawn(opener.command, opener.args, {
|
|
87
|
+
detached: true,
|
|
88
|
+
stdio: 'ignore',
|
|
89
|
+
...opener.options,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
child.on('error', error => {
|
|
93
|
+
console.warn(`Could not open the browser automatically: ${error.message}`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
child.unref();
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
async function main() {
|
|
93
|
-
|
|
100
|
+
const { help, port = DEFAULT_PORT } = parseCliArguments(process.argv.slice(2));
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
if (help) {
|
|
103
|
+
printHelp();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
const { startServer } = require('../server/server');
|
|
108
|
+
const { url } = await startServer({ port });
|
|
109
|
+
openInDefaultBrowser(url);
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
if (require.main === module) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
main().catch(error => {
|
|
114
|
+
console.error(error.message);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
|
110
117
|
}
|
|
111
118
|
|
|
112
119
|
module.exports = {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
main,
|
|
121
|
+
openInDefaultBrowser,
|
|
122
|
+
parseCliArguments,
|
|
116
123
|
};
|
package/changelog.md
CHANGED
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const express = require("express");
|
|
2
2
|
const path = require("node:path");
|
|
3
3
|
const { errorMiddleware } = require("./utils/errors");
|
|
4
|
+
const { resolveAppStatePaths } = require("./utils/appPaths");
|
|
4
5
|
const { AppStateStore } = require("./services/storage/appStateStore");
|
|
5
6
|
const { ConnectionManager } = require("./services/sqlite/connectionManager");
|
|
6
7
|
const { OverviewService } = require("./services/sqlite/overviewService");
|
|
@@ -17,12 +18,17 @@ const { createDataRouter } = require("./routes/data");
|
|
|
17
18
|
const { createSettingsRouter } = require("./routes/settings");
|
|
18
19
|
const { createExportRouter } = require("./routes/export");
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
const
|
|
21
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
22
|
+
const {
|
|
23
|
+
appStateDbPath: APP_STATE_DB_PATH,
|
|
24
|
+
legacyStatePath: LEGACY_STATE_PATH,
|
|
25
|
+
legacyDatabasePaths: LEGACY_DATABASE_PATHS,
|
|
26
|
+
} = resolveAppStatePaths(PACKAGE_ROOT);
|
|
22
27
|
const DEFAULT_PORT = 4173;
|
|
23
28
|
|
|
24
29
|
const appStateStore = new AppStateStore(APP_STATE_DB_PATH, {
|
|
25
30
|
legacyFilePath: LEGACY_STATE_PATH,
|
|
31
|
+
legacyDatabasePaths: LEGACY_DATABASE_PATHS,
|
|
26
32
|
});
|
|
27
33
|
const connectionManager = new ConnectionManager({ appStateStore });
|
|
28
34
|
const overviewService = new OverviewService({ connectionManager });
|
|
@@ -20,6 +20,9 @@ class AppStateStore {
|
|
|
20
20
|
constructor(filePath, options = {}) {
|
|
21
21
|
this.filePath = filePath;
|
|
22
22
|
this.legacyFilePath = options.legacyFilePath ?? null;
|
|
23
|
+
this.legacyDatabasePaths = Array.isArray(options.legacyDatabasePaths)
|
|
24
|
+
? options.legacyDatabasePaths
|
|
25
|
+
: [];
|
|
23
26
|
this.isFreshDatabase = !fs.existsSync(filePath);
|
|
24
27
|
|
|
25
28
|
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
@@ -29,8 +32,10 @@ class AppStateStore {
|
|
|
29
32
|
this.ensureSchema();
|
|
30
33
|
this.seedDefaultSettings();
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
const importedLegacyDatabase = this.importFirstLegacyDatabase();
|
|
36
|
+
|
|
37
|
+
if (!importedLegacyDatabase && this.shouldImportLegacyState()) {
|
|
38
|
+
this.tryImportLegacyState();
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
|
@@ -121,8 +126,90 @@ class AppStateStore {
|
|
|
121
126
|
};
|
|
122
127
|
}
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
getExistingLegacyDatabasePaths() {
|
|
130
|
+
return this.legacyDatabasePaths
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.map((legacyPath) => path.resolve(legacyPath))
|
|
133
|
+
.filter(
|
|
134
|
+
(legacyPath, index, legacyPaths) =>
|
|
135
|
+
legacyPath !== path.resolve(this.filePath) &&
|
|
136
|
+
legacyPaths.indexOf(legacyPath) === index &&
|
|
137
|
+
fs.existsSync(legacyPath)
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
readLegacyDatabase(legacyDatabasePath) {
|
|
142
|
+
const legacyDb = new Database(legacyDatabasePath, {
|
|
143
|
+
readonly: true,
|
|
144
|
+
fileMustExist: true,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
legacyDb.pragma("query_only = ON");
|
|
149
|
+
|
|
150
|
+
const tables = new Set(
|
|
151
|
+
legacyDb
|
|
152
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
153
|
+
.all()
|
|
154
|
+
.map((row) => row.name)
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
settings: tables.has("settings")
|
|
159
|
+
? Object.fromEntries(
|
|
160
|
+
legacyDb
|
|
161
|
+
.prepare("SELECT key, value FROM settings")
|
|
162
|
+
.all()
|
|
163
|
+
.map((row) => [row.key, row.value])
|
|
164
|
+
)
|
|
165
|
+
: {},
|
|
166
|
+
recentConnections: tables.has("recent_connections")
|
|
167
|
+
? legacyDb
|
|
168
|
+
.prepare(`
|
|
169
|
+
SELECT
|
|
170
|
+
id,
|
|
171
|
+
label,
|
|
172
|
+
path,
|
|
173
|
+
lastOpenedAt,
|
|
174
|
+
lastModifiedAt,
|
|
175
|
+
sizeBytes,
|
|
176
|
+
readOnly
|
|
177
|
+
FROM recent_connections
|
|
178
|
+
ORDER BY lastOpenedAt DESC, id ASC
|
|
179
|
+
`)
|
|
180
|
+
.all()
|
|
181
|
+
: [],
|
|
182
|
+
sqlHistory: tables.has("sql_history")
|
|
183
|
+
? legacyDb
|
|
184
|
+
.prepare(`
|
|
185
|
+
SELECT
|
|
186
|
+
id,
|
|
187
|
+
connectionId,
|
|
188
|
+
connectionLabel,
|
|
189
|
+
sql,
|
|
190
|
+
statementCount,
|
|
191
|
+
resultKind,
|
|
192
|
+
affectedRowCount,
|
|
193
|
+
rowCount,
|
|
194
|
+
timingMs,
|
|
195
|
+
executedAt
|
|
196
|
+
FROM sql_history
|
|
197
|
+
ORDER BY executedAt DESC, id ASC
|
|
198
|
+
`)
|
|
199
|
+
.all()
|
|
200
|
+
: [],
|
|
201
|
+
activeConnectionId: tables.has("app_meta")
|
|
202
|
+
? legacyDb
|
|
203
|
+
.prepare("SELECT value FROM app_meta WHERE key = ?")
|
|
204
|
+
.get("activeConnectionId")?.value ?? null
|
|
205
|
+
: null,
|
|
206
|
+
};
|
|
207
|
+
} finally {
|
|
208
|
+
legacyDb.close();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
importStateSnapshot(legacyState, sourcePath) {
|
|
126
213
|
const insertSetting = this.db.prepare(`
|
|
127
214
|
INSERT INTO settings (key, value)
|
|
128
215
|
VALUES (?, ?)
|
|
@@ -175,7 +262,10 @@ class AppStateStore {
|
|
|
175
262
|
|
|
176
263
|
this.db.transaction(() => {
|
|
177
264
|
for (const [key, value] of Object.entries(legacyState.settings ?? {})) {
|
|
178
|
-
|
|
265
|
+
const normalizedValue =
|
|
266
|
+
typeof value === "string" ? this.parseStoredValue(value) : value;
|
|
267
|
+
|
|
268
|
+
insertSetting.run(key, JSON.stringify(normalizedValue));
|
|
179
269
|
}
|
|
180
270
|
|
|
181
271
|
for (const connection of legacyState.recentConnections ?? []) {
|
|
@@ -208,11 +298,43 @@ class AppStateStore {
|
|
|
208
298
|
this.setMetaValue("activeConnectionId", legacyState.activeConnectionId ?? null);
|
|
209
299
|
this.setMetaValue(
|
|
210
300
|
"legacyImportSource",
|
|
211
|
-
path.relative(path.dirname(this.filePath),
|
|
301
|
+
path.relative(path.dirname(this.filePath), sourcePath)
|
|
212
302
|
);
|
|
213
303
|
})();
|
|
214
304
|
}
|
|
215
305
|
|
|
306
|
+
importFirstLegacyDatabase() {
|
|
307
|
+
if (!this.isFreshDatabase) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
for (const legacyDatabasePath of this.getExistingLegacyDatabasePaths()) {
|
|
312
|
+
try {
|
|
313
|
+
this.importStateSnapshot(
|
|
314
|
+
this.readLegacyDatabase(legacyDatabasePath),
|
|
315
|
+
legacyDatabasePath
|
|
316
|
+
);
|
|
317
|
+
return true;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.warn(
|
|
320
|
+
`Could not import legacy app state database from ${legacyDatabasePath}: ${error.message}`
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
tryImportLegacyState() {
|
|
329
|
+
try {
|
|
330
|
+
this.importStateSnapshot(this.readLegacyState(), this.legacyFilePath);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.warn(
|
|
333
|
+
`Could not import legacy app state from ${this.legacyFilePath}: ${error.message}`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
216
338
|
parseStoredValue(value) {
|
|
217
339
|
try {
|
|
218
340
|
return JSON.parse(value);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const os = require("node:os");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
const APP_NAME = "sqlite-hub";
|
|
6
|
+
const APP_STATE_DB_FILENAME = "sqlite-hub-state.db";
|
|
7
|
+
const LEGACY_STATE_FILENAME = "app-state.json";
|
|
8
|
+
|
|
9
|
+
function resolveAppStateDirectory() {
|
|
10
|
+
const homeDirectory = os.homedir();
|
|
11
|
+
|
|
12
|
+
if (process.platform === "darwin") {
|
|
13
|
+
return path.join(homeDirectory, "Library", "Application Support", APP_NAME);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (process.platform === "win32") {
|
|
17
|
+
const appDataDirectory =
|
|
18
|
+
process.env.APPDATA || path.join(homeDirectory, "AppData", "Roaming");
|
|
19
|
+
|
|
20
|
+
return path.join(appDataDirectory, APP_NAME);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (process.env.XDG_STATE_HOME) {
|
|
24
|
+
return path.join(process.env.XDG_STATE_HOME, APP_NAME);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return path.join(homeDirectory, ".local", "state", APP_NAME);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolvePackagedDataDirectory(packageRoot) {
|
|
31
|
+
return path.resolve(packageRoot, "data");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolvePackagedAppStateDbPath(packageRoot) {
|
|
35
|
+
return path.join(resolvePackagedDataDirectory(packageRoot), APP_STATE_DB_FILENAME);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolvePackagedLegacyStatePath(packageRoot) {
|
|
39
|
+
return path.join(resolvePackagedDataDirectory(packageRoot), LEGACY_STATE_FILENAME);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function resolveHomebrewCellarInfo(packageRoot) {
|
|
43
|
+
const resolvedPackageRoot = path.resolve(packageRoot);
|
|
44
|
+
const { root } = path.parse(resolvedPackageRoot);
|
|
45
|
+
const relativeSegments = resolvedPackageRoot
|
|
46
|
+
.slice(root.length)
|
|
47
|
+
.split(path.sep)
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
const cellarIndex = relativeSegments.indexOf("Cellar");
|
|
50
|
+
|
|
51
|
+
if (cellarIndex === -1) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const formulaName = relativeSegments[cellarIndex + 1];
|
|
56
|
+
const currentVersion = relativeSegments[cellarIndex + 2];
|
|
57
|
+
|
|
58
|
+
if (formulaName !== APP_NAME || !currentVersion) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
cellarRoot: path.join(root, ...relativeSegments.slice(0, cellarIndex + 2)),
|
|
64
|
+
currentVersion,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function safeStatMtimeMs(filePath) {
|
|
69
|
+
try {
|
|
70
|
+
return fs.statSync(filePath).mtimeMs;
|
|
71
|
+
} catch {
|
|
72
|
+
return -1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function collectHomebrewLegacyStateDbPaths(packageRoot) {
|
|
77
|
+
const cellarInfo = resolveHomebrewCellarInfo(packageRoot);
|
|
78
|
+
|
|
79
|
+
if (!cellarInfo || !fs.existsSync(cellarInfo.cellarRoot)) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return fs
|
|
84
|
+
.readdirSync(cellarInfo.cellarRoot, { withFileTypes: true })
|
|
85
|
+
.filter((entry) => entry.isDirectory() && entry.name !== cellarInfo.currentVersion)
|
|
86
|
+
.map((entry) => {
|
|
87
|
+
const candidatePath = path.join(
|
|
88
|
+
cellarInfo.cellarRoot,
|
|
89
|
+
entry.name,
|
|
90
|
+
"libexec",
|
|
91
|
+
"lib",
|
|
92
|
+
"node_modules",
|
|
93
|
+
APP_NAME,
|
|
94
|
+
"data",
|
|
95
|
+
APP_STATE_DB_FILENAME
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
path: candidatePath,
|
|
100
|
+
mtimeMs: safeStatMtimeMs(candidatePath),
|
|
101
|
+
};
|
|
102
|
+
})
|
|
103
|
+
.filter((candidate) => candidate.mtimeMs >= 0)
|
|
104
|
+
.sort((left, right) => right.mtimeMs - left.mtimeMs)
|
|
105
|
+
.map((candidate) => candidate.path);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function collectLegacyDatabasePaths(packageRoot) {
|
|
109
|
+
return [
|
|
110
|
+
resolvePackagedAppStateDbPath(packageRoot),
|
|
111
|
+
...collectHomebrewLegacyStateDbPaths(packageRoot),
|
|
112
|
+
].filter((candidatePath, index, candidates) => candidates.indexOf(candidatePath) === index);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function resolveAppStatePaths(packageRoot) {
|
|
116
|
+
const appStateDirectory = resolveAppStateDirectory();
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
appStateDirectory,
|
|
120
|
+
appStateDbPath: path.join(appStateDirectory, APP_STATE_DB_FILENAME),
|
|
121
|
+
legacyStatePath: resolvePackagedLegacyStatePath(packageRoot),
|
|
122
|
+
legacyDatabasePaths: collectLegacyDatabasePaths(packageRoot),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
APP_NAME,
|
|
128
|
+
APP_STATE_DB_FILENAME,
|
|
129
|
+
resolveAppStateDirectory,
|
|
130
|
+
resolveAppStatePaths,
|
|
131
|
+
};
|