obsidian-launcher 2.0.0 → 2.0.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.
@@ -5,9 +5,9 @@ import path from "path";
5
5
  import os from "os";
6
6
  import { PromisePool } from "@supercharge/promise-pool";
7
7
  import _ from "lodash";
8
- async function fileExists(path5) {
8
+ async function fileExists(path6) {
9
9
  try {
10
- await fsAsync.access(path5);
10
+ await fsAsync.access(path6);
11
11
  return true;
12
12
  } catch {
13
13
  return false;
@@ -16,10 +16,11 @@ async function fileExists(path5) {
16
16
  async function makeTmpDir(prefix) {
17
17
  return fsAsync.mkdtemp(path.join(os.tmpdir(), prefix ?? "tmp-"));
18
18
  }
19
- async function atomicCreate(dest, func, options = {}) {
19
+ async function atomicCreate(dest, func, opts = {}) {
20
20
  dest = path.resolve(dest);
21
21
  const createdParentDir = await fsAsync.mkdir(path.dirname(dest), { recursive: true });
22
22
  const tmpDir = await fsAsync.mkdtemp(path.join(path.dirname(dest), `.${path.basename(dest)}.tmp.`));
23
+ let success = false;
23
24
  try {
24
25
  let result = await func(tmpDir) ?? tmpDir;
25
26
  result = path.resolve(tmpDir, result);
@@ -30,13 +31,14 @@ async function atomicCreate(dest, func, options = {}) {
30
31
  await fsAsync.rename(dest, tmpDir + ".old");
31
32
  }
32
33
  await fsAsync.rename(result, dest);
33
- await fsAsync.rm(tmpDir + ".old", { recursive: true, force: true });
34
- await fsAsync.rm(tmpDir, { recursive: true, force: true });
35
- } catch (e) {
36
- if (!options.preserveTmpDir) {
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) {
37
40
  await fsAsync.rm(createdParentDir ?? tmpDir, { recursive: true, force: true });
38
41
  }
39
- throw e;
40
42
  }
41
43
  }
42
44
  async function linkOrCp(src, dest) {
@@ -47,6 +49,9 @@ async function linkOrCp(src, dest) {
47
49
  await fsAsync.copyFile(src, dest);
48
50
  }
49
51
  }
52
+ async function sleep(ms) {
53
+ return new Promise((resolve) => setTimeout(resolve, ms));
54
+ }
50
55
  async function pool(size, items, func) {
51
56
  const { results } = await PromisePool.for(items).withConcurrency(size).handleError(async (error) => {
52
57
  throw error;
@@ -89,14 +94,15 @@ function normalizeObject(canonical, obj) {
89
94
 
90
95
  // src/launcher.ts
91
96
  import fsAsync4 from "fs/promises";
92
- import path4 from "path";
97
+ import path5 from "path";
93
98
  import crypto from "crypto";
94
99
  import extractZip from "extract-zip";
95
100
  import { downloadArtifact } from "@electron/get";
96
101
  import child_process2 from "child_process";
97
102
  import semver2 from "semver";
98
103
  import { fileURLToPath as fileURLToPath2 } from "url";
99
- import dotenv from "dotenv";
104
+ import _4 from "lodash";
105
+ import dotenv2 from "dotenv";
100
106
 
101
107
  // src/types.ts
102
108
  var obsidianVersionsSchemaVersion = "2.0.0";
@@ -108,6 +114,9 @@ import { finished } from "stream/promises";
108
114
  import { Readable } from "stream";
109
115
  import fsAsync2 from "fs/promises";
110
116
  import readlineSync from "readline-sync";
117
+ import dotenv from "dotenv";
118
+ import path2 from "path";
119
+ import { env } from "process";
111
120
  function parseLinkHeader(linkHeader) {
112
121
  function parseLinkData(linkData) {
113
122
  return Object.fromEntries(
@@ -141,7 +150,7 @@ function createURL(url, base, params = {}) {
141
150
  }
142
151
  async function fetchGitHubAPI(url, params = {}) {
143
152
  url = createURL(url, "https://api.github.com", params);
144
- const token = process.env.GITHUB_TOKEN;
153
+ const token = env.GITHUB_TOKEN;
145
154
  const headers = token ? { Authorization: "Bearer " + token } : {};
146
155
  const response = await fetch(url, { headers });
147
156
  if (!response.ok) {
@@ -161,9 +170,9 @@ async function fetchGitHubAPIPaginated(url, params = {}) {
161
170
  }
162
171
  async function obsidianApiLogin(opts) {
163
172
  const { interactive = false, savePath } = opts;
164
- let email = process.env.OBSIDIAN_EMAIL;
165
- let password = process.env.OBSIDIAN_PASSWORD;
166
- let mfa;
173
+ if (savePath) dotenv.config({ path: [savePath], quiet: true });
174
+ let email = env.OBSIDIAN_EMAIL;
175
+ let password = env.OBSIDIAN_PASSWORD;
167
176
  if (!email || !password) {
168
177
  if (interactive) {
169
178
  console.log("Obsidian Insiders account is required to download Obsidian beta versions.");
@@ -171,50 +180,53 @@ async function obsidianApiLogin(opts) {
171
180
  password = password || readlineSync.question("Obsidian password: ", { hideEchoBack: true });
172
181
  } else {
173
182
  throw Error(
174
- "Obsidian Insiders account is required to download Obsidian beta versions. Either set the OBSIDIAN_EMAIL and OBSIDIAN_PASSWORD env vars (.env is supported) or pre-download the Obsidian beta with `npx obsidian-launcher download -v <version>`"
183
+ "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 -v <version>`"
175
184
  );
176
185
  }
177
186
  }
178
- const headers = {
179
- "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",
180
- "Origin": "app://obsidian.md",
181
- "Content-Type": "application/json"
182
- };
183
- let signin = await fetch("https://api.obsidian.md/user/signin", {
184
- method: "post",
185
- headers,
186
- body: JSON.stringify({ email, password, mfa: "" })
187
- }).then((r) => r.json());
188
- if (signin.error && signin.error.includes("2FA")) {
189
- if (interactive) {
190
- mfa = await readlineSync.question("Obsidian 2FA: ");
191
- signin = await fetch("https://api.obsidian.md/user/signin", {
192
- method: "post",
193
- headers,
194
- body: JSON.stringify({ email, password, mfa })
195
- }).then((r) => r.json());
196
- } else {
197
- throw Error(
198
- "Can't login with 2FA in a non-interactive session. To download Obsidian beta versions, either disable 2FA on your account or pre-download the Obsidian beta with `npx obsidian-launcher download -v <version>`"
199
- );
187
+ let needsMfa = false;
188
+ let retries = 0;
189
+ let signin = void 0;
190
+ while (!signin?.token && retries < 3) {
191
+ if (retries > 0 || env.CI) {
192
+ await sleep(2 * Math.random() + retries * retries * 2);
193
+ }
194
+ let mfa = "";
195
+ if (needsMfa && interactive) {
196
+ mfa = readlineSync.question("Obsidian 2FA: ");
197
+ }
198
+ signin = await fetch("https://api.obsidian.md/user/signin", {
199
+ method: "post",
200
+ headers: {
201
+ "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",
202
+ "Origin": "app://obsidian.md",
203
+ "Content-Type": "application/json"
204
+ },
205
+ body: JSON.stringify({ email, password, mfa })
206
+ }).then((r) => r.json());
207
+ const error = signin.error?.toLowerCase();
208
+ if (error?.includes("2fa") && !needsMfa) {
209
+ needsMfa = true;
210
+ if (!interactive) {
211
+ throw Error(
212
+ "Can't login with 2FA in a non-interactive session. To download Obsidian beta versions, either disable 2FA on your account or pre-download the Obsidian beta with `npx obsidian-launcher download -v <version>`"
213
+ );
214
+ }
215
+ } else if (["please wait", "try again"].some((m) => error?.includes(m))) {
216
+ console.warn(`Obsidian login failed: ${signin.error}`);
217
+ retries++;
218
+ } else if (!signin.token) {
219
+ throw Error(`Obsidian login failed: ${signin.error ?? "unknown error"}`);
200
220
  }
201
221
  }
202
- let error = void 0;
203
222
  if (!signin.token) {
204
- error = Error(`Obsidian login failed: ${signin.error}`);
205
- }
206
- if (!error && !signin.license) {
207
- error = Error("Obsidian Insiders account is required to download Obsidian beta versions");
208
- }
209
- if (error) {
210
- if (savePath) {
211
- await fsAsync2.rm(savePath, { force: true });
212
- }
213
- throw error;
223
+ throw Error(`Obsidian login failed: ${signin.error ?? "unknown error"}`);
224
+ } else if (!signin.license) {
225
+ throw Error("Obsidian Insiders account is required to download Obsidian beta versions");
214
226
  }
215
- if (interactive && savePath && (!process.env.OBSIDIAN_EMAIL || !process.env.OBSIDIAN_PASSWORD)) {
216
- const save = readlineSync.question("Save credentails to disk? [y/n]: ");
217
- if (["y", "yes"].includes(save.toLocaleLowerCase())) {
227
+ if (interactive && savePath && (!env.OBSIDIAN_EMAIL || !env.OBSIDIAN_PASSWORD)) {
228
+ const save = readlineSync.question("Cache credentails to disk? [y/n]: ");
229
+ if (["y", "yes"].includes(save.toLowerCase())) {
218
230
  await fsAsync2.writeFile(
219
231
  savePath,
220
232
  `OBSIDIAN_EMAIL='${email}'
@@ -222,6 +234,7 @@ OBSIDIAN_PASSWORD='${password}'
222
234
  `
223
235
  );
224
236
  }
237
+ console.log(`Saved Obsidian credentials to ${path2.relative(process.cwd(), savePath)}`);
225
238
  }
226
239
  return signin.token;
227
240
  }
@@ -246,7 +259,7 @@ async function downloadResponse(response, dest) {
246
259
  }
247
260
 
248
261
  // src/chromeLocalStorage.ts
249
- import path2 from "path";
262
+ import path3 from "path";
250
263
  import { ClassicLevel } from "classic-level";
251
264
  var ChromeLocalStorage = class {
252
265
  /** Pass the path to the user data dir for Chrome/Electron. If it doesn't exist it will be created. */
@@ -259,7 +272,7 @@ var ChromeLocalStorage = class {
259
272
  };
260
273
  this.encodeValue = (value) => `${value}`;
261
274
  this.decodeValue = (value) => value.slice(1);
262
- this.db = new ClassicLevel(path2.join(userDataDir, "Local Storage/leveldb/"));
275
+ this.db = new ClassicLevel(path3.join(userDataDir, "Local Storage/leveldb/"));
263
276
  }
264
277
  /**
265
278
  * Get a value from localStorage
@@ -324,7 +337,7 @@ var ChromeLocalStorage = class {
324
337
  // src/launcherUtils.ts
325
338
  import fsAsync3 from "fs/promises";
326
339
  import fs3 from "fs";
327
- import path3 from "path";
340
+ import path4 from "path";
328
341
  import child_process from "child_process";
329
342
  import semver from "semver";
330
343
  import _3 from "lodash";
@@ -338,7 +351,7 @@ async function extractGz(archive, dest) {
338
351
  await pipeline(fs3.createReadStream(archive), zlib.createGunzip(), fs3.createWriteStream(dest));
339
352
  }
340
353
  async function sevenZ(args, options) {
341
- const sevenZipScript = path3.resolve(fileURLToPath(import.meta.url), "../7z.js");
354
+ const sevenZipScript = path4.resolve(fileURLToPath(import.meta.url), "../7z.js");
342
355
  const proc = child_process.spawn(process.execPath, [sevenZipScript, ...args], {
343
356
  stdio: "pipe",
344
357
  ...options
@@ -358,13 +371,13 @@ ${stderr}`);
358
371
  }
359
372
  async function extractObsidianAppImage(appImage, dest) {
360
373
  await atomicCreate(dest, async (tmpDir) => {
361
- await sevenZ(["x", "-o.", path3.relative(tmpDir, appImage)], { cwd: tmpDir });
374
+ await sevenZ(["x", "-o.", path4.relative(tmpDir, appImage)], { cwd: tmpDir });
362
375
  return tmpDir;
363
376
  });
364
377
  }
365
378
  async function extractObsidianTar(tar, dest) {
366
379
  await atomicCreate(dest, async (tmpDir) => {
367
- await extractGz(tar, path3.join(tmpDir, "inflated.tar"));
380
+ await extractGz(tar, path4.join(tmpDir, "inflated.tar"));
368
381
  await sevenZ(["x", "-o.", "inflated.tar"], { cwd: tmpDir });
369
382
  return (await fsAsync3.readdir(tmpDir)).find((p) => p.match("obsidian-"));
370
383
  });
@@ -381,20 +394,20 @@ async function extractObsidianExe(exe, arch, dest) {
381
394
  throw Error(`No Obsidian installer found for ${process.platform} ${process.arch}`);
382
395
  }
383
396
  await atomicCreate(dest, async (tmpDir) => {
384
- await sevenZ(["x", "-oinstaller", path3.relative(tmpDir, exe), subArchive], { cwd: tmpDir });
385
- await sevenZ(["x", "-oobsidian", path3.join("installer", subArchive)], { cwd: tmpDir });
397
+ await sevenZ(["x", "-oinstaller", path4.relative(tmpDir, exe), subArchive], { cwd: tmpDir });
398
+ await sevenZ(["x", "-oobsidian", path4.join("installer", subArchive)], { cwd: tmpDir });
386
399
  return "obsidian";
387
400
  });
388
401
  }
389
402
  async function extractObsidianDmg(dmg, dest) {
390
- dest = path3.resolve(dest);
403
+ dest = path4.resolve(dest);
391
404
  await atomicCreate(dest, async (tmpDir) => {
392
- await sevenZ(["x", "-o.", path3.relative(tmpDir, dmg), "*/Obsidian.app", "Obsidian.app"], { cwd: tmpDir });
405
+ await sevenZ(["x", "-o.", path4.relative(tmpDir, dmg), "*/Obsidian.app", "Obsidian.app"], { cwd: tmpDir });
393
406
  const files = await fsAsync3.readdir(tmpDir);
394
407
  if (files.includes("Obsidian.app")) {
395
408
  return "Obsidian.app";
396
409
  } else {
397
- return path3.join(files[0], "Obsidian.app");
410
+ return path4.join(files[0], "Obsidian.app");
398
411
  }
399
412
  });
400
413
  }
@@ -473,9 +486,9 @@ async function getInstallerInfo(installerKey, url) {
473
486
  console.log(`Extrating installer info for ${installerName}...`);
474
487
  const tmpDir = await makeTmpDir("obsidian-launcher-");
475
488
  try {
476
- const installerPath = path3.join(tmpDir, url.split("/").at(-1));
489
+ const installerPath = path4.join(tmpDir, url.split("/").at(-1));
477
490
  await downloadResponse(await fetch(url), installerPath);
478
- const exractedPath = path3.join(tmpDir, "Obsidian");
491
+ const exractedPath = path4.join(tmpDir, "Obsidian");
479
492
  let platforms = [];
480
493
  if (installerKey == "appImage" || installerKey == "appImageArm") {
481
494
  await extractObsidianAppImage(installerPath, exractedPath);
@@ -485,7 +498,7 @@ async function getInstallerInfo(installerKey, url) {
485
498
  platforms = ["linux-" + (installerKey == "tar" ? "x64" : "arm64")];
486
499
  } else if (installerKey == "exe") {
487
500
  await extractObsidianExe(installerPath, "x64", exractedPath);
488
- const { stdout } = await sevenZ(["l", "-ba", path3.relative(tmpDir, installerPath)], { cwd: tmpDir });
501
+ const { stdout } = await sevenZ(["l", "-ba", path4.relative(tmpDir, installerPath)], { cwd: tmpDir });
489
502
  const lines = stdout.trim().split("\n").map((l) => l.trim());
490
503
  const files = lines.map((l) => l.split(/\s+/).at(-1).replace(/\\/g, "/"));
491
504
  if (files.includes("$PLUGINSDIR/app-arm64.7z")) platforms.push("win32-arm64");
@@ -501,7 +514,7 @@ async function getInstallerInfo(installerKey, url) {
501
514
  const installerFiles = await fsAsync3.readdir(exractedPath, { recursive: true, withFileTypes: true });
502
515
  for (const file of installerFiles) {
503
516
  if (file.isFile() && !file.name.endsWith(".asar")) {
504
- const stream = fs3.createReadStream(path3.join(file.parentPath, file.name), { encoding: "utf-8" });
517
+ const stream = fs3.createReadStream(path4.join(file.parentPath, file.name), { encoding: "utf-8" });
505
518
  let prev = "";
506
519
  for await (let chunk of stream) {
507
520
  const regex = /Chrome\/\d+\.\d+\.\d+\.\d+|Electron\/\d+\.\d+\.\d+/g;
@@ -525,12 +538,11 @@ async function getInstallerInfo(installerKey, url) {
525
538
  }
526
539
  }
527
540
  function normalizeObsidianVersionInfo(versionInfo) {
528
- versionInfo = {
529
- ...versionInfo,
530
- // kept for backwards compatibility
531
- electronVersion: versionInfo.installers?.appImage?.electron,
532
- chromeVersion: versionInfo.installers?.appImage?.chrome
533
- };
541
+ versionInfo = _3.cloneDeep(versionInfo);
542
+ versionInfo.electronVersion = versionInfo.installers?.appImage?.electron;
543
+ versionInfo.chromeVersion = versionInfo.installers?.appImage?.chrome;
544
+ versionInfo.downloads = versionInfo.downloads ?? {};
545
+ versionInfo.installers = versionInfo.installers ?? {};
534
546
  const canonicalForm = {
535
547
  version: null,
536
548
  minInstallerVersion: null,
@@ -562,11 +574,11 @@ function normalizeObsidianVersionInfo(versionInfo) {
562
574
  }
563
575
 
564
576
  // src/launcher.ts
565
- import _4 from "lodash";
566
577
  var currentPlatform = {
567
578
  platform: process.platform,
568
579
  arch: process.arch
569
580
  };
581
+ dotenv2.config({ path: [".env"], quiet: true });
570
582
  var ObsidianLauncher = class {
571
583
  /**
572
584
  * Construct an ObsidianLauncher.
@@ -579,7 +591,7 @@ var ObsidianLauncher = class {
579
591
  */
580
592
  constructor(opts = {}) {
581
593
  this.interactive = false;
582
- this.cacheDir = path4.resolve(opts.cacheDir ?? process.env.OBSIDIAN_CACHE ?? "./.obsidian-cache");
594
+ this.cacheDir = path5.resolve(opts.cacheDir ?? process.env.OBSIDIAN_CACHE ?? "./.obsidian-cache");
583
595
  const defaultVersionsUrl = "https://raw.githubusercontent.com/jesse-r-s-hines/wdio-obsidian-service/HEAD/obsidian-versions.json";
584
596
  this.versionsUrl = opts.versionsUrl ?? defaultVersionsUrl;
585
597
  const defaultCommunityPluginsUrl = "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/HEAD/community-plugins.json";
@@ -589,10 +601,6 @@ var ObsidianLauncher = class {
589
601
  this.cacheDuration = opts.cacheDuration ?? 30 * 60 * 1e3;
590
602
  this.interactive = opts.interactive ?? false;
591
603
  this.metadataCache = {};
592
- dotenv.config({
593
- path: [".env", path4.join(this.cacheDir, "obsidian-credentials.env")],
594
- quiet: true
595
- });
596
604
  }
597
605
  /**
598
606
  * Returns file content fetched from url as JSON. Caches content to dest and uses that cache if its more recent than
@@ -600,7 +608,7 @@ var ObsidianLauncher = class {
600
608
  */
601
609
  async cachedFetch(url, dest, cacheValid) {
602
610
  cacheValid = cacheValid ?? (() => true);
603
- dest = path4.join(this.cacheDir, dest);
611
+ dest = path5.join(this.cacheDir, dest);
604
612
  if (!(dest in this.metadataCache)) {
605
613
  let data;
606
614
  let error;
@@ -623,8 +631,8 @@ var ObsidianLauncher = class {
623
631
  }));
624
632
  if (response.success) {
625
633
  await atomicCreate(dest, async (tmpDir) => {
626
- await fsAsync4.writeFile(path4.join(tmpDir, "download.json"), response.result);
627
- return path4.join(tmpDir, "download.json");
634
+ await fsAsync4.writeFile(path5.join(tmpDir, "download.json"), response.result);
635
+ return path5.join(tmpDir, "download.json");
628
636
  });
629
637
  data = JSON.parse(response.result);
630
638
  } else {
@@ -651,12 +659,12 @@ var ObsidianLauncher = class {
651
659
  */
652
660
  async getRootManifest() {
653
661
  if (!("manifest.json" in this.metadataCache)) {
654
- const root = path4.parse(process.cwd()).root;
662
+ const root = path5.parse(process.cwd()).root;
655
663
  let dir = process.cwd();
656
- while (dir != root && !await fileExists(path4.join(dir, "manifest.json"))) {
657
- dir = path4.dirname(dir);
664
+ while (dir != root && !await fileExists(path5.join(dir, "manifest.json"))) {
665
+ dir = path5.dirname(dir);
658
666
  }
659
- const manifestPath = path4.join(dir, "manifest.json");
667
+ const manifestPath = path5.join(dir, "manifest.json");
660
668
  if (await fileExists(manifestPath)) {
661
669
  this.metadataCache["manifest.json"] = JSON.parse(await fsAsync4.readFile(manifestPath, "utf-8"));
662
670
  } else {
@@ -779,12 +787,12 @@ var ObsidianLauncher = class {
779
787
  * @returns [appVersion, installerVersion][] resolved to specific versions.
780
788
  */
781
789
  async parseVersions(versions) {
782
- let parsedVersions = versions.split(/[ ,]/).filter((v) => v).map((v) => {
790
+ const parsedVersions = versions.split(/[ ,]/).filter((v) => v).map((v) => {
783
791
  const [appVersion, installerVersion = "earliest"] = v.split("/");
784
792
  return [appVersion, installerVersion];
785
793
  });
786
- let resolvedVersions = [];
787
- for (let [appVersion, installerVersion] of parsedVersions) {
794
+ const resolvedVersions = [];
795
+ for (const [appVersion, installerVersion] of parsedVersions) {
788
796
  resolvedVersions.push(await this.resolveVersion(appVersion, installerVersion));
789
797
  }
790
798
  return _4.uniqBy(resolvedVersions, (v) => v.join("/"));
@@ -825,17 +833,17 @@ var ObsidianLauncher = class {
825
833
  const versionInfo = await this.getVersionInfo(installerVersion);
826
834
  installerVersion = versionInfo.version;
827
835
  const installerInfo = await this.getInstallerInfo(installerVersion, { platform, arch });
828
- const cacheDir = path4.join(this.cacheDir, `obsidian-installer/${platform}-${arch}/Obsidian-${installerVersion}`);
836
+ const cacheDir = path5.join(this.cacheDir, `obsidian-installer/${platform}-${arch}/Obsidian-${installerVersion}`);
829
837
  let binaryPath;
830
838
  let extractor;
831
839
  if (platform == "linux") {
832
- binaryPath = path4.join(cacheDir, "obsidian");
840
+ binaryPath = path5.join(cacheDir, "obsidian");
833
841
  extractor = (installer, dest) => extractObsidianAppImage(installer, dest);
834
842
  } else if (platform == "win32") {
835
- binaryPath = path4.join(cacheDir, "Obsidian.exe");
843
+ binaryPath = path5.join(cacheDir, "Obsidian.exe");
836
844
  extractor = (installer, dest) => extractObsidianExe(installer, arch, dest);
837
845
  } else if (platform == "darwin") {
838
- binaryPath = path4.join(cacheDir, "Contents/MacOS/Obsidian");
846
+ binaryPath = path5.join(cacheDir, "Contents/MacOS/Obsidian");
839
847
  extractor = (installer, dest) => extractObsidianDmg(installer, dest);
840
848
  } else {
841
849
  throw Error(`Unsupported platform ${platform}`);
@@ -843,9 +851,9 @@ var ObsidianLauncher = class {
843
851
  if (!await fileExists(binaryPath)) {
844
852
  console.log(`Downloading Obsidian installer v${installerVersion}...`);
845
853
  await atomicCreate(cacheDir, async (tmpDir) => {
846
- const installer = path4.join(tmpDir, "installer");
854
+ const installer = path5.join(tmpDir, "installer");
847
855
  await downloadResponse(await fetch(installerInfo.url), installer);
848
- const extracted = path4.join(tmpDir, "extracted");
856
+ const extracted = path5.join(tmpDir, "extracted");
849
857
  await extractor(installer, extracted);
850
858
  return extracted;
851
859
  });
@@ -855,9 +863,9 @@ var ObsidianLauncher = class {
855
863
  /**
856
864
  * Downloads the Obsidian asar for the given version. Returns the file path.
857
865
  *
858
- * To download Obsidian beta versions you'll need have an Obsidian Insiders account and either set the
859
- * `OBSIDIAN_EMAIL` and `OBSIDIAN_PASSWORD` env vars (`.env` is supported) or pre-download the Obsidian beta with
860
- * `npx obsidian-launcher download -v latest-beta`
866
+ * To download Obsidian beta versions you'll need to have an Obsidian Insiders account and either set the
867
+ * `OBSIDIAN_EMAIL` and `OBSIDIAN_PASSWORD` env vars (`.env` file is supported) or pre-download the Obsidian beta
868
+ * with `npx obsidian-launcher download -v latest-beta`
861
869
  *
862
870
  * @param appVersion Obsidian version to download
863
871
  */
@@ -867,7 +875,7 @@ var ObsidianLauncher = class {
867
875
  if (!appUrl) {
868
876
  throw Error(`No asar found for Obsidian version ${appVersion}`);
869
877
  }
870
- const appPath = path4.join(this.cacheDir, "obsidian-app", `obsidian-${versionInfo.version}.asar`);
878
+ const appPath = path5.join(this.cacheDir, "obsidian-app", `obsidian-${versionInfo.version}.asar`);
871
879
  if (!await fileExists(appPath)) {
872
880
  console.log(`Downloading Obsidian app v${versionInfo.version} ...`);
873
881
  await atomicCreate(appPath, async (tmpDir) => {
@@ -877,15 +885,15 @@ var ObsidianLauncher = class {
877
885
  if (!this.obsidianApiToken) {
878
886
  this.obsidianApiToken = await obsidianApiLogin({
879
887
  interactive: this.interactive,
880
- savePath: path4.join(this.cacheDir, "obsidian-credentials.env")
888
+ savePath: path5.join(this.cacheDir, "obsidian-credentials.env")
881
889
  });
882
890
  }
883
891
  response = await fetchObsidianApi(appUrl, { token: this.obsidianApiToken });
884
892
  } else {
885
893
  response = await fetch(appUrl);
886
894
  }
887
- const archive = path4.join(tmpDir, "app.asar.gz");
888
- const asar = path4.join(tmpDir, "app.asar");
895
+ const archive = path5.join(tmpDir, "app.asar.gz");
896
+ const asar = path5.join(tmpDir, "app.asar");
889
897
  await downloadResponse(response, archive);
890
898
  await extractGz(archive, asar);
891
899
  return asar;
@@ -907,12 +915,12 @@ var ObsidianLauncher = class {
907
915
  async downloadChromedriver(installerVersion, opts = {}) {
908
916
  const { platform, arch } = _4.merge({}, opts, currentPlatform);
909
917
  const installerInfo = await this.getInstallerInfo(installerVersion, { platform, arch });
910
- const cacheDir = path4.join(this.cacheDir, `electron-chromedriver/${platform}-${arch}/${installerInfo.electron}`);
918
+ const cacheDir = path5.join(this.cacheDir, `electron-chromedriver/${platform}-${arch}/${installerInfo.electron}`);
911
919
  let chromedriverPath;
912
920
  if (process.platform == "win32") {
913
- chromedriverPath = path4.join(cacheDir, `chromedriver.exe`);
921
+ chromedriverPath = path5.join(cacheDir, `chromedriver.exe`);
914
922
  } else {
915
- chromedriverPath = path4.join(cacheDir, `chromedriver`);
923
+ chromedriverPath = path5.join(cacheDir, `chromedriver`);
916
924
  }
917
925
  if (!await fileExists(chromedriverPath)) {
918
926
  console.log(`Downloading chromedriver for electron ${installerInfo.electron} ...`);
@@ -920,9 +928,9 @@ var ObsidianLauncher = class {
920
928
  const chromedriverZipPath = await downloadArtifact({
921
929
  version: installerInfo.electron,
922
930
  artifactName: "chromedriver",
923
- cacheRoot: path4.join(tmpDir, "download")
931
+ cacheRoot: path5.join(tmpDir, "download")
924
932
  });
925
- const extracted = path4.join(tmpDir, "extracted");
933
+ const extracted = path5.join(tmpDir, "extracted");
926
934
  await extractZip(chromedriverZipPath, { dir: extracted });
927
935
  return extracted;
928
936
  });
@@ -940,11 +948,11 @@ var ObsidianLauncher = class {
940
948
  `No apk found for Obsidian version ${version}` + (versionInfo.isBeta ? ` (${version} is a beta version)` : "")
941
949
  );
942
950
  }
943
- const apkPath = path4.join(this.cacheDir, "obsidian-apk", `obsidian-${versionInfo.version}.apk`);
951
+ const apkPath = path5.join(this.cacheDir, "obsidian-apk", `obsidian-${versionInfo.version}.apk`);
944
952
  if (!await fileExists(apkPath)) {
945
953
  console.log(`Downloading Obsidian apk v${versionInfo.version} ...`);
946
954
  await atomicCreate(apkPath, async (tmpDir) => {
947
- const dest = path4.join(tmpDir, "obsidian.apk");
955
+ const dest = path5.join(tmpDir, "obsidian.apk");
948
956
  await downloadResponse(await fetch(apkUrl), dest);
949
957
  return dest;
950
958
  });
@@ -955,7 +963,7 @@ var ObsidianLauncher = class {
955
963
  async getLatestPluginVersion(repo) {
956
964
  repo = normalizeGitHubRepo(repo);
957
965
  const manifestUrl = `https://raw.githubusercontent.com/${repo}/HEAD/manifest.json`;
958
- const cacheDest = path4.join("obsidian-plugins", repo, "latest.json");
966
+ const cacheDest = path5.join("obsidian-plugins", repo, "latest.json");
959
967
  const manifest = await this.cachedFetch(manifestUrl, cacheDest);
960
968
  return manifest.version;
961
969
  }
@@ -974,7 +982,7 @@ var ObsidianLauncher = class {
974
982
  throw Error(`Invalid version "${version}"`);
975
983
  }
976
984
  version = semver2.valid(version);
977
- const pluginDir = path4.join(this.cacheDir, "obsidian-plugins", repo, version);
985
+ const pluginDir = path5.join(this.cacheDir, "obsidian-plugins", repo, version);
978
986
  if (!await fileExists(pluginDir)) {
979
987
  await atomicCreate(pluginDir, async (tmpDir) => {
980
988
  const assetsToDownload = { "manifest.json": true, "main.js": true, "styles.css": false };
@@ -983,7 +991,7 @@ var ObsidianLauncher = class {
983
991
  const url = `https://github.com/${repo}/releases/download/${version}/${file}`;
984
992
  const response = await fetch(url);
985
993
  if (response.ok) {
986
- await downloadResponse(response, path4.join(tmpDir, file));
994
+ await downloadResponse(response, path5.join(tmpDir, file));
987
995
  } else if (required) {
988
996
  throw Error(`No ${file} found for ${repo} version ${version}`);
989
997
  }
@@ -1026,11 +1034,11 @@ var ObsidianLauncher = class {
1026
1034
  let pluginPath;
1027
1035
  let originalType;
1028
1036
  if (typeof plugin == "string") {
1029
- pluginPath = path4.resolve(plugin);
1037
+ pluginPath = path5.resolve(plugin);
1030
1038
  originalType = "local";
1031
1039
  } else if ("path" in plugin) {
1032
1040
  ;
1033
- pluginPath = path4.resolve(plugin.path);
1041
+ pluginPath = path5.resolve(plugin.path);
1034
1042
  originalType = "local";
1035
1043
  } else if ("repo" in plugin) {
1036
1044
  pluginPath = await this.downloadGitHubPlugin(plugin.repo, plugin.version);
@@ -1041,7 +1049,7 @@ var ObsidianLauncher = class {
1041
1049
  } else {
1042
1050
  throw Error("You must specify one of plugin path, repo, or id");
1043
1051
  }
1044
- const manifestPath = path4.join(pluginPath, "manifest.json");
1052
+ const manifestPath = path5.join(pluginPath, "manifest.json");
1045
1053
  if (!await fileExists(manifestPath)) {
1046
1054
  throw Error(`No plugin found at ${pluginPath}`);
1047
1055
  }
@@ -1052,7 +1060,7 @@ var ObsidianLauncher = class {
1052
1060
  throw Error(`${manifestPath} malformed.`);
1053
1061
  }
1054
1062
  }
1055
- if (!await fileExists(path4.join(pluginPath, "main.js"))) {
1063
+ if (!await fileExists(path5.join(pluginPath, "main.js"))) {
1056
1064
  throw Error(`No main.js found under ${pluginPath}`);
1057
1065
  }
1058
1066
  let enabled;
@@ -1069,7 +1077,7 @@ var ObsidianLauncher = class {
1069
1077
  async getLatestThemeVersion(repo) {
1070
1078
  repo = normalizeGitHubRepo(repo);
1071
1079
  const manifestUrl = `https://raw.githubusercontent.com/${repo}/HEAD/manifest.json`;
1072
- const cacheDest = path4.join("obsidian-themes", repo, "latest.json");
1080
+ const cacheDest = path5.join("obsidian-themes", repo, "latest.json");
1073
1081
  const manifest = await this.cachedFetch(manifestUrl, cacheDest);
1074
1082
  return manifest.version;
1075
1083
  }
@@ -1081,7 +1089,7 @@ var ObsidianLauncher = class {
1081
1089
  async downloadGitHubTheme(repo) {
1082
1090
  repo = normalizeGitHubRepo(repo);
1083
1091
  const version = await this.getLatestThemeVersion(repo);
1084
- const themeDir = path4.join(this.cacheDir, "obsidian-themes", repo, version);
1092
+ const themeDir = path5.join(this.cacheDir, "obsidian-themes", repo, version);
1085
1093
  if (!await fileExists(themeDir)) {
1086
1094
  await atomicCreate(themeDir, async (tmpDir) => {
1087
1095
  const assetsToDownload = ["manifest.json", "theme.css"];
@@ -1090,7 +1098,7 @@ var ObsidianLauncher = class {
1090
1098
  const url = `https://raw.githubusercontent.com/${repo}/HEAD/${file}`;
1091
1099
  const response = await fetch(url);
1092
1100
  if (response.ok) {
1093
- await downloadResponse(response, path4.join(tmpDir, file));
1101
+ await downloadResponse(response, path5.join(tmpDir, file));
1094
1102
  } else {
1095
1103
  throw Error(`No ${file} found for ${repo}`);
1096
1104
  }
@@ -1132,11 +1140,11 @@ var ObsidianLauncher = class {
1132
1140
  let themePath;
1133
1141
  let originalType;
1134
1142
  if (typeof theme == "string") {
1135
- themePath = path4.resolve(theme);
1143
+ themePath = path5.resolve(theme);
1136
1144
  originalType = "local";
1137
1145
  } else if ("path" in theme) {
1138
1146
  ;
1139
- themePath = path4.resolve(theme.path);
1147
+ themePath = path5.resolve(theme.path);
1140
1148
  originalType = "local";
1141
1149
  } else if ("repo" in theme) {
1142
1150
  themePath = await this.downloadGitHubTheme(theme.repo);
@@ -1147,19 +1155,19 @@ var ObsidianLauncher = class {
1147
1155
  } else {
1148
1156
  throw Error("You must specify one of theme path, repo, or name");
1149
1157
  }
1150
- const manifestPath = path4.join(themePath, "manifest.json");
1158
+ const manifestPath = path5.join(themePath, "manifest.json");
1151
1159
  if (!await fileExists(manifestPath)) {
1152
1160
  throw Error(`No theme found at ${themePath}`);
1153
1161
  }
1154
1162
  let themeName = typeof theme == "object" && "name" in theme ? theme.name : void 0;
1155
1163
  if (!themeName) {
1156
- const manifestPath2 = path4.join(themePath, "manifest.json");
1164
+ const manifestPath2 = path5.join(themePath, "manifest.json");
1157
1165
  themeName = JSON.parse(await fsAsync4.readFile(manifestPath2, "utf8").catch(() => "{}")).name;
1158
1166
  if (!themeName) {
1159
1167
  throw Error(`${themePath}/manifest.json malformed.`);
1160
1168
  }
1161
1169
  }
1162
- if (!await fileExists(path4.join(themePath, "theme.css"))) {
1170
+ if (!await fileExists(path5.join(themePath, "theme.css"))) {
1163
1171
  throw Error(`No theme.css found under ${themePath}`);
1164
1172
  }
1165
1173
  let enabled;
@@ -1179,21 +1187,21 @@ var ObsidianLauncher = class {
1179
1187
  */
1180
1188
  async installPlugins(vault, plugins) {
1181
1189
  const downloadedPlugins = await this.downloadPlugins(plugins);
1182
- const obsidianDir = path4.join(vault, ".obsidian");
1190
+ const obsidianDir = path5.join(vault, ".obsidian");
1183
1191
  await fsAsync4.mkdir(obsidianDir, { recursive: true });
1184
- const enabledPluginsPath = path4.join(obsidianDir, "community-plugins.json");
1192
+ const enabledPluginsPath = path5.join(obsidianDir, "community-plugins.json");
1185
1193
  let originalEnabledPlugins = [];
1186
1194
  if (await fileExists(enabledPluginsPath)) {
1187
1195
  originalEnabledPlugins = JSON.parse(await fsAsync4.readFile(enabledPluginsPath, "utf-8"));
1188
1196
  }
1189
1197
  let enabledPlugins = [...originalEnabledPlugins];
1190
1198
  for (const { path: pluginPath, enabled = true, originalType } of downloadedPlugins) {
1191
- const manifestPath = path4.join(pluginPath, "manifest.json");
1199
+ const manifestPath = path5.join(pluginPath, "manifest.json");
1192
1200
  const pluginId = JSON.parse(await fsAsync4.readFile(manifestPath, "utf8").catch(() => "{}")).id;
1193
1201
  if (!pluginId) {
1194
1202
  throw Error(`${manifestPath} missing or malformed.`);
1195
1203
  }
1196
- const pluginDest = path4.join(obsidianDir, "plugins", pluginId);
1204
+ const pluginDest = path5.join(obsidianDir, "plugins", pluginId);
1197
1205
  await fsAsync4.mkdir(pluginDest, { recursive: true });
1198
1206
  const files = {
1199
1207
  "manifest.json": [true, true],
@@ -1202,12 +1210,12 @@ var ObsidianLauncher = class {
1202
1210
  "data.json": [false, false]
1203
1211
  };
1204
1212
  for (const [file, [required, deleteIfMissing]] of Object.entries(files)) {
1205
- if (await fileExists(path4.join(pluginPath, file))) {
1206
- await linkOrCp(path4.join(pluginPath, file), path4.join(pluginDest, file));
1213
+ if (await fileExists(path5.join(pluginPath, file))) {
1214
+ await linkOrCp(path5.join(pluginPath, file), path5.join(pluginDest, file));
1207
1215
  } else if (required) {
1208
1216
  throw Error(`${pluginPath}/${file} missing.`);
1209
1217
  } else if (deleteIfMissing) {
1210
- await fsAsync4.rm(path4.join(pluginDest, file), { force: true });
1218
+ await fsAsync4.rm(path5.join(pluginDest, file), { force: true });
1211
1219
  }
1212
1220
  }
1213
1221
  const pluginAlreadyListed = enabledPlugins.includes(pluginId);
@@ -1217,7 +1225,7 @@ var ObsidianLauncher = class {
1217
1225
  enabledPlugins = enabledPlugins.filter((p) => p != pluginId);
1218
1226
  }
1219
1227
  if (originalType == "local") {
1220
- await fsAsync4.writeFile(path4.join(pluginDest, ".hotreload"), "");
1228
+ await fsAsync4.writeFile(path5.join(pluginDest, ".hotreload"), "");
1221
1229
  }
1222
1230
  }
1223
1231
  if (!_4.isEqual(enabledPlugins, originalEnabledPlugins)) {
@@ -1231,12 +1239,12 @@ var ObsidianLauncher = class {
1231
1239
  */
1232
1240
  async installThemes(vault, themes) {
1233
1241
  const downloadedThemes = await this.downloadThemes(themes);
1234
- const obsidianDir = path4.join(vault, ".obsidian");
1242
+ const obsidianDir = path5.join(vault, ".obsidian");
1235
1243
  await fsAsync4.mkdir(obsidianDir, { recursive: true });
1236
1244
  let enabledTheme = void 0;
1237
1245
  for (const { path: themePath, enabled = true } of downloadedThemes) {
1238
- const manifestPath = path4.join(themePath, "manifest.json");
1239
- const cssPath = path4.join(themePath, "theme.css");
1246
+ const manifestPath = path5.join(themePath, "manifest.json");
1247
+ const cssPath = path5.join(themePath, "theme.css");
1240
1248
  const themeName = JSON.parse(await fsAsync4.readFile(manifestPath, "utf8").catch(() => "{}")).name;
1241
1249
  if (!themeName) {
1242
1250
  throw Error(`${manifestPath} missing or malformed.`);
@@ -1244,10 +1252,10 @@ var ObsidianLauncher = class {
1244
1252
  if (!await fileExists(cssPath)) {
1245
1253
  throw Error(`${cssPath} missing.`);
1246
1254
  }
1247
- const themeDest = path4.join(obsidianDir, "themes", themeName);
1255
+ const themeDest = path5.join(obsidianDir, "themes", themeName);
1248
1256
  await fsAsync4.mkdir(themeDest, { recursive: true });
1249
- await linkOrCp(manifestPath, path4.join(themeDest, "manifest.json"));
1250
- await linkOrCp(cssPath, path4.join(themeDest, "theme.css"));
1257
+ await linkOrCp(manifestPath, path5.join(themeDest, "manifest.json"));
1258
+ await linkOrCp(cssPath, path5.join(themeDest, "theme.css"));
1251
1259
  if (enabledTheme && enabled) {
1252
1260
  throw Error("You can only have one enabled theme.");
1253
1261
  } else if (enabled) {
@@ -1255,7 +1263,7 @@ var ObsidianLauncher = class {
1255
1263
  }
1256
1264
  }
1257
1265
  if (themes.length > 0) {
1258
- const appearancePath = path4.join(obsidianDir, "appearance.json");
1266
+ const appearancePath = path5.join(obsidianDir, "appearance.json");
1259
1267
  let appearance = {};
1260
1268
  if (await fileExists(appearancePath)) {
1261
1269
  appearance = JSON.parse(await fsAsync4.readFile(appearancePath, "utf-8"));
@@ -1301,20 +1309,20 @@ var ObsidianLauncher = class {
1301
1309
  Object.assign(obsidianJson, {
1302
1310
  vaults: {
1303
1311
  [vaultId]: {
1304
- path: path4.resolve(params.vault),
1312
+ path: path5.resolve(params.vault),
1305
1313
  ts: (/* @__PURE__ */ new Date()).getTime(),
1306
1314
  open: true
1307
1315
  }
1308
1316
  }
1309
1317
  });
1310
1318
  }
1311
- await fsAsync4.writeFile(path4.join(configDir, "obsidian.json"), JSON.stringify(obsidianJson));
1312
- await fsAsync4.writeFile(path4.join(configDir, "Preferences"), JSON.stringify(chromePreferences));
1319
+ await fsAsync4.writeFile(path5.join(configDir, "obsidian.json"), JSON.stringify(obsidianJson));
1320
+ await fsAsync4.writeFile(path5.join(configDir, "Preferences"), JSON.stringify(chromePreferences));
1313
1321
  let appPath = params.appPath;
1314
1322
  if (!appPath) {
1315
1323
  appPath = await this.downloadApp(appVersion);
1316
1324
  }
1317
- await linkOrCp(appPath, path4.join(configDir, path4.basename(appPath)));
1325
+ await linkOrCp(appPath, path5.join(configDir, path5.basename(appPath)));
1318
1326
  const localStorage = new ChromeLocalStorage(configDir);
1319
1327
  await localStorage.setItems("app://obsidian.md", localStorageData);
1320
1328
  await localStorage.close();
@@ -1332,7 +1340,7 @@ var ObsidianLauncher = class {
1332
1340
  async setupVault(params) {
1333
1341
  let vault = params.vault;
1334
1342
  if (params.copy) {
1335
- const dest = await makeTmpDir(`${path4.basename(vault)}-`);
1343
+ const dest = await makeTmpDir(`${path5.basename(vault)}-`);
1336
1344
  await fsAsync4.cp(vault, dest, { recursive: true, preserveTimestamps: true });
1337
1345
  vault = dest;
1338
1346
  }
@@ -1482,7 +1490,7 @@ var ObsidianLauncher = class {
1482
1490
  const { platform, arch } = process;
1483
1491
  dest = `obsidian-installer/${platform}-${arch}/Obsidian-${version}`;
1484
1492
  }
1485
- return await fileExists(path4.join(this.cacheDir, dest));
1493
+ return await fileExists(path5.join(this.cacheDir, dest));
1486
1494
  }
1487
1495
  /**
1488
1496
  * Returns true if we either have the credentials to download the version or it's already in cache.
@@ -1508,4 +1516,4 @@ export {
1508
1516
  watchFiles,
1509
1517
  ObsidianLauncher
1510
1518
  };
1511
- //# sourceMappingURL=chunk-KPNJCYO4.js.map
1519
+ //# sourceMappingURL=chunk-EVX5ML6K.js.map