obsidian-launcher 2.1.6 → 2.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/dist/{chunk-EPJSKILH.cjs → chunk-GVT27UTK.cjs} +492 -207
- package/dist/chunk-GVT27UTK.cjs.map +1 -0
- package/dist/{chunk-O3OTELNK.js → chunk-LBBOWJGG.js} +478 -193
- package/dist/chunk-LBBOWJGG.js.map +1 -0
- package/dist/cli.cjs +22 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +19 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-EPJSKILH.cjs.map +0 -1
- package/dist/chunk-O3OTELNK.js.map +0 -1
|
@@ -7,8 +7,16 @@ var _promises = require('fs/promises'); var _promises2 = _interopRequireDefault(
|
|
|
7
7
|
var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
|
|
8
8
|
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
9
9
|
var _os = require('os'); var _os2 = _interopRequireDefault(_os);
|
|
10
|
+
var _consola = require('consola');
|
|
10
11
|
var _promisepool = require('@supercharge/promise-pool');
|
|
11
12
|
var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash);
|
|
13
|
+
var consola = _consola.createConsola.call(void 0, {
|
|
14
|
+
throttle: -1,
|
|
15
|
+
// disable throttle
|
|
16
|
+
formatOptions: {
|
|
17
|
+
date: false
|
|
18
|
+
}
|
|
19
|
+
});
|
|
12
20
|
async function fileExists(path6) {
|
|
13
21
|
try {
|
|
14
22
|
await _promises2.default.stat(path6);
|
|
@@ -23,6 +31,14 @@ async function fileExists(path6) {
|
|
|
23
31
|
async function makeTmpDir(prefix) {
|
|
24
32
|
return _promises2.default.mkdtemp(_path2.default.join(_os2.default.tmpdir(), _nullishCoalesce(prefix, () => ( "tmp-"))));
|
|
25
33
|
}
|
|
34
|
+
var logged = /* @__PURE__ */ new Map();
|
|
35
|
+
function warnOnce(key, message) {
|
|
36
|
+
const times = _nullishCoalesce(logged.get(key), () => ( 0));
|
|
37
|
+
if (times <= 0) {
|
|
38
|
+
consola.warn({ message });
|
|
39
|
+
}
|
|
40
|
+
logged.set(key, times + 1);
|
|
41
|
+
}
|
|
26
42
|
async function atomicCreate(dest, func, opts = {}) {
|
|
27
43
|
const { replace = true, preserveTmpDir = false } = opts;
|
|
28
44
|
dest = _path2.default.resolve(dest);
|
|
@@ -68,6 +84,14 @@ async function linkOrCp(src, dest) {
|
|
|
68
84
|
async function sleep(ms) {
|
|
69
85
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
70
86
|
}
|
|
87
|
+
async function withTimeout(promise, timeout) {
|
|
88
|
+
let timer;
|
|
89
|
+
const result = Promise.race([
|
|
90
|
+
promise,
|
|
91
|
+
new Promise((resolve, reject) => timer = setTimeout(() => reject(Error("Promise timed out")), timeout))
|
|
92
|
+
]);
|
|
93
|
+
return result.finally(() => clearTimeout(timer));
|
|
94
|
+
}
|
|
71
95
|
async function pool(size, items, func) {
|
|
72
96
|
const { results } = await _promisepool.PromisePool.for(items).withConcurrency(size).handleError(async (error) => {
|
|
73
97
|
throw error;
|
|
@@ -77,6 +101,47 @@ async function pool(size, items, func) {
|
|
|
77
101
|
async function maybe(promise) {
|
|
78
102
|
return promise.then((r) => ({ success: true, result: r, error: void 0 })).catch((e) => ({ success: false, result: void 0, error: e }));
|
|
79
103
|
}
|
|
104
|
+
async function until(func, opts) {
|
|
105
|
+
const { timeout, poll = 100 } = opts;
|
|
106
|
+
let time = 0;
|
|
107
|
+
let result;
|
|
108
|
+
let error;
|
|
109
|
+
while (!result && time < timeout) {
|
|
110
|
+
try {
|
|
111
|
+
result = await func();
|
|
112
|
+
error = void 0;
|
|
113
|
+
} catch (e) {
|
|
114
|
+
error = e;
|
|
115
|
+
}
|
|
116
|
+
if (!result) {
|
|
117
|
+
await sleep(poll);
|
|
118
|
+
}
|
|
119
|
+
time += poll;
|
|
120
|
+
}
|
|
121
|
+
if (!result) {
|
|
122
|
+
throw new Error("Timed out waiting for condition" + (error ? `: ${error}` : ""));
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
async function retry(func, opts = {}) {
|
|
127
|
+
const { retries = 4, backoff = 1e3, retryIf = () => true } = opts;
|
|
128
|
+
let attempt = 0;
|
|
129
|
+
let error;
|
|
130
|
+
while (attempt < retries) {
|
|
131
|
+
try {
|
|
132
|
+
return await func(attempt);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
error = e;
|
|
135
|
+
}
|
|
136
|
+
const delay = backoff * Math.random() + backoff * Math.pow(2, attempt);
|
|
137
|
+
attempt += 1;
|
|
138
|
+
if (!retryIf(error) || attempt >= retries) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
await sleep(delay);
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
80
145
|
function watchFiles(files, func, options) {
|
|
81
146
|
const debouncedFunc = _lodash2.default.debounce((curr, prev) => {
|
|
82
147
|
if (curr.mtimeMs > prev.mtimeMs || curr.mtimeMs == 0 && prev.mtimeMs != 0) {
|
|
@@ -190,15 +255,17 @@ async function obsidianApiLogin(opts) {
|
|
|
190
255
|
let email = _nullishCoalesce(_process.env.OBSIDIAN_EMAIL, () => ( cached.OBSIDIAN_EMAIL));
|
|
191
256
|
let password = _nullishCoalesce(_process.env.OBSIDIAN_PASSWORD, () => ( cached.OBSIDIAN_PASSWORD));
|
|
192
257
|
let promptedCredentials = false;
|
|
258
|
+
const predownloadMessage = "pre-download the Obsidian beta with:\n npx obsidian-launcher download app -v <version>";
|
|
193
259
|
if (!email || !password) {
|
|
194
260
|
if (interactive) {
|
|
195
|
-
|
|
261
|
+
consola.log("Obsidian Insiders account is required to download Obsidian beta versions.");
|
|
262
|
+
consola.log("You can set OBSIDIAN_EMAIL and OBSIDIAN_PASSWORD environment variables (.env file is supported) to avoid this prompt.");
|
|
196
263
|
email = email || _readlinesync2.default.question("Obsidian email: ");
|
|
197
264
|
password = password || _readlinesync2.default.question("Obsidian password: ", { hideEchoBack: true });
|
|
198
265
|
promptedCredentials = true;
|
|
199
266
|
} else {
|
|
200
267
|
throw Error(
|
|
201
|
-
"Obsidian Insiders account is required to download Obsidian beta versions.
|
|
268
|
+
"Obsidian Insiders account is required to download Obsidian beta versions. Set the OBSIDIAN_EMAIL and OBSIDIAN_PASSWORD environment variables (.env file is supported)" + (_process.env.CI ? "." : ` or ${predownloadMessage}`)
|
|
202
269
|
);
|
|
203
270
|
}
|
|
204
271
|
}
|
|
@@ -231,12 +298,12 @@ async function obsidianApiLogin(opts) {
|
|
|
231
298
|
needsMfa = true;
|
|
232
299
|
if (!interactive) {
|
|
233
300
|
throw Error(
|
|
234
|
-
"Can't login with 2FA in a non-interactive session. To download Obsidian beta versions
|
|
301
|
+
"Can't login with 2FA in a non-interactive session. To download Obsidian beta versions disable 2FA on your account" + (_process.env.CI ? "." : ` or ${predownloadMessage}`)
|
|
235
302
|
);
|
|
236
303
|
}
|
|
237
304
|
} else if (["please wait", "try again"].some((m) => _optionalChain([error, 'optionalAccess', _21 => _21.includes, 'call', _22 => _22(m)]))) {
|
|
238
|
-
|
|
239
|
-
|
|
305
|
+
consola.warn(`Obsidian login failed: ${signin.error}`);
|
|
306
|
+
consola.warn("Retrying obsidian login...");
|
|
240
307
|
retries++;
|
|
241
308
|
} else if (!signin.token) {
|
|
242
309
|
throw Error(`Obsidian login failed: ${_nullishCoalesce(signin.error, () => ( "unknown error"))}`);
|
|
@@ -256,7 +323,7 @@ async function obsidianApiLogin(opts) {
|
|
|
256
323
|
OBSIDIAN_PASSWORD='${password}'
|
|
257
324
|
`
|
|
258
325
|
);
|
|
259
|
-
|
|
326
|
+
consola.log(`Saved Obsidian credentials to ${_path2.default.relative(process.cwd(), savePath)}`);
|
|
260
327
|
}
|
|
261
328
|
}
|
|
262
329
|
return signin.token;
|
|
@@ -275,13 +342,21 @@ async function fetchObsidianApi(url, opts) {
|
|
|
275
342
|
});
|
|
276
343
|
return response;
|
|
277
344
|
}
|
|
278
|
-
async function downloadResponse(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
345
|
+
async function downloadResponse(func, dest, opts = {}) {
|
|
346
|
+
await retry(async () => {
|
|
347
|
+
const response = await func();
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
const error = Error(`${response.url} failed with ${response.status}`);
|
|
350
|
+
error.status = response.status;
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
353
|
+
const fileStream = _fs2.default.createWriteStream(dest, { flags: "w" });
|
|
354
|
+
const fetchStream = _stream.Readable.fromWeb(response.body);
|
|
355
|
+
await _promises3.finished.call(void 0, fetchStream.pipe(fileStream));
|
|
356
|
+
}, {
|
|
357
|
+
retryIf: (e) => !e.status || ![401, 403, 404].includes(e.status),
|
|
358
|
+
...opts
|
|
359
|
+
});
|
|
285
360
|
}
|
|
286
361
|
|
|
287
362
|
// src/chromeLocalStorage.ts
|
|
@@ -371,6 +446,7 @@ var _util = require('util');
|
|
|
371
446
|
|
|
372
447
|
var _zlib = require('zlib'); var _zlib2 = _interopRequireDefault(_zlib);
|
|
373
448
|
|
|
449
|
+
var _chromeremoteinterface = require('chrome-remote-interface'); var _chromeremoteinterface2 = _interopRequireDefault(_chromeremoteinterface);
|
|
374
450
|
var execFile = _util.promisify.call(void 0, _child_process2.default.execFile);
|
|
375
451
|
function normalizeGitHubRepo(repo) {
|
|
376
452
|
return _nullishCoalesce(_optionalChain([repo, 'access', _26 => _26.match, 'call', _27 => _27(/^(https?:\/\/)?(github.com\/)?(.*?)\/?$/), 'optionalAccess', _28 => _28[3]]), () => ( repo));
|
|
@@ -451,61 +527,96 @@ async function extractObsidianDmg(dmg, dest) {
|
|
|
451
527
|
}
|
|
452
528
|
});
|
|
453
529
|
}
|
|
454
|
-
async function
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
await downloadResponse(await fetch(url), installerPath);
|
|
461
|
-
const exractedPath = _path2.default.join(tmpDir, "Obsidian");
|
|
462
|
-
let platforms = [];
|
|
463
|
-
if (installerKey == "appImage" || installerKey == "appImageArm") {
|
|
464
|
-
await extractObsidianAppImage(installerPath, exractedPath);
|
|
465
|
-
platforms = ["linux-" + (installerKey == "appImage" ? "x64" : "arm64")];
|
|
466
|
-
} else if (installerKey == "tar" || installerKey == "tarArm") {
|
|
467
|
-
await extractObsidianTar(installerPath, exractedPath);
|
|
468
|
-
platforms = ["linux-" + (installerKey == "tar" ? "x64" : "arm64")];
|
|
469
|
-
} else if (installerKey == "exe") {
|
|
470
|
-
await extractObsidianExe(installerPath, "x64", exractedPath);
|
|
471
|
-
const { stdout } = await sevenZ(["l", "-ba", _path2.default.relative(tmpDir, installerPath)], { cwd: tmpDir });
|
|
472
|
-
const lines = stdout.trim().split("\n").map((l) => l.trim());
|
|
473
|
-
const files = lines.map((l) => l.split(/\s+/).at(-1).replace(/\\/g, "/"));
|
|
474
|
-
if (files.includes("$PLUGINSDIR/app-arm64.7z")) platforms.push("win32-arm64");
|
|
475
|
-
if (files.includes("$PLUGINSDIR/app-32.7z")) platforms.push("win32-ia32");
|
|
476
|
-
if (files.includes("$PLUGINSDIR/app-64.7z")) platforms.push("win32-x64");
|
|
477
|
-
} else if (installerKey == "dmg") {
|
|
478
|
-
await extractObsidianDmg(installerPath, exractedPath);
|
|
479
|
-
platforms = ["darwin-arm64", "darwin-x64"];
|
|
480
|
-
} else {
|
|
481
|
-
throw new Error(`Unknown installer key ${installerKey}`);
|
|
530
|
+
async function getCdpSession(launcher, appVersion, installerVersion) {
|
|
531
|
+
[appVersion, installerVersion] = await launcher.resolveVersion(appVersion, installerVersion);
|
|
532
|
+
const cleanup = [];
|
|
533
|
+
const doCleanup = async () => {
|
|
534
|
+
for (const func of [...cleanup].reverse()) {
|
|
535
|
+
await func();
|
|
482
536
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
537
|
+
};
|
|
538
|
+
const vault = await makeTmpDir("obsidian-vault-");
|
|
539
|
+
cleanup.push(() => _promises2.default.rm(vault, { recursive: true, force: true }));
|
|
540
|
+
const pluginDir = _path2.default.join(vault, ".obsidian", "plugins", "obsidian-launcher");
|
|
541
|
+
await _promises2.default.mkdir(pluginDir, { recursive: true });
|
|
542
|
+
await _promises2.default.writeFile(_path2.default.join(pluginDir, "manifest.json"), JSON.stringify({
|
|
543
|
+
id: "obsidian-launcher",
|
|
544
|
+
name: "Obsidian Launcher",
|
|
545
|
+
version: "1.0.0",
|
|
546
|
+
minAppVersion: "0.0.1",
|
|
547
|
+
description: "",
|
|
548
|
+
author: "obsidian-launcher",
|
|
549
|
+
isDesktopOnly: false
|
|
550
|
+
}));
|
|
551
|
+
await _promises2.default.writeFile(_path2.default.join(pluginDir, "main.js"), `
|
|
552
|
+
const obsidian = require('obsidian');
|
|
553
|
+
class ObsidianLauncherPlugin extends obsidian.Plugin {
|
|
554
|
+
async onload() { window.obsidianLauncher = {app: this.app, obsidian: obsidian}; };
|
|
494
555
|
}
|
|
556
|
+
module.exports = ObsidianLauncherPlugin;
|
|
557
|
+
`);
|
|
558
|
+
await _promises2.default.writeFile(_path2.default.join(vault, ".obsidian", "community-plugins.json"), JSON.stringify([
|
|
559
|
+
"obsidian-launcher"
|
|
560
|
+
]));
|
|
561
|
+
try {
|
|
562
|
+
const { proc, configDir } = await launcher.launch({
|
|
563
|
+
appVersion,
|
|
564
|
+
installerVersion,
|
|
565
|
+
vault,
|
|
566
|
+
copy: false,
|
|
567
|
+
args: [`--remote-debugging-port=0`, "--test-type=webdriver"]
|
|
568
|
+
// will choose a random available port
|
|
569
|
+
});
|
|
570
|
+
cleanup.push(() => _promises2.default.rm(configDir, { recursive: true, force: true }));
|
|
571
|
+
const procExit = new Promise((resolve) => proc.on("close", (code) => resolve(_nullishCoalesce(code, () => ( -1)))));
|
|
572
|
+
cleanup.push(async () => {
|
|
573
|
+
proc.kill("SIGTERM");
|
|
574
|
+
const timeout = await maybe(withTimeout(procExit, 5 * 1e3));
|
|
575
|
+
if (!timeout.success) {
|
|
576
|
+
consola.warn(`Stuck process ${proc.pid}, using SIGKILL`);
|
|
577
|
+
proc.kill("SIGKILL");
|
|
495
578
|
}
|
|
579
|
+
await procExit;
|
|
580
|
+
});
|
|
581
|
+
const portPromise = new Promise((resolve, reject) => {
|
|
582
|
+
void procExit.then(() => reject(Error("Processed ended without opening a port")));
|
|
583
|
+
proc.stderr.on("data", (data) => {
|
|
584
|
+
const port2 = _optionalChain([data, 'access', _29 => _29.toString, 'call', _30 => _30(), 'access', _31 => _31.match, 'call', _32 => _32(/ws:\/\/[\w.]+?:(\d+)/), 'optionalAccess', _33 => _33[1]]);
|
|
585
|
+
if (port2) {
|
|
586
|
+
resolve(Number(port2));
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
const port = await maybe(withTimeout(portPromise, 20 * 1e3));
|
|
591
|
+
if (!port.success) {
|
|
592
|
+
throw new Error("Timed out waiting for Chrome DevTools protocol port");
|
|
496
593
|
}
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
594
|
+
const client = await _chromeremoteinterface2.default.call(void 0, { port: port.result });
|
|
595
|
+
cleanup.push(() => client.close());
|
|
596
|
+
const expr = _semver2.default.gte(appVersion, "0.12.8") ? "!!window.obsidianLauncher" : "!!window.app.workspace";
|
|
597
|
+
await until(
|
|
598
|
+
() => client.Runtime.evaluate({ expression: expr }).then((r) => r.result.value),
|
|
599
|
+
{ timeout: 5e3 }
|
|
600
|
+
);
|
|
601
|
+
return {
|
|
602
|
+
client,
|
|
603
|
+
cleanup: doCleanup,
|
|
604
|
+
proc
|
|
605
|
+
};
|
|
606
|
+
} catch (e) {
|
|
607
|
+
await doCleanup();
|
|
608
|
+
throw e;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async function cdpEvaluate(client, expression) {
|
|
612
|
+
const response = await client.Runtime.evaluate({ expression, returnByValue: true });
|
|
613
|
+
if (response.exceptionDetails) {
|
|
614
|
+
throw Error(response.exceptionDetails.text);
|
|
508
615
|
}
|
|
616
|
+
return response.result.value;
|
|
617
|
+
}
|
|
618
|
+
async function cdpEvaluateUntil(client, expression, opts) {
|
|
619
|
+
return await until(() => cdpEvaluate(client, expression), opts);
|
|
509
620
|
}
|
|
510
621
|
async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
|
|
511
622
|
const repo = "obsidianmd/obsidian-releases";
|
|
@@ -522,37 +633,31 @@ async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
|
|
|
522
633
|
commitHistory,
|
|
523
634
|
(commit) => fetch(`https://raw.githubusercontent.com/${repo}/${commit.sha}/desktop-releases.json`).then((r) => r.json())
|
|
524
635
|
);
|
|
525
|
-
const commitDate = _nullishCoalesce(_optionalChain([commitHistory, 'access',
|
|
526
|
-
const commitSha = _nullishCoalesce(_optionalChain([commitHistory, 'access',
|
|
636
|
+
const commitDate = _nullishCoalesce(_optionalChain([commitHistory, 'access', _34 => _34.at, 'call', _35 => _35(-1), 'optionalAccess', _36 => _36.commit, 'access', _37 => _37.committer, 'access', _38 => _38.date]), () => ( sinceDate));
|
|
637
|
+
const commitSha = _nullishCoalesce(_optionalChain([commitHistory, 'access', _39 => _39.at, 'call', _40 => _40(-1), 'optionalAccess', _41 => _41.sha]), () => ( sinceSha));
|
|
527
638
|
return [fileHistory, { commitDate, commitSha }];
|
|
528
639
|
}
|
|
529
640
|
async function fetchObsidianGitHubReleases() {
|
|
530
|
-
const
|
|
531
|
-
return
|
|
641
|
+
const releases = await fetchGitHubAPIPaginated(`repos/obsidianmd/obsidian-releases/releases`);
|
|
642
|
+
return releases.reverse();
|
|
532
643
|
}
|
|
533
|
-
var
|
|
534
|
-
"
|
|
535
|
-
|
|
536
|
-
"
|
|
537
|
-
|
|
644
|
+
var BROKEN_VERSIONS = [
|
|
645
|
+
"0.12.16",
|
|
646
|
+
// broken download link
|
|
647
|
+
"1.4.7",
|
|
648
|
+
// broken download link
|
|
649
|
+
"1.4.8",
|
|
650
|
+
// broken download link
|
|
651
|
+
"1.0.1"
|
|
652
|
+
// won't launch
|
|
538
653
|
];
|
|
539
654
|
function parseObsidianDesktopRelease(fileRelease) {
|
|
540
655
|
const parse = (r, isBeta) => {
|
|
541
|
-
const version = r.latestVersion;
|
|
542
|
-
let minInstallerVersion = r.minimumVersion;
|
|
543
|
-
if (minInstallerVersion == "0.0.0") {
|
|
544
|
-
minInstallerVersion = void 0;
|
|
545
|
-
} else if (_semver2.default.satisfies(version, ">=1.3.0 <=1.3.4")) {
|
|
546
|
-
minInstallerVersion = "0.14.5";
|
|
547
|
-
} else if (_semver2.default.gte(version, "1.5.3") && _semver2.default.lt(minInstallerVersion, "1.1.9")) {
|
|
548
|
-
minInstallerVersion = "1.1.9";
|
|
549
|
-
}
|
|
550
656
|
return {
|
|
551
657
|
version: r.latestVersion,
|
|
552
|
-
minInstallerVersion,
|
|
553
658
|
isBeta,
|
|
554
659
|
downloads: {
|
|
555
|
-
asar:
|
|
660
|
+
asar: r.downloadUrl
|
|
556
661
|
}
|
|
557
662
|
};
|
|
558
663
|
};
|
|
@@ -564,11 +669,10 @@ function parseObsidianDesktopRelease(fileRelease) {
|
|
|
564
669
|
}
|
|
565
670
|
function parseObsidianGithubRelease(gitHubRelease) {
|
|
566
671
|
const version = gitHubRelease.name;
|
|
567
|
-
|
|
672
|
+
const assets = gitHubRelease.assets.map((a) => ({
|
|
568
673
|
url: a.browser_download_url,
|
|
569
674
|
digest: _nullishCoalesce(a.digest, () => ( `id:${a.id}`))
|
|
570
675
|
}));
|
|
571
|
-
assets = assets.filter((a) => !BROKEN_ASSETS.includes(a.url));
|
|
572
676
|
const asar = assets.find((a) => a.url.match(`${version}.asar.gz$`));
|
|
573
677
|
const appImage = assets.find((a) => a.url.match(`${version}.AppImage$`));
|
|
574
678
|
const appImageArm = assets.find((a) => a.url.match(`${version}-arm64.AppImage$`));
|
|
@@ -581,14 +685,14 @@ function parseObsidianGithubRelease(gitHubRelease) {
|
|
|
581
685
|
version,
|
|
582
686
|
gitHubRelease: gitHubRelease.html_url,
|
|
583
687
|
downloads: {
|
|
584
|
-
asar: _optionalChain([asar, 'optionalAccess',
|
|
585
|
-
appImage: _optionalChain([appImage, 'optionalAccess',
|
|
586
|
-
appImageArm: _optionalChain([appImageArm, 'optionalAccess',
|
|
587
|
-
tar: _optionalChain([tar, 'optionalAccess',
|
|
588
|
-
tarArm: _optionalChain([tarArm, 'optionalAccess',
|
|
589
|
-
dmg: _optionalChain([dmg, 'optionalAccess',
|
|
590
|
-
exe: _optionalChain([exe, 'optionalAccess',
|
|
591
|
-
apk: _optionalChain([apk, 'optionalAccess',
|
|
688
|
+
asar: _optionalChain([asar, 'optionalAccess', _42 => _42.url]),
|
|
689
|
+
appImage: _optionalChain([appImage, 'optionalAccess', _43 => _43.url]),
|
|
690
|
+
appImageArm: _optionalChain([appImageArm, 'optionalAccess', _44 => _44.url]),
|
|
691
|
+
tar: _optionalChain([tar, 'optionalAccess', _45 => _45.url]),
|
|
692
|
+
tarArm: _optionalChain([tarArm, 'optionalAccess', _46 => _46.url]),
|
|
693
|
+
dmg: _optionalChain([dmg, 'optionalAccess', _47 => _47.url]),
|
|
694
|
+
exe: _optionalChain([exe, 'optionalAccess', _48 => _48.url]),
|
|
695
|
+
apk: _optionalChain([apk, 'optionalAccess', _49 => _49.url])
|
|
592
696
|
},
|
|
593
697
|
installers: {
|
|
594
698
|
appImage: appImage ? { digest: appImage.digest } : void 0,
|
|
@@ -608,58 +712,184 @@ var INSTALLER_KEYS = [
|
|
|
608
712
|
"dmg",
|
|
609
713
|
"exe"
|
|
610
714
|
];
|
|
611
|
-
function
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
715
|
+
async function extractInstallerInfo(version, installerKey, url) {
|
|
716
|
+
const installerName = url.split("/").at(-1);
|
|
717
|
+
consola.log(`Extrating installer info for ${installerName}...`);
|
|
718
|
+
const tmpDir = await makeTmpDir("obsidian-launcher-");
|
|
719
|
+
try {
|
|
720
|
+
const installerPath = _path2.default.join(tmpDir, url.split("/").at(-1));
|
|
721
|
+
await downloadResponse(() => fetch(url), installerPath);
|
|
722
|
+
const exractedPath = _path2.default.join(tmpDir, "Obsidian");
|
|
723
|
+
let platforms = [];
|
|
724
|
+
if (installerKey == "appImage" || installerKey == "appImageArm") {
|
|
725
|
+
await extractObsidianAppImage(installerPath, exractedPath);
|
|
726
|
+
platforms = ["linux-" + (installerKey == "appImage" ? "x64" : "arm64")];
|
|
727
|
+
} else if (installerKey == "tar" || installerKey == "tarArm") {
|
|
728
|
+
await extractObsidianTar(installerPath, exractedPath);
|
|
729
|
+
platforms = ["linux-" + (installerKey == "tar" ? "x64" : "arm64")];
|
|
730
|
+
} else if (installerKey == "exe") {
|
|
731
|
+
await extractObsidianExe(installerPath, "x64", exractedPath);
|
|
732
|
+
const { stdout } = await sevenZ(["l", "-ba", _path2.default.relative(tmpDir, installerPath)], { cwd: tmpDir });
|
|
733
|
+
const lines = stdout.trim().split("\n").map((l) => l.trim());
|
|
734
|
+
const files = lines.map((l) => l.split(/\s+/).at(-1).replace(/\\/g, "/"));
|
|
735
|
+
if (files.includes("$PLUGINSDIR/app-arm64.7z")) platforms.push("win32-arm64");
|
|
736
|
+
if (files.includes("$PLUGINSDIR/app-32.7z")) platforms.push("win32-ia32");
|
|
737
|
+
if (files.includes("$PLUGINSDIR/app-64.7z")) platforms.push("win32-x64");
|
|
738
|
+
} else if (installerKey == "dmg") {
|
|
739
|
+
await extractObsidianDmg(installerPath, exractedPath);
|
|
740
|
+
platforms = ["darwin-arm64", "darwin-x64"];
|
|
741
|
+
} else {
|
|
742
|
+
throw new Error(`Unknown installer key ${installerKey}`);
|
|
619
743
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
744
|
+
const matches = [];
|
|
745
|
+
const installerFiles = await _promises2.default.readdir(exractedPath, { recursive: true, withFileTypes: true });
|
|
746
|
+
for (const file of installerFiles) {
|
|
747
|
+
if (file.isFile() && !file.name.endsWith(".asar")) {
|
|
748
|
+
const stream = _fs2.default.createReadStream(_path2.default.join(file.parentPath, file.name), { encoding: "utf-8" });
|
|
749
|
+
let prev = "";
|
|
750
|
+
for await (let chunk of stream) {
|
|
751
|
+
const regex = /Chrome\/\d+\.\d+\.\d+\.\d+|Electron\/\d+\.\d+\.\d+/g;
|
|
752
|
+
chunk = prev + chunk;
|
|
753
|
+
matches.push(...[...(prev + chunk).matchAll(regex)].map((m) => m[0]));
|
|
754
|
+
prev = chunk.slice(-64);
|
|
631
755
|
}
|
|
632
756
|
}
|
|
633
|
-
newVersions[parsed.version] = newVersion;
|
|
634
757
|
}
|
|
758
|
+
const versionSortKey = (v) => v.split(".").map((s) => s.padStart(9, "0")).join(".");
|
|
759
|
+
const versions = _lodash2.default.call(void 0, matches).map((m) => m.split("/")).groupBy(0).mapValues((ms) => ms.map((m) => m[1])).mapValues((ms) => _lodash2.default.sortBy(ms, versionSortKey).at(-1)).value();
|
|
760
|
+
const electron = versions["Electron"];
|
|
761
|
+
const chrome = versions["Chrome"];
|
|
762
|
+
if (!electron || !chrome) {
|
|
763
|
+
throw new Error(`Failed to extract Electron and Chrome versions from binary ${installerPath}`);
|
|
764
|
+
}
|
|
765
|
+
consola.log(`Extracted installer info for ${installerName}`);
|
|
766
|
+
return { electron, chrome, platforms };
|
|
767
|
+
} finally {
|
|
768
|
+
await _promises2.default.rm(tmpDir, { recursive: true, force: true });
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
async function checkCompatibility(launcher, appVersion, installerVersion) {
|
|
772
|
+
[appVersion, installerVersion] = await launcher.resolveVersion(appVersion, installerVersion);
|
|
773
|
+
consola.log(`Checking if app ${appVersion} and installer ${installerVersion} are compatible...`);
|
|
774
|
+
await launcher.downloadApp(appVersion);
|
|
775
|
+
await launcher.downloadInstaller(installerVersion);
|
|
776
|
+
const cdpResult = await maybe(retry(
|
|
777
|
+
() => getCdpSession(launcher, appVersion, installerVersion),
|
|
778
|
+
{ retries: 3, backoff: 4e3 }
|
|
779
|
+
));
|
|
780
|
+
if (!cdpResult.success) {
|
|
781
|
+
consola.log(`app ${appVersion} with installer ${installerVersion} failed to launch: ${cdpResult.error}`);
|
|
782
|
+
return false;
|
|
635
783
|
}
|
|
636
|
-
|
|
637
|
-
let
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
784
|
+
const { client, cleanup } = cdpResult.result;
|
|
785
|
+
let result = true;
|
|
786
|
+
try {
|
|
787
|
+
if (_semver2.default.lt(appVersion, "0.7.4")) {
|
|
788
|
+
result = _semver2.default.gte(installerVersion, "0.6.4");
|
|
789
|
+
} else if (_semver2.default.lt(appVersion, "0.13.4")) {
|
|
790
|
+
await cdpEvaluate(client, `window.app.commands.executeCommandById('app:open-settings')`);
|
|
791
|
+
await until(() => cdpEvaluate(client, `
|
|
792
|
+
document.evaluate(
|
|
793
|
+
"//*[contains(@class, 'vertical-tab-nav-item') and contains(text(),'About')]",
|
|
794
|
+
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
|
|
795
|
+
).singleNodeValue.click() ?? true;
|
|
796
|
+
`), { timeout: 5e3 });
|
|
797
|
+
const aboutText = await until(() => cdpEvaluate(client, `
|
|
798
|
+
document.evaluate(
|
|
799
|
+
"//*[contains(@class, 'setting-item-name') and contains(text(),'Current version:')]",
|
|
800
|
+
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
|
|
801
|
+
).singleNodeValue.parentNode.innerText;
|
|
802
|
+
`), { timeout: 5e3 });
|
|
803
|
+
if (["manual installation", "manually install"].some((t) => aboutText.includes(t))) {
|
|
804
|
+
result = false;
|
|
805
|
+
}
|
|
806
|
+
} else {
|
|
807
|
+
await cdpEvaluate(client, `window.obsidianLauncher.app.commands.executeCommandById('app:show-debug-info')`);
|
|
808
|
+
const debugInfo = await cdpEvaluateUntil(
|
|
809
|
+
client,
|
|
810
|
+
`document.querySelector(".debug-textarea").value.trim()`,
|
|
811
|
+
{ timeout: 5e3 }
|
|
812
|
+
);
|
|
813
|
+
if (debugInfo.toLowerCase().match(/installer version too low/)) {
|
|
814
|
+
result = false;
|
|
643
815
|
}
|
|
644
816
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
maxInstallerVersion
|
|
648
|
-
// override maxInstallerVersion if it was already set
|
|
649
|
-
});
|
|
817
|
+
} finally {
|
|
818
|
+
await cleanup();
|
|
650
819
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
820
|
+
consola.log(`app ${appVersion} and installer ${installerVersion} are ${!result ? "in" : ""}compatible`);
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
async function getCompatibilityInfos(versions, { _checkCompatibility = checkCompatibility } = {}) {
|
|
824
|
+
const tmp = await makeTmpDir("obsidian-installer-compat-");
|
|
825
|
+
try {
|
|
826
|
+
const versionsFile = {
|
|
827
|
+
metadata: {
|
|
828
|
+
schemaVersion: obsidianVersionsSchemaVersion,
|
|
829
|
+
commitDate: "1970-01-01T00:00:00Z",
|
|
830
|
+
commitSha: "0000000000000000000000000000000000000000",
|
|
831
|
+
timestamp: "1970-01-01T00:00:00Z"
|
|
832
|
+
},
|
|
833
|
+
versions: versions.map((v) => ({
|
|
834
|
+
...v,
|
|
835
|
+
minInstallerVersion: _nullishCoalesce(v.minInstallerVersion, () => ( "0.0.0")),
|
|
836
|
+
maxInstallerVersion: _nullishCoalesce(v.maxInstallerVersion, () => ( "999.9.9"))
|
|
837
|
+
}))
|
|
838
|
+
};
|
|
839
|
+
await _promises2.default.writeFile(_path2.default.join(tmp, "obsidian-versions.json"), JSON.stringify(versionsFile));
|
|
840
|
+
const launcher = new ObsidianLauncher({
|
|
841
|
+
cacheDir: _path2.default.join(tmp, "cache"),
|
|
842
|
+
versionsUrl: _url.pathToFileURL.call(void 0, _path2.default.join(tmp, "obsidian-versions.json")).toString()
|
|
655
843
|
});
|
|
844
|
+
const versionArr = _lodash2.default.call(void 0, _lodash2.default.cloneDeep(versions)).sort((a, b) => _semver2.default.compare(a.version, b.version)).dropWhile((v) => !v.downloads.appImage).filter((v) => !!v.downloads.asar).value();
|
|
845
|
+
let maxInstallerVersion = void 0;
|
|
846
|
+
for (const version of versionArr) {
|
|
847
|
+
if (version.downloads.appImage) {
|
|
848
|
+
maxInstallerVersion = version.version;
|
|
849
|
+
}
|
|
850
|
+
version.maxInstallerVersion = maxInstallerVersion;
|
|
851
|
+
}
|
|
852
|
+
const installerArr = versionArr.filter((v) => !!v.downloads.appImage);
|
|
853
|
+
const installerIndexMap = _lodash2.default.fromPairs(installerArr.map((v, i) => [v.version, i]));
|
|
854
|
+
for (const [i, version] of versionArr.entries()) {
|
|
855
|
+
if (version.minInstallerVersion) {
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
const prev = i > 0 ? versionArr[i - 1] : void 0;
|
|
859
|
+
let start = prev ? installerIndexMap[prev.minInstallerVersion] : 0;
|
|
860
|
+
let end = installerIndexMap[version.maxInstallerVersion];
|
|
861
|
+
while (start <= end) {
|
|
862
|
+
const mid = Math.floor((start + end) / 2);
|
|
863
|
+
const compatible = await _checkCompatibility(
|
|
864
|
+
launcher,
|
|
865
|
+
version.version,
|
|
866
|
+
installerArr[mid].version
|
|
867
|
+
);
|
|
868
|
+
if (!compatible) {
|
|
869
|
+
start = mid + 1;
|
|
870
|
+
} else {
|
|
871
|
+
end = mid - 1;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (start > installerIndexMap[version.maxInstallerVersion]) {
|
|
875
|
+
throw Error(`${version.version} failed to launch for all installers`);
|
|
876
|
+
}
|
|
877
|
+
version.minInstallerVersion = installerArr[start].version;
|
|
878
|
+
}
|
|
879
|
+
const origVersions = _lodash2.default.call(void 0, versions).map((v) => _lodash2.default.pick(v, ["version", "minInstallerVersion", "maxInstallerVersion"])).keyBy((v) => v.version).value();
|
|
880
|
+
return versionArr.map((v) => ({
|
|
881
|
+
version: v.version,
|
|
882
|
+
minInstallerVersion: v.minInstallerVersion,
|
|
883
|
+
maxInstallerVersion: v.maxInstallerVersion
|
|
884
|
+
})).filter((v) => !_lodash2.default.isEqual(v, origVersions[v.version]));
|
|
885
|
+
} finally {
|
|
886
|
+
await _promises2.default.rm(tmp, { recursive: true, force: true });
|
|
656
887
|
}
|
|
657
|
-
return Object.values(newVersions).map(normalizeObsidianVersionInfo).sort((a, b) => _semver2.default.compare(a.version, b.version));
|
|
658
888
|
}
|
|
659
889
|
function normalizeObsidianVersionInfo(versionInfo) {
|
|
660
890
|
versionInfo = _lodash2.default.cloneDeep(versionInfo);
|
|
661
|
-
versionInfo.electronVersion = _optionalChain([versionInfo, 'access',
|
|
662
|
-
versionInfo.chromeVersion = _optionalChain([versionInfo, 'access',
|
|
891
|
+
versionInfo.electronVersion = _optionalChain([versionInfo, 'access', _50 => _50.installers, 'optionalAccess', _51 => _51.appImage, 'optionalAccess', _52 => _52.electron]);
|
|
892
|
+
versionInfo.chromeVersion = _optionalChain([versionInfo, 'access', _53 => _53.installers, 'optionalAccess', _54 => _54.appImage, 'optionalAccess', _55 => _55.chrome]);
|
|
663
893
|
versionInfo.downloads = _nullishCoalesce(versionInfo.downloads, () => ( {}));
|
|
664
894
|
versionInfo.installers = _nullishCoalesce(versionInfo.installers, () => ( {}));
|
|
665
895
|
const canonicalForm = {
|
|
@@ -691,6 +921,74 @@ function normalizeObsidianVersionInfo(versionInfo) {
|
|
|
691
921
|
};
|
|
692
922
|
return normalizeObject(canonicalForm, versionInfo);
|
|
693
923
|
}
|
|
924
|
+
async function updateObsidianVersionList(original, {
|
|
925
|
+
maxInstances = 1,
|
|
926
|
+
_fetchObsidianDesktopReleases = fetchObsidianDesktopReleases,
|
|
927
|
+
_fetchObsidianGitHubReleases = fetchObsidianGitHubReleases,
|
|
928
|
+
_extractInstallerInfo = extractInstallerInfo,
|
|
929
|
+
_checkCompatibility = checkCompatibility
|
|
930
|
+
} = {}) {
|
|
931
|
+
const oldVersions = _lodash2.default.keyBy(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _56 => _56.versions]), () => ( [])), (v) => v.version);
|
|
932
|
+
let newVersions = _lodash2.default.cloneDeep(oldVersions);
|
|
933
|
+
const [destkopReleases, commitInfo] = await _fetchObsidianDesktopReleases(
|
|
934
|
+
_optionalChain([original, 'optionalAccess', _57 => _57.metadata, 'access', _58 => _58.commitDate]),
|
|
935
|
+
_optionalChain([original, 'optionalAccess', _59 => _59.metadata, 'access', _60 => _60.commitSha])
|
|
936
|
+
);
|
|
937
|
+
for (const destkopRelease of destkopReleases) {
|
|
938
|
+
const { current, beta } = parseObsidianDesktopRelease(destkopRelease);
|
|
939
|
+
if (beta) {
|
|
940
|
+
newVersions[beta.version] = _lodash2.default.merge(_nullishCoalesce(newVersions[beta.version], () => ( {})), beta);
|
|
941
|
+
}
|
|
942
|
+
newVersions[current.version] = _lodash2.default.merge(_nullishCoalesce(newVersions[current.version], () => ( {})), current);
|
|
943
|
+
}
|
|
944
|
+
const gitHubReleases = await _fetchObsidianGitHubReleases();
|
|
945
|
+
for (const githubRelease of gitHubReleases) {
|
|
946
|
+
if (_semver2.default.valid(githubRelease.name) && !_semver2.default.prerelease(githubRelease.name)) {
|
|
947
|
+
const parsed = parseObsidianGithubRelease(githubRelease);
|
|
948
|
+
const newVersion = _lodash2.default.merge(_nullishCoalesce(newVersions[parsed.version], () => ( {})), parsed);
|
|
949
|
+
for (const installerKey of INSTALLER_KEYS) {
|
|
950
|
+
const oldDigest = _optionalChain([oldVersions, 'access', _61 => _61[parsed.version], 'optionalAccess', _62 => _62.installers, 'access', _63 => _63[installerKey], 'optionalAccess', _64 => _64.digest]);
|
|
951
|
+
const newDigest = _optionalChain([newVersion, 'access', _65 => _65.installers, 'optionalAccess', _66 => _66[installerKey], 'optionalAccess', _67 => _67.digest]);
|
|
952
|
+
if (oldDigest && oldDigest != newDigest) {
|
|
953
|
+
newVersion.installers[installerKey] = { digest: newDigest };
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
newVersions[parsed.version] = newVersion;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
newVersions = _lodash2.default.omitBy(newVersions, (v) => BROKEN_VERSIONS.includes(v.version));
|
|
960
|
+
const newInstallers = Object.values(newVersions).flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => _optionalChain([v, 'access', _68 => _68.downloads, 'optionalAccess', _69 => _69[key]]) && !_optionalChain([v, 'access', _70 => _70.installers, 'optionalAccess', _71 => _71[key], 'optionalAccess', _72 => _72.chrome]));
|
|
961
|
+
const installerInfos = await pool(maxInstances, newInstallers, async ([v, key]) => {
|
|
962
|
+
const installerInfo = await _extractInstallerInfo(v.version, key, v.downloads[key]);
|
|
963
|
+
return { version: v.version, installers: { [key]: installerInfo } };
|
|
964
|
+
});
|
|
965
|
+
for (const installerInfo of installerInfos) {
|
|
966
|
+
newVersions[installerInfo.version] = _lodash2.default.merge(_nullishCoalesce(newVersions[installerInfo.version], () => ( {})), installerInfo);
|
|
967
|
+
}
|
|
968
|
+
const compatInfos = await getCompatibilityInfos(
|
|
969
|
+
Object.values(newVersions),
|
|
970
|
+
{ _checkCompatibility }
|
|
971
|
+
);
|
|
972
|
+
for (const compatInfo of compatInfos) {
|
|
973
|
+
newVersions[compatInfo.version] = _lodash2.default.merge(_nullishCoalesce(newVersions[compatInfo.version], () => ( {})), compatInfo);
|
|
974
|
+
}
|
|
975
|
+
const result = {
|
|
976
|
+
metadata: {
|
|
977
|
+
schemaVersion: obsidianVersionsSchemaVersion,
|
|
978
|
+
commitDate: commitInfo.commitDate,
|
|
979
|
+
commitSha: commitInfo.commitSha,
|
|
980
|
+
timestamp: _nullishCoalesce(_optionalChain([original, 'optionalAccess', _73 => _73.metadata, 'access', _74 => _74.timestamp]), () => ( ""))
|
|
981
|
+
// set down below
|
|
982
|
+
},
|
|
983
|
+
versions: Object.values(newVersions).map(normalizeObsidianVersionInfo).sort((a, b) => _semver2.default.compare(a.version, b.version))
|
|
984
|
+
};
|
|
985
|
+
const dayMs = 24 * 60 * 60 * 1e3;
|
|
986
|
+
const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _75 => _75.metadata, 'access', _76 => _76.timestamp]), () => ( 0))).getTime();
|
|
987
|
+
if (!_lodash2.default.isEqual(original, result) || timeSinceLastUpdate > 29 * dayMs) {
|
|
988
|
+
result.metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
989
|
+
}
|
|
990
|
+
return result;
|
|
991
|
+
}
|
|
694
992
|
|
|
695
993
|
// src/launcher.ts
|
|
696
994
|
var currentPlatform = {
|
|
@@ -698,6 +996,7 @@ var currentPlatform = {
|
|
|
698
996
|
arch: process.arch
|
|
699
997
|
};
|
|
700
998
|
_dotenv2.default.config({ path: [".env"], quiet: true });
|
|
999
|
+
var minSupportedObsidianVersion = "0.12.8";
|
|
701
1000
|
var ObsidianLauncher = class {
|
|
702
1001
|
/**
|
|
703
1002
|
* Construct an ObsidianLauncher.
|
|
@@ -731,7 +1030,7 @@ var ObsidianLauncher = class {
|
|
|
731
1030
|
if (!(dest in this.metadataCache)) {
|
|
732
1031
|
let data;
|
|
733
1032
|
let error;
|
|
734
|
-
const cacheMtime = await _asyncOptionalChain([(await _promises2.default.stat(dest).catch(() => void 0)), 'optionalAccess', async
|
|
1033
|
+
const cacheMtime = await _asyncOptionalChain([(await _promises2.default.stat(dest).catch(() => void 0)), 'optionalAccess', async _77 => _77.mtime]);
|
|
735
1034
|
if (url.startsWith("file:")) {
|
|
736
1035
|
data = JSON.parse(await _promises2.default.readFile(_url.fileURLToPath.call(void 0, url), "utf-8"));
|
|
737
1036
|
}
|
|
@@ -761,8 +1060,8 @@ var ObsidianLauncher = class {
|
|
|
761
1060
|
if (!data && await fileExists(dest)) {
|
|
762
1061
|
const parsed = JSON.parse(await _promises2.default.readFile(dest, "utf-8"));
|
|
763
1062
|
if (cacheValid(parsed)) {
|
|
764
|
-
|
|
765
|
-
|
|
1063
|
+
consola.warn(error);
|
|
1064
|
+
consola.warn(`Unable to download ${url}, using cached file.`);
|
|
766
1065
|
data = parsed;
|
|
767
1066
|
}
|
|
768
1067
|
}
|
|
@@ -835,8 +1134,11 @@ var ObsidianLauncher = class {
|
|
|
835
1134
|
appVersion = appVersionInfo.version;
|
|
836
1135
|
let installerVersionInfo;
|
|
837
1136
|
const { platform, arch } = process;
|
|
838
|
-
if (
|
|
839
|
-
|
|
1137
|
+
if (_semver2.default.lt(appVersion, minSupportedObsidianVersion)) {
|
|
1138
|
+
warnOnce(
|
|
1139
|
+
"unsupported-version",
|
|
1140
|
+
`Obsidian versions before ${minSupportedObsidianVersion} are unsupported, some obsidian-launcher features will not work.`
|
|
1141
|
+
);
|
|
840
1142
|
}
|
|
841
1143
|
if (installerVersion == "latest") {
|
|
842
1144
|
installerVersionInfo = _lodash2.default.findLast(
|
|
@@ -845,7 +1147,7 @@ var ObsidianLauncher = class {
|
|
|
845
1147
|
);
|
|
846
1148
|
} else if (installerVersion == "earliest") {
|
|
847
1149
|
installerVersionInfo = versions.find(
|
|
848
|
-
(v) => _semver2.default.gte(v.version, appVersionInfo.minInstallerVersion) && !!this.getInstallerKey(v, { platform, arch })
|
|
1150
|
+
(v) => appVersionInfo.minInstallerVersion && _semver2.default.gte(v.version, appVersionInfo.minInstallerVersion) && _semver2.default.lte(v.version, appVersionInfo.version) && !!this.getInstallerKey(v, { platform, arch })
|
|
849
1151
|
);
|
|
850
1152
|
} else {
|
|
851
1153
|
installerVersion = _nullishCoalesce(_semver2.default.valid(installerVersion), () => ( installerVersion));
|
|
@@ -853,14 +1155,15 @@ var ObsidianLauncher = class {
|
|
|
853
1155
|
}
|
|
854
1156
|
if (!installerVersionInfo) {
|
|
855
1157
|
if (["earliest", "latest"].includes(installerVersion)) {
|
|
856
|
-
throw Error(`No compatible installers available for Obsidian ${appVersion}`);
|
|
1158
|
+
throw Error(`No compatible installers available for Obsidian ${appVersion} for ${platform}-${arch}`);
|
|
857
1159
|
} else {
|
|
858
1160
|
throw Error(`No Obsidian installer ${installerVersion} found`);
|
|
859
1161
|
}
|
|
860
1162
|
}
|
|
861
|
-
if (_semver2.default.lt(installerVersionInfo.version, appVersionInfo.minInstallerVersion) || _semver2.default.gt(installerVersionInfo.version, appVersionInfo.maxInstallerVersion)) {
|
|
862
|
-
|
|
863
|
-
`
|
|
1163
|
+
if (!appVersionInfo.minInstallerVersion || !appVersionInfo.maxInstallerVersion || _semver2.default.lt(installerVersionInfo.version, appVersionInfo.minInstallerVersion) || _semver2.default.gt(installerVersionInfo.version, appVersionInfo.maxInstallerVersion)) {
|
|
1164
|
+
warnOnce(
|
|
1165
|
+
`incompatible-versions-${appVersionInfo.version}-${installerVersionInfo.version}`,
|
|
1166
|
+
`App and installer versions are incompatible: app ${appVersionInfo.version} is compatible with installer >=${appVersionInfo.minInstallerVersion} <=${appVersionInfo.maxInstallerVersion} but ${installerVersionInfo.version} specified.`
|
|
864
1167
|
);
|
|
865
1168
|
}
|
|
866
1169
|
return [appVersionInfo.version, installerVersionInfo.version];
|
|
@@ -877,7 +1180,7 @@ var ObsidianLauncher = class {
|
|
|
877
1180
|
appVersion = versions.filter((v) => !v.isBeta).at(-1).version;
|
|
878
1181
|
} else if (appVersion == "earliest") {
|
|
879
1182
|
const manifest = await this.getRootManifest();
|
|
880
|
-
if (!_optionalChain([manifest, 'optionalAccess',
|
|
1183
|
+
if (!_optionalChain([manifest, 'optionalAccess', _78 => _78.minAppVersion])) {
|
|
881
1184
|
throw Error('Unable to resolve Obsidian appVersion "earliest", no manifest.json or minAppVersion found.');
|
|
882
1185
|
}
|
|
883
1186
|
appVersion = manifest.minAppVersion;
|
|
@@ -941,6 +1244,20 @@ var ObsidianLauncher = class {
|
|
|
941
1244
|
);
|
|
942
1245
|
}
|
|
943
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Log into the Obsidian api using your Insider's account so you can download beta versions.
|
|
1249
|
+
*
|
|
1250
|
+
* login will be called automatically when using downloadApp on an Obsidian beta version so you usually won't need
|
|
1251
|
+
* to call this directly.
|
|
1252
|
+
*/
|
|
1253
|
+
async login() {
|
|
1254
|
+
if (!this.obsidianApiToken) {
|
|
1255
|
+
this.obsidianApiToken = await obsidianApiLogin({
|
|
1256
|
+
interactive: this.interactive,
|
|
1257
|
+
savePath: _path2.default.join(this.cacheDir, "obsidian-credentials.env")
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
944
1261
|
/**
|
|
945
1262
|
* Downloads the Obsidian installer for the given version and platform/arch (defaults to host platform/arch).
|
|
946
1263
|
* Returns the file path.
|
|
@@ -969,9 +1286,9 @@ var ObsidianLauncher = class {
|
|
|
969
1286
|
throw Error(`Unsupported platform ${platform}`);
|
|
970
1287
|
}
|
|
971
1288
|
await atomicCreate(installerDir, async (scratch) => {
|
|
972
|
-
|
|
1289
|
+
consola.log(`Downloading Obsidian installer v${installerVersion}...`);
|
|
973
1290
|
const installer = _path2.default.join(scratch, "installer");
|
|
974
|
-
await downloadResponse(
|
|
1291
|
+
await downloadResponse(() => fetch(installerInfo.url), installer);
|
|
975
1292
|
const extracted = _path2.default.join(scratch, "extracted");
|
|
976
1293
|
await extractor(installer, extracted);
|
|
977
1294
|
return extracted;
|
|
@@ -982,8 +1299,8 @@ var ObsidianLauncher = class {
|
|
|
982
1299
|
* Downloads the Obsidian asar for the given version. Returns the file path.
|
|
983
1300
|
*
|
|
984
1301
|
* To download Obsidian beta versions you'll need to have an Obsidian Insiders account and either set the
|
|
985
|
-
* `OBSIDIAN_EMAIL` and `OBSIDIAN_PASSWORD`
|
|
986
|
-
* with `npx obsidian-launcher download app -v latest-beta`
|
|
1302
|
+
* `OBSIDIAN_EMAIL` and `OBSIDIAN_PASSWORD` environment variables (`.env` file is supported) or pre-download the
|
|
1303
|
+
* Obsidian beta with `npx obsidian-launcher download app -v latest-beta`
|
|
987
1304
|
*
|
|
988
1305
|
* @param appVersion Obsidian version to download
|
|
989
1306
|
*/
|
|
@@ -995,23 +1312,18 @@ var ObsidianLauncher = class {
|
|
|
995
1312
|
}
|
|
996
1313
|
const appPath = _path2.default.join(this.cacheDir, "obsidian-app", `obsidian-${versionInfo.version}.asar`);
|
|
997
1314
|
const isInsiders = new URL(appUrl).hostname.endsWith(".obsidian.md");
|
|
998
|
-
if (isInsiders && !
|
|
999
|
-
this.
|
|
1000
|
-
interactive: this.interactive,
|
|
1001
|
-
savePath: _path2.default.join(this.cacheDir, "obsidian-credentials.env")
|
|
1002
|
-
});
|
|
1315
|
+
if (isInsiders && !await fileExists(appPath)) {
|
|
1316
|
+
await this.login();
|
|
1003
1317
|
}
|
|
1004
1318
|
await atomicCreate(appPath, async (scratch) => {
|
|
1005
|
-
|
|
1006
|
-
|
|
1319
|
+
consola.log(`Downloading Obsidian app v${versionInfo.version} ...`);
|
|
1320
|
+
const archive = _path2.default.join(scratch, "app.asar.gz");
|
|
1321
|
+
const asar = _path2.default.join(scratch, "app.asar");
|
|
1007
1322
|
if (isInsiders) {
|
|
1008
|
-
|
|
1323
|
+
await downloadResponse(() => fetchObsidianApi(appUrl, { token: this.obsidianApiToken }), archive);
|
|
1009
1324
|
} else {
|
|
1010
|
-
|
|
1325
|
+
await downloadResponse(() => fetch(appUrl), archive);
|
|
1011
1326
|
}
|
|
1012
|
-
const archive = _path2.default.join(scratch, "app.asar.gz");
|
|
1013
|
-
const asar = _path2.default.join(scratch, "app.asar");
|
|
1014
|
-
await downloadResponse(response, archive);
|
|
1015
1327
|
await extractGz(archive, asar);
|
|
1016
1328
|
return asar;
|
|
1017
1329
|
}, { replace: false });
|
|
@@ -1039,7 +1351,7 @@ var ObsidianLauncher = class {
|
|
|
1039
1351
|
chromedriverPath = _path2.default.join(chromedriverDir, `chromedriver`);
|
|
1040
1352
|
}
|
|
1041
1353
|
await atomicCreate(chromedriverDir, async (scratch) => {
|
|
1042
|
-
|
|
1354
|
+
consola.log(`Downloading chromedriver for electron ${installerInfo.electron} ...`);
|
|
1043
1355
|
const chromedriverZipPath = await _get.downloadArtifact.call(void 0, {
|
|
1044
1356
|
version: installerInfo.electron,
|
|
1045
1357
|
artifactName: "chromedriver",
|
|
@@ -1064,9 +1376,9 @@ var ObsidianLauncher = class {
|
|
|
1064
1376
|
}
|
|
1065
1377
|
const apkPath = _path2.default.join(this.cacheDir, "obsidian-apk", `obsidian-${versionInfo.version}.apk`);
|
|
1066
1378
|
await atomicCreate(apkPath, async (scratch) => {
|
|
1067
|
-
|
|
1379
|
+
consola.log(`Downloading Obsidian apk v${versionInfo.version} ...`);
|
|
1068
1380
|
const dest = _path2.default.join(scratch, "obsidian.apk");
|
|
1069
|
-
await downloadResponse(
|
|
1381
|
+
await downloadResponse(() => fetch(apkUrl), dest);
|
|
1070
1382
|
return dest;
|
|
1071
1383
|
}, { replace: false });
|
|
1072
1384
|
return apkPath;
|
|
@@ -1100,11 +1412,12 @@ var ObsidianLauncher = class {
|
|
|
1100
1412
|
await Promise.all(
|
|
1101
1413
|
Object.entries(assetsToDownload).map(async ([file, required]) => {
|
|
1102
1414
|
const url = `https://github.com/${repo}/releases/download/${version}/${file}`;
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1415
|
+
try {
|
|
1416
|
+
await downloadResponse(() => fetch(url), _path2.default.join(scratch, file));
|
|
1417
|
+
} catch (e3) {
|
|
1418
|
+
if (required) {
|
|
1419
|
+
throw Error(`No ${file} found for ${repo} version ${version}`);
|
|
1420
|
+
}
|
|
1108
1421
|
}
|
|
1109
1422
|
})
|
|
1110
1423
|
);
|
|
@@ -1274,10 +1587,9 @@ var ObsidianLauncher = class {
|
|
|
1274
1587
|
assetsToDownload.map(
|
|
1275
1588
|
async (file) => {
|
|
1276
1589
|
const url = `${baseUrl}/${file}`;
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
} else {
|
|
1590
|
+
try {
|
|
1591
|
+
await downloadResponse(() => fetch(url), _path2.default.join(scratch, file));
|
|
1592
|
+
} catch (e4) {
|
|
1281
1593
|
throw Error(`No ${file} found for ${repo}`);
|
|
1282
1594
|
}
|
|
1283
1595
|
}
|
|
@@ -1440,6 +1752,9 @@ var ObsidianLauncher = class {
|
|
|
1440
1752
|
}
|
|
1441
1753
|
}
|
|
1442
1754
|
});
|
|
1755
|
+
if (_semver2.default.lt(appVersion, "0.10.0")) {
|
|
1756
|
+
obsidianJson.last_open = vaultId;
|
|
1757
|
+
}
|
|
1443
1758
|
}
|
|
1444
1759
|
await _promises2.default.writeFile(_path2.default.join(configDir, "obsidian.json"), JSON.stringify(obsidianJson));
|
|
1445
1760
|
await _promises2.default.writeFile(_path2.default.join(configDir, "Preferences"), JSON.stringify(chromePreferences));
|
|
@@ -1527,39 +1842,7 @@ var ObsidianLauncher = class {
|
|
|
1527
1842
|
* the internal Electron version.
|
|
1528
1843
|
*/
|
|
1529
1844
|
async updateVersionList(original, opts = {}) {
|
|
1530
|
-
|
|
1531
|
-
const [destkopReleases, commitInfo] = await fetchObsidianDesktopReleases(
|
|
1532
|
-
_optionalChain([original, 'optionalAccess', _62 => _62.metadata, 'access', _63 => _63.commitDate]),
|
|
1533
|
-
_optionalChain([original, 'optionalAccess', _64 => _64.metadata, 'access', _65 => _65.commitSha])
|
|
1534
|
-
);
|
|
1535
|
-
const gitHubReleases = await fetchObsidianGitHubReleases();
|
|
1536
|
-
let newVersions = updateObsidianVersionList({
|
|
1537
|
-
original: _optionalChain([original, 'optionalAccess', _66 => _66.versions]),
|
|
1538
|
-
destkopReleases,
|
|
1539
|
-
gitHubReleases
|
|
1540
|
-
});
|
|
1541
|
-
const newInstallers = newVersions.flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => _optionalChain([v, 'access', _67 => _67.downloads, 'optionalAccess', _68 => _68[key]]) && !_optionalChain([v, 'access', _69 => _69.installers, 'optionalAccess', _70 => _70[key], 'optionalAccess', _71 => _71.chrome]));
|
|
1542
|
-
const installerInfos = await pool(maxInstances, newInstallers, async ([v, key]) => {
|
|
1543
|
-
const installerInfo = await extractInstallerInfo(key, v.downloads[key]);
|
|
1544
|
-
return { version: v.version, key, installerInfo };
|
|
1545
|
-
});
|
|
1546
|
-
newVersions = updateObsidianVersionList({ original: newVersions, installerInfos });
|
|
1547
|
-
const result = {
|
|
1548
|
-
metadata: {
|
|
1549
|
-
schemaVersion: obsidianVersionsSchemaVersion,
|
|
1550
|
-
commitDate: commitInfo.commitDate,
|
|
1551
|
-
commitSha: commitInfo.commitSha,
|
|
1552
|
-
timestamp: _nullishCoalesce(_optionalChain([original, 'optionalAccess', _72 => _72.metadata, 'access', _73 => _73.timestamp]), () => ( ""))
|
|
1553
|
-
// set down below
|
|
1554
|
-
},
|
|
1555
|
-
versions: newVersions
|
|
1556
|
-
};
|
|
1557
|
-
const dayMs = 24 * 60 * 60 * 1e3;
|
|
1558
|
-
const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(_nullishCoalesce(_optionalChain([original, 'optionalAccess', _74 => _74.metadata, 'access', _75 => _75.timestamp]), () => ( 0))).getTime();
|
|
1559
|
-
if (!_lodash2.default.isEqual(original, result) || timeSinceLastUpdate > 29 * dayMs) {
|
|
1560
|
-
result.metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1561
|
-
}
|
|
1562
|
-
return result;
|
|
1845
|
+
return updateObsidianVersionList(original, { maxInstances: opts.maxInstances });
|
|
1563
1846
|
}
|
|
1564
1847
|
/**
|
|
1565
1848
|
* Returns true if the Obsidian version is already in the cache.
|
|
@@ -1588,7 +1871,7 @@ var ObsidianLauncher = class {
|
|
|
1588
1871
|
return false;
|
|
1589
1872
|
}
|
|
1590
1873
|
if (new URL(versionInfo.downloads.asar).hostname.endsWith(".obsidian.md")) {
|
|
1591
|
-
const hasCreds = !!(process.env["OBSIDIAN_EMAIL"] && process.env["OBSIDIAN_PASSWORD"]);
|
|
1874
|
+
const hasCreds = !!(process.env["OBSIDIAN_EMAIL"] && process.env["OBSIDIAN_PASSWORD"]) || await fileExists(_path2.default.join(this.cacheDir, "obsidian-credentials.env"));
|
|
1592
1875
|
const inCache = await this.isInCache("app", versionInfo.version);
|
|
1593
1876
|
return hasCreds || inCache;
|
|
1594
1877
|
} else {
|
|
@@ -1600,5 +1883,7 @@ var ObsidianLauncher = class {
|
|
|
1600
1883
|
|
|
1601
1884
|
|
|
1602
1885
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1886
|
+
|
|
1887
|
+
|
|
1888
|
+
exports.consola = consola; exports.watchFiles = watchFiles; exports.minSupportedObsidianVersion = minSupportedObsidianVersion; exports.ObsidianLauncher = ObsidianLauncher;
|
|
1889
|
+
//# sourceMappingURL=chunk-GVT27UTK.cjs.map
|