obsidian-launcher 2.1.0 → 2.1.2

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 CHANGED
@@ -7,7 +7,7 @@ The primary use case for this package is to allow [wdio-obsidian-service](../wdi
7
7
  ## Example Usage
8
8
  You can run the CLI via `npx`, e.g.:
9
9
  ```bash
10
- npx obsidian-launcher watch --version 1.8.10 --copy -p . test/vault
10
+ npx obsidian-launcher watch --version 1.8.10 --copy --plugin . test/vault
11
11
  ```
12
12
  This will download and launch Obsidian 1.8.10 with a sandboxed configuration directory so you don't need to worry about it interfering with your system Obsidian installation. You can even launch multiple different versions of Obsidian side-by-side. See [below](#cli) for CLI docs.
13
13
 
@@ -32,7 +32,7 @@ Obsidian Desktop is distributed in two parts, the "installer" which is the execu
32
32
  `appVersion` can be set to one of:
33
33
  - a specific version string like "1.7.7"
34
34
  - "latest": run the latest non-beta Obsidian version
35
- - "latest-beta": run the latest beta Obsidian version (or latest is there is no current beta)
35
+ - "latest-beta": run the latest beta Obsidian version (or latest if there is no current beta)
36
36
  - To download Obsidian beta versions you'll need to have an Obsidian Insiders account
37
37
  - "earliest": run the `minAppVersion` set in your plugin's `manifest.json`
38
38
 
@@ -61,7 +61,7 @@ Several commands can take a list of plugins and themes to install. You can speci
61
61
  - `id:<community-id>`: For plugins, id of a community plugin, e.g. `id:templater-obsidian`
62
62
  - `name:<community-name>`: For themes, name of a community theme, e.g. `name:Minimal`
63
63
 
64
- You can install a specific version of a plugin or theme with `-p id:myplugin=1.2.3`.
64
+ You can install a specific version of a plugin or theme with `-p id:myplugin@1.2.3`.
65
65
 
66
66
  ### launch
67
67
  Download and launch Obsidian, opening the specified vault.
@@ -7,38 +7,50 @@ import { PromisePool } from "@supercharge/promise-pool";
7
7
  import _ from "lodash";
8
8
  async function fileExists(path6) {
9
9
  try {
10
- await fsAsync.access(path6);
10
+ await fsAsync.stat(path6);
11
11
  return true;
12
- } catch {
13
- return false;
12
+ } catch (e) {
13
+ if (e?.code == "ENOENT") {
14
+ return false;
15
+ }
16
+ throw e;
14
17
  }
15
18
  }
16
19
  async function makeTmpDir(prefix) {
17
20
  return fsAsync.mkdtemp(path.join(os.tmpdir(), prefix ?? "tmp-"));
18
21
  }
19
22
  async function atomicCreate(dest, func, opts = {}) {
23
+ const { replace = true, preserveTmpDir = false } = opts;
20
24
  dest = path.resolve(dest);
21
- const createdParentDir = await fsAsync.mkdir(path.dirname(dest), { recursive: true });
22
- const tmpDir = await fsAsync.mkdtemp(path.join(path.dirname(dest), `.${path.basename(dest)}.tmp.`));
23
- let success = false;
25
+ const parentDir = path.dirname(dest);
26
+ if (!replace && await fileExists(dest)) return;
27
+ await fsAsync.mkdir(parentDir, { recursive: true });
28
+ const scratch = await fsAsync.mkdtemp(path.join(parentDir, `.${path.basename(dest)}.tmp.`));
24
29
  try {
25
- let result = await func(tmpDir) ?? tmpDir;
26
- result = path.resolve(tmpDir, result);
27
- if (!result.startsWith(tmpDir)) {
28
- throw new Error(`Returned path ${result} not under tmpDir`);
30
+ let result = await func(scratch) ?? scratch;
31
+ result = path.resolve(scratch, result);
32
+ if (!result.startsWith(scratch)) {
33
+ throw new Error(`Returned path ${result} not under scratch`);
29
34
  }
30
- if (await fileExists(dest) && (await fsAsync.stat(dest)).isDirectory()) {
31
- await fsAsync.rename(dest, tmpDir + ".old");
35
+ if (replace) {
36
+ if ((await fsAsync.stat(dest).catch(() => null))?.isDirectory()) {
37
+ await fsAsync.rename(dest, `${scratch}.old`);
38
+ }
39
+ await fsAsync.rename(result, dest);
40
+ } else {
41
+ if (!await fileExists(dest)) {
42
+ await fsAsync.rename(result, dest).catch((e) => {
43
+ if (e?.code != "ENOTEMPTY") throw e;
44
+ });
45
+ }
32
46
  }
33
- await fsAsync.rename(result, dest);
34
- success = true;
35
- } finally {
36
- if (success) {
37
- await fsAsync.rm(tmpDir + ".old", { recursive: true, force: true });
38
- await fsAsync.rm(tmpDir, { recursive: true, force: true });
39
- } else if (!opts.preserveTmpDir) {
40
- await fsAsync.rm(createdParentDir ?? tmpDir, { recursive: true, force: true });
47
+ await fsAsync.rm(scratch, { recursive: true, force: true });
48
+ await fsAsync.rm(`${scratch}.old`, { recursive: true, force: true });
49
+ } catch (e) {
50
+ if (!preserveTmpDir) {
51
+ await fsAsync.rm(scratch, { recursive: true, force: true });
41
52
  }
53
+ throw e;
42
54
  }
43
55
  }
44
56
  async function linkOrCp(src, dest) {
@@ -116,8 +128,29 @@ import fsAsync2 from "fs/promises";
116
128
  import readlineSync from "readline-sync";
117
129
  import dotenv from "dotenv";
118
130
  import path2 from "path";
119
- import { Octokit } from "octokit";
120
131
  import { env } from "process";
132
+ function parseLinkHeader(linkHeader) {
133
+ function parseLinkData(linkData) {
134
+ return Object.fromEntries(
135
+ linkData.split(";").flatMap((x) => {
136
+ const partMatch = x.trim().match(/^([^=]+?)\s*=\s*"?([^"]+)"?$/);
137
+ return partMatch ? [[partMatch[1], partMatch[2]]] : [];
138
+ })
139
+ );
140
+ }
141
+ const linkDatas = linkHeader.split(/,\s*(?=<)/).flatMap((link) => {
142
+ const linkMatch = link.trim().match(/^<([^>]*)>(.*)$/);
143
+ if (linkMatch) {
144
+ return [{
145
+ url: linkMatch[1],
146
+ ...parseLinkData(linkMatch[2])
147
+ }];
148
+ } else {
149
+ return [];
150
+ }
151
+ }).filter((l) => l.rel);
152
+ return Object.fromEntries(linkDatas.map((l) => [l.rel, l]));
153
+ }
121
154
  function createURL(url, base, params = {}) {
122
155
  const cleanParams = _2(params).pickBy((x) => x !== void 0).mapValues((v) => String(v)).value();
123
156
  const urlObj = new URL(url, base);
@@ -127,37 +160,59 @@ function createURL(url, base, params = {}) {
127
160
  }
128
161
  return urlObj.toString();
129
162
  }
130
- function getGithubClient() {
131
- return new Octokit({ auth: env.GITHUB_TOKEN });
163
+ async function fetchGitHubAPI(url, params = {}) {
164
+ url = createURL(url, "https://api.github.com", params);
165
+ const token = env.GITHUB_TOKEN;
166
+ const headers = token ? { Authorization: "Bearer " + token } : {};
167
+ const response = await fetch(url, { headers });
168
+ if (!response.ok) {
169
+ throw new Error(`GitHub API error: ${await response.text()}`);
170
+ }
171
+ return response;
172
+ }
173
+ async function fetchGitHubAPIPaginated(url, params = {}) {
174
+ const results = [];
175
+ let next = createURL(url, "https://api.github.com", { per_page: 100, ...params });
176
+ while (next) {
177
+ const response = await fetchGitHubAPI(next);
178
+ results.push(...await response.json());
179
+ next = parseLinkHeader(response.headers.get("link") ?? "").next?.url;
180
+ }
181
+ return results;
132
182
  }
133
183
  async function obsidianApiLogin(opts) {
134
184
  const { interactive = false, savePath } = opts;
135
- if (savePath) dotenv.config({ path: [savePath], quiet: true });
136
- let email = env.OBSIDIAN_EMAIL;
137
- let password = env.OBSIDIAN_PASSWORD;
185
+ const cached = savePath ? dotenv.parse(await fsAsync2.readFile(savePath).catch(() => "")) : {};
186
+ let email = env.OBSIDIAN_EMAIL ?? cached.OBSIDIAN_EMAIL;
187
+ let password = env.OBSIDIAN_PASSWORD ?? cached.OBSIDIAN_PASSWORD;
188
+ let promptedCredentials = false;
138
189
  if (!email || !password) {
139
190
  if (interactive) {
140
191
  console.log("Obsidian Insiders account is required to download Obsidian beta versions.");
141
192
  email = email || readlineSync.question("Obsidian email: ");
142
193
  password = password || readlineSync.question("Obsidian password: ", { hideEchoBack: true });
194
+ promptedCredentials = true;
143
195
  } else {
144
196
  throw Error(
145
197
  "Obsidian Insiders account is required to download Obsidian beta versions. Either set the OBSIDIAN_EMAIL and OBSIDIAN_PASSWORD env vars (.env file is supported) or pre-download the Obsidian beta with `npx obsidian-launcher download app -v <version>`"
146
198
  );
147
199
  }
148
200
  }
201
+ function parseSignin(r) {
202
+ return { token: r.token ? "token" : void 0, error: r.error?.toString(), license: r.license };
203
+ }
149
204
  let needsMfa = false;
150
205
  let retries = 0;
151
206
  let signin = void 0;
152
207
  while (!signin?.token && retries < 3) {
153
208
  if (retries > 0 || env.CI) {
154
- await sleep(2 * Math.random() + retries * retries * 2);
209
+ await sleep(2 * Math.random() + retries * retries * 3);
155
210
  }
156
211
  let mfa = "";
157
212
  if (needsMfa && interactive) {
158
213
  mfa = readlineSync.question("Obsidian 2FA: ");
159
214
  }
160
- signin = await fetch("https://api.obsidian.md/user/signin", {
215
+ const response = await fetch("https://api.obsidian.md/user/signin", {
161
216
  method: "post",
162
217
  headers: {
163
218
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36",
@@ -166,6 +221,7 @@ async function obsidianApiLogin(opts) {
166
221
  },
167
222
  body: JSON.stringify({ email, password, mfa })
168
223
  }).then((r) => r.json());
224
+ signin = parseSignin(response);
169
225
  const error = signin.error?.toLowerCase();
170
226
  if (error?.includes("2fa") && !needsMfa) {
171
227
  needsMfa = true;
@@ -176,6 +232,7 @@ async function obsidianApiLogin(opts) {
176
232
  }
177
233
  } else if (["please wait", "try again"].some((m) => error?.includes(m))) {
178
234
  console.warn(`Obsidian login failed: ${signin.error}`);
235
+ console.warn("Retrying obsidian login...");
179
236
  retries++;
180
237
  } else if (!signin.token) {
181
238
  throw Error(`Obsidian login failed: ${signin.error ?? "unknown error"}`);
@@ -186,7 +243,7 @@ async function obsidianApiLogin(opts) {
186
243
  } else if (!signin?.license) {
187
244
  throw Error("Obsidian Insiders account is required to download Obsidian beta versions");
188
245
  }
189
- if (interactive && savePath && (!env.OBSIDIAN_EMAIL || !env.OBSIDIAN_PASSWORD)) {
246
+ if (savePath && promptedCredentials) {
190
247
  const save = readlineSync.question("Cache credentails to disk? [y/n]: ");
191
248
  if (["y", "yes"].includes(save.toLowerCase())) {
192
249
  await fsAsync2.writeFile(
@@ -201,6 +258,9 @@ OBSIDIAN_PASSWORD='${password}'
201
258
  return signin.token;
202
259
  }
203
260
  async function fetchObsidianApi(url, opts) {
261
+ if (!opts.token) {
262
+ throw Error("Obsidian credentials required to download Obsidian beta release");
263
+ }
204
264
  url = createURL(url, "https://releases.obsidian.md");
205
265
  const response = await fetch(url, {
206
266
  headers: {
@@ -332,16 +392,16 @@ ${stderr}`);
332
392
  return result;
333
393
  }
334
394
  async function extractObsidianAppImage(appImage, dest) {
335
- await atomicCreate(dest, async (tmpDir) => {
336
- await sevenZ(["x", "-o.", path4.relative(tmpDir, appImage)], { cwd: tmpDir });
337
- return tmpDir;
395
+ await atomicCreate(dest, async (scratch) => {
396
+ await sevenZ(["x", "-o.", path4.relative(scratch, appImage)], { cwd: scratch });
397
+ return scratch;
338
398
  });
339
399
  }
340
400
  async function extractObsidianTar(tar, dest) {
341
- await atomicCreate(dest, async (tmpDir) => {
342
- await extractGz(tar, path4.join(tmpDir, "inflated.tar"));
343
- await sevenZ(["x", "-o.", "inflated.tar"], { cwd: tmpDir });
344
- return (await fsAsync3.readdir(tmpDir)).find((p) => p.match("obsidian-"));
401
+ await atomicCreate(dest, async (scratch) => {
402
+ await extractGz(tar, path4.join(scratch, "inflated.tar"));
403
+ await sevenZ(["x", "-o.", "inflated.tar"], { cwd: scratch });
404
+ return (await fsAsync3.readdir(scratch)).find((p) => p.match("obsidian-"));
345
405
  });
346
406
  }
347
407
  async function extractObsidianExe(exe, arch, dest) {
@@ -355,17 +415,17 @@ async function extractObsidianExe(exe, arch, dest) {
355
415
  } else {
356
416
  throw Error(`No Obsidian installer found for ${process.platform} ${process.arch}`);
357
417
  }
358
- await atomicCreate(dest, async (tmpDir) => {
359
- await sevenZ(["x", "-oinstaller", path4.relative(tmpDir, exe), subArchive], { cwd: tmpDir });
360
- await sevenZ(["x", "-oobsidian", path4.join("installer", subArchive)], { cwd: tmpDir });
418
+ await atomicCreate(dest, async (scratch) => {
419
+ await sevenZ(["x", "-oinstaller", path4.relative(scratch, exe), subArchive], { cwd: scratch });
420
+ await sevenZ(["x", "-oobsidian", path4.join("installer", subArchive)], { cwd: scratch });
361
421
  return "obsidian";
362
422
  });
363
423
  }
364
424
  async function extractObsidianDmg(dmg, dest) {
365
425
  dest = path4.resolve(dest);
366
- await atomicCreate(dest, async (tmpDir) => {
367
- await sevenZ(["x", "-o.", path4.relative(tmpDir, dmg), "*/Obsidian.app", "Obsidian.app"], { cwd: tmpDir });
368
- const files = await fsAsync3.readdir(tmpDir);
426
+ await atomicCreate(dest, async (scratch) => {
427
+ await sevenZ(["x", "-o.", path4.relative(scratch, dmg), "*/Obsidian.app", "Obsidian.app"], { cwd: scratch });
428
+ const files = await fsAsync3.readdir(scratch);
369
429
  if (files.includes("Obsidian.app")) {
370
430
  return "Obsidian.app";
371
431
  } else {
@@ -375,13 +435,9 @@ async function extractObsidianDmg(dmg, dest) {
375
435
  }
376
436
  async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
377
437
  const repo = "obsidianmd/obsidian-releases";
378
- const github = getGithubClient();
379
- let commitHistory = await github.paginate(github.rest.repos.listCommits, {
380
- owner: "obsidianmd",
381
- repo: "obsidian-releases",
438
+ let commitHistory = await fetchGitHubAPIPaginated(`repos/${repo}/commits`, {
382
439
  path: "desktop-releases.json",
383
- since: sinceDate,
384
- per_page: 100
440
+ since: sinceDate
385
441
  });
386
442
  commitHistory.reverse();
387
443
  if (sinceSha) {
@@ -392,19 +448,13 @@ async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
392
448
  commitHistory,
393
449
  (commit) => fetch(`https://raw.githubusercontent.com/${repo}/${commit.sha}/desktop-releases.json`).then((r) => r.json())
394
450
  );
395
- const commitDate = commitHistory.at(-1)?.commit.committer?.date ?? sinceDate;
451
+ const commitDate = commitHistory.at(-1)?.commit.committer.date ?? sinceDate;
396
452
  const commitSha = commitHistory.at(-1)?.sha ?? sinceSha;
397
453
  return [fileHistory, { commitDate, commitSha }];
398
454
  }
399
455
  async function fetchObsidianGitHubReleases() {
400
- const github = getGithubClient();
401
- let gitHubReleases = await github.paginate(github.rest.repos.listReleases, {
402
- owner: "obsidianmd",
403
- repo: "obsidian-releases",
404
- per_page: 100
405
- });
406
- gitHubReleases = gitHubReleases.reverse();
407
- return gitHubReleases;
456
+ const gitHubReleases = await fetchGitHubAPIPaginated(`repos/obsidianmd/obsidian-releases/releases`);
457
+ return gitHubReleases.reverse();
408
458
  }
409
459
  var BROKEN_ASSETS = [
410
460
  "https://releases.obsidian.md/release/obsidian-0.12.16.asar.gz",
@@ -518,7 +568,11 @@ function updateObsidianVersionList(args) {
518
568
  minInstallerVersion = version;
519
569
  }
520
570
  }
521
- newVersions[version] = _3.merge({ minInstallerVersion, maxInstallerVersion }, newVersions[version]);
571
+ newVersions[version] = _3.merge(newVersions[version], {
572
+ minInstallerVersion: newVersions[version]?.minInstallerVersion ?? minInstallerVersion,
573
+ maxInstallerVersion
574
+ // override maxInstallerVersion if it was already set
575
+ });
522
576
  }
523
577
  for (const installerInfo of installerInfos) {
524
578
  newVersions[installerInfo.version] = _3.merge(newVersions[installerInfo.version] ?? {}, {
@@ -677,10 +731,10 @@ var ObsidianLauncher = class {
677
731
  return d;
678
732
  }));
679
733
  if (response.success) {
680
- await atomicCreate(dest, async (tmpDir) => {
681
- await fsAsync4.writeFile(path5.join(tmpDir, "download.json"), response.result);
682
- return path5.join(tmpDir, "download.json");
683
- });
734
+ await atomicCreate(dest, async (scratch) => {
735
+ await fsAsync4.writeFile(path5.join(scratch, "download.json"), response.result);
736
+ return path5.join(scratch, "download.json");
737
+ }, { replace: true });
684
738
  data = JSON.parse(response.result);
685
739
  } else {
686
740
  error = response.error;
@@ -747,7 +801,7 @@ var ObsidianLauncher = class {
747
801
  * Resolves Obsidian app and installer version strings to absolute versions.
748
802
  * @param appVersion specific version or one of
749
803
  * - "latest": Get the current latest non-beta Obsidian version
750
- * - "latest-beta": Get the current latest beta Obsidian version (or latest is there is no current beta)
804
+ * - "latest-beta": Get the current latest beta Obsidian version (or latest if there is no current beta)
751
805
  * - "earliest": Get the `minAppVersion` set in your `manifest.json`
752
806
  * @param installerVersion specific version or one of
753
807
  * - "latest": Get the latest Obsidian installer compatible with `appVersion`
@@ -881,31 +935,29 @@ var ObsidianLauncher = class {
881
935
  const versionInfo = await this.getVersionInfo(installerVersion);
882
936
  installerVersion = versionInfo.version;
883
937
  const installerInfo = await this.getInstallerInfo(installerVersion, { platform, arch });
884
- const cacheDir = path5.join(this.cacheDir, `obsidian-installer/${platform}-${arch}/Obsidian-${installerVersion}`);
938
+ const installerDir = path5.join(this.cacheDir, `obsidian-installer/${platform}-${arch}/Obsidian-${installerVersion}`);
885
939
  let binaryPath;
886
940
  let extractor;
887
941
  if (platform == "linux") {
888
- binaryPath = path5.join(cacheDir, "obsidian");
942
+ binaryPath = path5.join(installerDir, "obsidian");
889
943
  extractor = (installer, dest) => extractObsidianAppImage(installer, dest);
890
944
  } else if (platform == "win32") {
891
- binaryPath = path5.join(cacheDir, "Obsidian.exe");
945
+ binaryPath = path5.join(installerDir, "Obsidian.exe");
892
946
  extractor = (installer, dest) => extractObsidianExe(installer, arch, dest);
893
947
  } else if (platform == "darwin") {
894
- binaryPath = path5.join(cacheDir, "Contents/MacOS/Obsidian");
948
+ binaryPath = path5.join(installerDir, "Contents/MacOS/Obsidian");
895
949
  extractor = (installer, dest) => extractObsidianDmg(installer, dest);
896
950
  } else {
897
951
  throw Error(`Unsupported platform ${platform}`);
898
952
  }
899
- if (!await fileExists(binaryPath)) {
953
+ await atomicCreate(installerDir, async (scratch) => {
900
954
  console.log(`Downloading Obsidian installer v${installerVersion}...`);
901
- await atomicCreate(cacheDir, async (tmpDir) => {
902
- const installer = path5.join(tmpDir, "installer");
903
- await downloadResponse(await fetch(installerInfo.url), installer);
904
- const extracted = path5.join(tmpDir, "extracted");
905
- await extractor(installer, extracted);
906
- return extracted;
907
- });
908
- }
955
+ const installer = path5.join(scratch, "installer");
956
+ await downloadResponse(await fetch(installerInfo.url), installer);
957
+ const extracted = path5.join(scratch, "extracted");
958
+ await extractor(installer, extracted);
959
+ return extracted;
960
+ }, { replace: false });
909
961
  return binaryPath;
910
962
  }
911
963
  /**
@@ -924,29 +976,27 @@ var ObsidianLauncher = class {
924
976
  throw Error(`No asar found for Obsidian version ${appVersion}`);
925
977
  }
926
978
  const appPath = path5.join(this.cacheDir, "obsidian-app", `obsidian-${versionInfo.version}.asar`);
927
- if (!await fileExists(appPath)) {
928
- console.log(`Downloading Obsidian app v${versionInfo.version} ...`);
929
- await atomicCreate(appPath, async (tmpDir) => {
930
- const isInsiders = new URL(appUrl).hostname.endsWith(".obsidian.md");
931
- let response;
932
- if (isInsiders) {
933
- if (!this.obsidianApiToken) {
934
- this.obsidianApiToken = await obsidianApiLogin({
935
- interactive: this.interactive,
936
- savePath: path5.join(this.cacheDir, "obsidian-credentials.env")
937
- });
938
- }
939
- response = await fetchObsidianApi(appUrl, { token: this.obsidianApiToken });
940
- } else {
941
- response = await fetch(appUrl);
942
- }
943
- const archive = path5.join(tmpDir, "app.asar.gz");
944
- const asar = path5.join(tmpDir, "app.asar");
945
- await downloadResponse(response, archive);
946
- await extractGz(archive, asar);
947
- return asar;
979
+ const isInsiders = new URL(appUrl).hostname.endsWith(".obsidian.md");
980
+ if (isInsiders && !this.obsidianApiToken && !await fileExists(appPath)) {
981
+ this.obsidianApiToken = await obsidianApiLogin({
982
+ interactive: this.interactive,
983
+ savePath: path5.join(this.cacheDir, "obsidian-credentials.env")
948
984
  });
949
985
  }
986
+ await atomicCreate(appPath, async (scratch) => {
987
+ console.log(`Downloading Obsidian app v${versionInfo.version} ...`);
988
+ let response;
989
+ if (isInsiders) {
990
+ response = await fetchObsidianApi(appUrl, { token: this.obsidianApiToken });
991
+ } else {
992
+ response = await fetch(appUrl);
993
+ }
994
+ const archive = path5.join(scratch, "app.asar.gz");
995
+ const asar = path5.join(scratch, "app.asar");
996
+ await downloadResponse(response, archive);
997
+ await extractGz(archive, asar);
998
+ return asar;
999
+ }, { replace: false });
950
1000
  return appPath;
951
1001
  }
952
1002
  /**
@@ -963,26 +1013,24 @@ var ObsidianLauncher = class {
963
1013
  async downloadChromedriver(installerVersion, opts = {}) {
964
1014
  const { platform, arch } = _4.defaults({}, opts, currentPlatform);
965
1015
  const installerInfo = await this.getInstallerInfo(installerVersion, { platform, arch });
966
- const cacheDir = path5.join(this.cacheDir, `electron-chromedriver/${platform}-${arch}/${installerInfo.electron}`);
1016
+ const chromedriverDir = path5.join(this.cacheDir, `electron-chromedriver/${platform}-${arch}/${installerInfo.electron}`);
967
1017
  let chromedriverPath;
968
1018
  if (process.platform == "win32") {
969
- chromedriverPath = path5.join(cacheDir, `chromedriver.exe`);
1019
+ chromedriverPath = path5.join(chromedriverDir, `chromedriver.exe`);
970
1020
  } else {
971
- chromedriverPath = path5.join(cacheDir, `chromedriver`);
1021
+ chromedriverPath = path5.join(chromedriverDir, `chromedriver`);
972
1022
  }
973
- if (!await fileExists(chromedriverPath)) {
1023
+ await atomicCreate(chromedriverDir, async (scratch) => {
974
1024
  console.log(`Downloading chromedriver for electron ${installerInfo.electron} ...`);
975
- await atomicCreate(cacheDir, async (tmpDir) => {
976
- const chromedriverZipPath = await downloadArtifact({
977
- version: installerInfo.electron,
978
- artifactName: "chromedriver",
979
- cacheRoot: path5.join(tmpDir, "download")
980
- });
981
- const extracted = path5.join(tmpDir, "extracted");
982
- await extractZip(chromedriverZipPath, { dir: extracted });
983
- return extracted;
1025
+ const chromedriverZipPath = await downloadArtifact({
1026
+ version: installerInfo.electron,
1027
+ artifactName: "chromedriver",
1028
+ cacheRoot: path5.join(scratch, "download")
984
1029
  });
985
- }
1030
+ const extracted = path5.join(scratch, "extracted");
1031
+ await extractZip(chromedriverZipPath, { dir: extracted });
1032
+ return extracted;
1033
+ }, { replace: false });
986
1034
  return chromedriverPath;
987
1035
  }
988
1036
  /**
@@ -997,14 +1045,12 @@ var ObsidianLauncher = class {
997
1045
  );
998
1046
  }
999
1047
  const apkPath = path5.join(this.cacheDir, "obsidian-apk", `obsidian-${versionInfo.version}.apk`);
1000
- if (!await fileExists(apkPath)) {
1048
+ await atomicCreate(apkPath, async (scratch) => {
1001
1049
  console.log(`Downloading Obsidian apk v${versionInfo.version} ...`);
1002
- await atomicCreate(apkPath, async (tmpDir) => {
1003
- const dest = path5.join(tmpDir, "obsidian.apk");
1004
- await downloadResponse(await fetch(apkUrl), dest);
1005
- return dest;
1006
- });
1007
- }
1050
+ const dest = path5.join(scratch, "obsidian.apk");
1051
+ await downloadResponse(await fetch(apkUrl), dest);
1052
+ return dest;
1053
+ }, { replace: false });
1008
1054
  return apkPath;
1009
1055
  }
1010
1056
  /** Gets the latest version of a plugin. */
@@ -1031,23 +1077,21 @@ var ObsidianLauncher = class {
1031
1077
  }
1032
1078
  version = semver2.valid(version);
1033
1079
  const pluginDir = path5.join(this.cacheDir, "obsidian-plugins", repo, version);
1034
- if (!await fileExists(pluginDir)) {
1035
- await atomicCreate(pluginDir, async (tmpDir) => {
1036
- const assetsToDownload = { "manifest.json": true, "main.js": true, "styles.css": false };
1037
- await Promise.all(
1038
- Object.entries(assetsToDownload).map(async ([file, required]) => {
1039
- const url = `https://github.com/${repo}/releases/download/${version}/${file}`;
1040
- const response = await fetch(url);
1041
- if (response.ok) {
1042
- await downloadResponse(response, path5.join(tmpDir, file));
1043
- } else if (required) {
1044
- throw Error(`No ${file} found for ${repo} version ${version}`);
1045
- }
1046
- })
1047
- );
1048
- return tmpDir;
1049
- });
1050
- }
1080
+ await atomicCreate(pluginDir, async (scratch) => {
1081
+ const assetsToDownload = { "manifest.json": true, "main.js": true, "styles.css": false };
1082
+ await Promise.all(
1083
+ Object.entries(assetsToDownload).map(async ([file, required]) => {
1084
+ const url = `https://github.com/${repo}/releases/download/${version}/${file}`;
1085
+ const response = await fetch(url);
1086
+ if (response.ok) {
1087
+ await downloadResponse(response, path5.join(scratch, file));
1088
+ } else if (required) {
1089
+ throw Error(`No ${file} found for ${repo} version ${version}`);
1090
+ }
1091
+ })
1092
+ );
1093
+ return scratch;
1094
+ }, { replace: false });
1051
1095
  return pluginDir;
1052
1096
  }
1053
1097
  /**
@@ -1199,31 +1243,29 @@ var ObsidianLauncher = class {
1199
1243
  }
1200
1244
  version = semver2.valid(version);
1201
1245
  const themeDir = path5.join(this.cacheDir, "obsidian-themes", repo, version);
1202
- if (!await fileExists(themeDir)) {
1203
- await atomicCreate(themeDir, async (tmpDir) => {
1204
- const assetsToDownload = ["manifest.json", "theme.css"];
1205
- let baseUrl = `https://github.com/${repo}/releases/download/${version}`;
1206
- if (!(await fetch(`${baseUrl}/manifest.json`)).ok) {
1207
- if (version != latest) {
1208
- throw Error(`No theme version "${version}" found`);
1209
- }
1210
- baseUrl = `https://raw.githubusercontent.com/${repo}/HEAD`;
1246
+ await atomicCreate(themeDir, async (scratch) => {
1247
+ const assetsToDownload = ["manifest.json", "theme.css"];
1248
+ let baseUrl = `https://github.com/${repo}/releases/download/${version}`;
1249
+ if (!(await fetch(`${baseUrl}/manifest.json`)).ok) {
1250
+ if (version != latest) {
1251
+ throw Error(`No theme version "${version}" found`);
1211
1252
  }
1212
- await Promise.all(
1213
- assetsToDownload.map(
1214
- async (file) => {
1215
- const url = `${baseUrl}/${file}`;
1216
- const response = await fetch(url);
1217
- if (response.ok) {
1218
- await downloadResponse(response, path5.join(tmpDir, file));
1219
- } else {
1220
- throw Error(`No ${file} found for ${repo}`);
1221
- }
1253
+ baseUrl = `https://raw.githubusercontent.com/${repo}/HEAD`;
1254
+ }
1255
+ await Promise.all(
1256
+ assetsToDownload.map(
1257
+ async (file) => {
1258
+ const url = `${baseUrl}/${file}`;
1259
+ const response = await fetch(url);
1260
+ if (response.ok) {
1261
+ await downloadResponse(response, path5.join(scratch, file));
1262
+ } else {
1263
+ throw Error(`No ${file} found for ${repo}`);
1222
1264
  }
1223
- )
1224
- );
1225
- });
1226
- }
1265
+ }
1266
+ )
1267
+ );
1268
+ }, { replace: false });
1227
1269
  return themeDir;
1228
1270
  }
1229
1271
  /**
@@ -1541,4 +1583,4 @@ export {
1541
1583
  watchFiles,
1542
1584
  ObsidianLauncher
1543
1585
  };
1544
- //# sourceMappingURL=chunk-OUPK2DAJ.js.map
1586
+ //# sourceMappingURL=chunk-HMKNUWAY.js.map