obsidian-launcher 2.0.2 → 2.1.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/LICENSE +21 -0
- package/README.md +26 -14
- package/dist/7z.cjs +1 -1
- package/dist/{chunk-QQJCBP4N.cjs → chunk-MNS2C2HB.cjs} +2 -2
- package/dist/chunk-MNS2C2HB.cjs.map +1 -0
- package/dist/{chunk-EVX5ML6K.js → chunk-OUPK2DAJ.js} +206 -181
- package/dist/chunk-OUPK2DAJ.js.map +1 -0
- package/dist/{chunk-2B6JXRBE.cjs → chunk-SG2CJVPK.cjs} +225 -200
- package/dist/chunk-SG2CJVPK.cjs.map +1 -0
- package/dist/cli.cjs +57 -26
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +54 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -29
- package/dist/index.d.ts +37 -29
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +78 -78
- package/dist/chunk-2B6JXRBE.cjs.map +0 -1
- package/dist/chunk-EVX5ML6K.js.map +0 -1
- package/dist/chunk-QQJCBP4N.cjs.map +0 -1
|
@@ -116,57 +116,19 @@ import fsAsync2 from "fs/promises";
|
|
|
116
116
|
import readlineSync from "readline-sync";
|
|
117
117
|
import dotenv from "dotenv";
|
|
118
118
|
import path2 from "path";
|
|
119
|
+
import { Octokit } from "octokit";
|
|
119
120
|
import { env } from "process";
|
|
120
|
-
function parseLinkHeader(linkHeader) {
|
|
121
|
-
function parseLinkData(linkData) {
|
|
122
|
-
return Object.fromEntries(
|
|
123
|
-
linkData.split(";").flatMap((x) => {
|
|
124
|
-
const partMatch = x.trim().match(/^([^=]+?)\s*=\s*"?([^"]+)"?$/);
|
|
125
|
-
return partMatch ? [[partMatch[1], partMatch[2]]] : [];
|
|
126
|
-
})
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
const linkDatas = linkHeader.split(/,\s*(?=<)/).flatMap((link) => {
|
|
130
|
-
const linkMatch = link.trim().match(/^<([^>]*)>(.*)$/);
|
|
131
|
-
if (linkMatch) {
|
|
132
|
-
return [{
|
|
133
|
-
url: linkMatch[1],
|
|
134
|
-
...parseLinkData(linkMatch[2])
|
|
135
|
-
}];
|
|
136
|
-
} else {
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
}).filter((l) => l.rel);
|
|
140
|
-
return Object.fromEntries(linkDatas.map((l) => [l.rel, l]));
|
|
141
|
-
}
|
|
142
121
|
function createURL(url, base, params = {}) {
|
|
143
|
-
|
|
122
|
+
const cleanParams = _2(params).pickBy((x) => x !== void 0).mapValues((v) => String(v)).value();
|
|
144
123
|
const urlObj = new URL(url, base);
|
|
145
|
-
const searchParams = new URLSearchParams({ ...Object.fromEntries(urlObj.searchParams), ...
|
|
124
|
+
const searchParams = new URLSearchParams({ ...Object.fromEntries(urlObj.searchParams), ...cleanParams });
|
|
146
125
|
if ([...searchParams].length > 0) {
|
|
147
126
|
urlObj.search = "?" + searchParams;
|
|
148
127
|
}
|
|
149
128
|
return urlObj.toString();
|
|
150
129
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const token = env.GITHUB_TOKEN;
|
|
154
|
-
const headers = token ? { Authorization: "Bearer " + token } : {};
|
|
155
|
-
const response = await fetch(url, { headers });
|
|
156
|
-
if (!response.ok) {
|
|
157
|
-
throw new Error(`GitHub API error: ${await response.text()}`);
|
|
158
|
-
}
|
|
159
|
-
return response;
|
|
160
|
-
}
|
|
161
|
-
async function fetchGitHubAPIPaginated(url, params = {}) {
|
|
162
|
-
const results = [];
|
|
163
|
-
let next = createURL(url, "https://api.github.com", { per_page: 100, ...params });
|
|
164
|
-
while (next) {
|
|
165
|
-
const response = await fetchGitHubAPI(next);
|
|
166
|
-
results.push(...await response.json());
|
|
167
|
-
next = parseLinkHeader(response.headers.get("link") ?? "").next?.url;
|
|
168
|
-
}
|
|
169
|
-
return results;
|
|
130
|
+
function getGithubClient() {
|
|
131
|
+
return new Octokit({ auth: env.GITHUB_TOKEN });
|
|
170
132
|
}
|
|
171
133
|
async function obsidianApiLogin(opts) {
|
|
172
134
|
const { interactive = false, savePath } = opts;
|
|
@@ -180,7 +142,7 @@ async function obsidianApiLogin(opts) {
|
|
|
180
142
|
password = password || readlineSync.question("Obsidian password: ", { hideEchoBack: true });
|
|
181
143
|
} else {
|
|
182
144
|
throw Error(
|
|
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>`"
|
|
145
|
+
"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>`"
|
|
184
146
|
);
|
|
185
147
|
}
|
|
186
148
|
}
|
|
@@ -209,7 +171,7 @@ async function obsidianApiLogin(opts) {
|
|
|
209
171
|
needsMfa = true;
|
|
210
172
|
if (!interactive) {
|
|
211
173
|
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>`"
|
|
174
|
+
"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 app -v <version>`"
|
|
213
175
|
);
|
|
214
176
|
}
|
|
215
177
|
} else if (["please wait", "try again"].some((m) => error?.includes(m))) {
|
|
@@ -219,9 +181,9 @@ async function obsidianApiLogin(opts) {
|
|
|
219
181
|
throw Error(`Obsidian login failed: ${signin.error ?? "unknown error"}`);
|
|
220
182
|
}
|
|
221
183
|
}
|
|
222
|
-
if (!signin
|
|
223
|
-
throw Error(`Obsidian login failed: ${signin
|
|
224
|
-
} else if (!signin
|
|
184
|
+
if (!signin?.token) {
|
|
185
|
+
throw Error(`Obsidian login failed: ${signin?.error ?? "unknown error"}`);
|
|
186
|
+
} else if (!signin?.license) {
|
|
225
187
|
throw Error("Obsidian Insiders account is required to download Obsidian beta versions");
|
|
226
188
|
}
|
|
227
189
|
if (interactive && savePath && (!env.OBSIDIAN_EMAIL || !env.OBSIDIAN_PASSWORD)) {
|
|
@@ -233,8 +195,8 @@ async function obsidianApiLogin(opts) {
|
|
|
233
195
|
OBSIDIAN_PASSWORD='${password}'
|
|
234
196
|
`
|
|
235
197
|
);
|
|
198
|
+
console.log(`Saved Obsidian credentials to ${path2.relative(process.cwd(), savePath)}`);
|
|
236
199
|
}
|
|
237
|
-
console.log(`Saved Obsidian credentials to ${path2.relative(process.cwd(), savePath)}`);
|
|
238
200
|
}
|
|
239
201
|
return signin.token;
|
|
240
202
|
}
|
|
@@ -411,6 +373,39 @@ async function extractObsidianDmg(dmg, dest) {
|
|
|
411
373
|
}
|
|
412
374
|
});
|
|
413
375
|
}
|
|
376
|
+
async function fetchObsidianDesktopReleases(sinceDate, sinceSha) {
|
|
377
|
+
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",
|
|
382
|
+
path: "desktop-releases.json",
|
|
383
|
+
since: sinceDate,
|
|
384
|
+
per_page: 100
|
|
385
|
+
});
|
|
386
|
+
commitHistory.reverse();
|
|
387
|
+
if (sinceSha) {
|
|
388
|
+
commitHistory = _3.takeRightWhile(commitHistory, (c) => c.sha != sinceSha);
|
|
389
|
+
}
|
|
390
|
+
const fileHistory = await pool(
|
|
391
|
+
4,
|
|
392
|
+
commitHistory,
|
|
393
|
+
(commit) => fetch(`https://raw.githubusercontent.com/${repo}/${commit.sha}/desktop-releases.json`).then((r) => r.json())
|
|
394
|
+
);
|
|
395
|
+
const commitDate = commitHistory.at(-1)?.commit.committer?.date ?? sinceDate;
|
|
396
|
+
const commitSha = commitHistory.at(-1)?.sha ?? sinceSha;
|
|
397
|
+
return [fileHistory, { commitDate, commitSha }];
|
|
398
|
+
}
|
|
399
|
+
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;
|
|
408
|
+
}
|
|
414
409
|
var BROKEN_ASSETS = [
|
|
415
410
|
"https://releases.obsidian.md/release/obsidian-0.12.16.asar.gz",
|
|
416
411
|
"https://github.com/obsidianmd/obsidian-releases/releases/download/v0.12.16/obsidian-0.12.16.asar.gz",
|
|
@@ -481,7 +476,59 @@ function parseObsidianGithubRelease(gitHubRelease) {
|
|
|
481
476
|
}
|
|
482
477
|
};
|
|
483
478
|
}
|
|
484
|
-
|
|
479
|
+
var INSTALLER_KEYS = [
|
|
480
|
+
"appImage",
|
|
481
|
+
"appImageArm",
|
|
482
|
+
"tar",
|
|
483
|
+
"tarArm",
|
|
484
|
+
"dmg",
|
|
485
|
+
"exe"
|
|
486
|
+
];
|
|
487
|
+
function updateObsidianVersionList(args) {
|
|
488
|
+
const { original = [], destkopReleases = [], gitHubReleases = [], installerInfos = [] } = args;
|
|
489
|
+
const oldVersions = _3.keyBy(original, (v) => v.version);
|
|
490
|
+
const newVersions = _3.cloneDeep(oldVersions);
|
|
491
|
+
for (const destkopRelease of destkopReleases) {
|
|
492
|
+
const { current, beta } = parseObsidianDesktopRelease(destkopRelease);
|
|
493
|
+
if (beta) {
|
|
494
|
+
newVersions[beta.version] = _3.merge(newVersions[beta.version] ?? {}, beta);
|
|
495
|
+
}
|
|
496
|
+
newVersions[current.version] = _3.merge(newVersions[current.version] ?? {}, current);
|
|
497
|
+
}
|
|
498
|
+
for (const githubRelease of gitHubReleases) {
|
|
499
|
+
if (semver.valid(githubRelease.name) && !semver.prerelease(githubRelease.name)) {
|
|
500
|
+
const parsed = parseObsidianGithubRelease(githubRelease);
|
|
501
|
+
const newVersion = _3.merge(newVersions[parsed.version] ?? {}, parsed);
|
|
502
|
+
for (const installerKey of INSTALLER_KEYS) {
|
|
503
|
+
const oldDigest = oldVersions[parsed.version]?.installers[installerKey]?.digest;
|
|
504
|
+
const newDigest = newVersion.installers?.[installerKey]?.digest;
|
|
505
|
+
if (oldDigest && oldDigest != newDigest) {
|
|
506
|
+
newVersion.installers[installerKey] = { digest: newDigest };
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
newVersions[parsed.version] = newVersion;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
let minInstallerVersion = void 0;
|
|
513
|
+
let maxInstallerVersion = void 0;
|
|
514
|
+
for (const version of Object.keys(newVersions).sort(semver.compare)) {
|
|
515
|
+
if (newVersions[version].downloads.appImage) {
|
|
516
|
+
maxInstallerVersion = version;
|
|
517
|
+
if (!minInstallerVersion) {
|
|
518
|
+
minInstallerVersion = version;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
newVersions[version] = _3.merge({ minInstallerVersion, maxInstallerVersion }, newVersions[version]);
|
|
522
|
+
}
|
|
523
|
+
for (const installerInfo of installerInfos) {
|
|
524
|
+
newVersions[installerInfo.version] = _3.merge(newVersions[installerInfo.version] ?? {}, {
|
|
525
|
+
version: installerInfo.version,
|
|
526
|
+
installers: { [installerInfo.key]: installerInfo.installerInfo }
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return Object.values(newVersions).map(normalizeObsidianVersionInfo).sort((a, b) => semver.compare(a.version, b.version));
|
|
530
|
+
}
|
|
531
|
+
async function extractInstallerInfo(installerKey, url) {
|
|
485
532
|
const installerName = url.split("/").at(-1);
|
|
486
533
|
console.log(`Extrating installer info for ${installerName}...`);
|
|
487
534
|
const tmpDir = await makeTmpDir("obsidian-launcher-");
|
|
@@ -757,10 +804,11 @@ var ObsidianLauncher = class {
|
|
|
757
804
|
} else if (appVersion == "latest") {
|
|
758
805
|
appVersion = versions.filter((v) => !v.isBeta).at(-1).version;
|
|
759
806
|
} else if (appVersion == "earliest") {
|
|
760
|
-
|
|
761
|
-
if (!
|
|
807
|
+
const manifest = await this.getRootManifest();
|
|
808
|
+
if (!manifest?.minAppVersion) {
|
|
762
809
|
throw Error('Unable to resolve Obsidian appVersion "earliest", no manifest.json or minAppVersion found.');
|
|
763
810
|
}
|
|
811
|
+
appVersion = manifest.minAppVersion;
|
|
764
812
|
} else {
|
|
765
813
|
appVersion = semver2.valid(appVersion) ?? appVersion;
|
|
766
814
|
}
|
|
@@ -798,7 +846,7 @@ var ObsidianLauncher = class {
|
|
|
798
846
|
return _4.uniqBy(resolvedVersions, (v) => v.join("/"));
|
|
799
847
|
}
|
|
800
848
|
getInstallerKey(installerVersionInfo, opts = {}) {
|
|
801
|
-
const { platform, arch } = _4.
|
|
849
|
+
const { platform, arch } = _4.defaults({}, opts, currentPlatform);
|
|
802
850
|
const platformName = `${platform}-${arch}`;
|
|
803
851
|
const key = _4.findKey(installerVersionInfo.installers, (v) => v && v.platforms.includes(platformName));
|
|
804
852
|
return key;
|
|
@@ -810,7 +858,7 @@ var ObsidianLauncher = class {
|
|
|
810
858
|
* @param opts.arch Architecture (defaults to host architecture)
|
|
811
859
|
*/
|
|
812
860
|
async getInstallerInfo(installerVersion, opts = {}) {
|
|
813
|
-
const { platform, arch } = _4.
|
|
861
|
+
const { platform, arch } = _4.defaults({}, opts, currentPlatform);
|
|
814
862
|
const versionInfo = await this.getVersionInfo(installerVersion);
|
|
815
863
|
const key = this.getInstallerKey(versionInfo, { platform, arch });
|
|
816
864
|
if (key) {
|
|
@@ -829,7 +877,7 @@ var ObsidianLauncher = class {
|
|
|
829
877
|
* @param opts.arch Architecture of the installer to download (defaults to host architecture)
|
|
830
878
|
*/
|
|
831
879
|
async downloadInstaller(installerVersion, opts = {}) {
|
|
832
|
-
const { platform, arch } = _4.
|
|
880
|
+
const { platform, arch } = _4.defaults({}, opts, currentPlatform);
|
|
833
881
|
const versionInfo = await this.getVersionInfo(installerVersion);
|
|
834
882
|
installerVersion = versionInfo.version;
|
|
835
883
|
const installerInfo = await this.getInstallerInfo(installerVersion, { platform, arch });
|
|
@@ -865,7 +913,7 @@ var ObsidianLauncher = class {
|
|
|
865
913
|
*
|
|
866
914
|
* To download Obsidian beta versions you'll need to have an Obsidian Insiders account and either set the
|
|
867
915
|
* `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`
|
|
916
|
+
* with `npx obsidian-launcher download app -v latest-beta`
|
|
869
917
|
*
|
|
870
918
|
* @param appVersion Obsidian version to download
|
|
871
919
|
*/
|
|
@@ -913,7 +961,7 @@ var ObsidianLauncher = class {
|
|
|
913
961
|
* @param installerVersion Obsidian installer version
|
|
914
962
|
*/
|
|
915
963
|
async downloadChromedriver(installerVersion, opts = {}) {
|
|
916
|
-
const { platform, arch } = _4.
|
|
964
|
+
const { platform, arch } = _4.defaults({}, opts, currentPlatform);
|
|
917
965
|
const installerInfo = await this.getInstallerInfo(installerVersion, { platform, arch });
|
|
918
966
|
const cacheDir = path5.join(this.cacheDir, `electron-chromedriver/${platform}-${arch}/${installerInfo.electron}`);
|
|
919
967
|
let chromedriverPath;
|
|
@@ -1073,6 +1121,60 @@ var ObsidianLauncher = class {
|
|
|
1073
1121
|
})
|
|
1074
1122
|
);
|
|
1075
1123
|
}
|
|
1124
|
+
/**
|
|
1125
|
+
* Installs plugins into an Obsidian vault
|
|
1126
|
+
* @param vault Path to the vault to install the plugins in
|
|
1127
|
+
* @param plugins List plugins to install
|
|
1128
|
+
*/
|
|
1129
|
+
async installPlugins(vault, plugins) {
|
|
1130
|
+
const downloadedPlugins = await this.downloadPlugins(plugins);
|
|
1131
|
+
const obsidianDir = path5.join(vault, ".obsidian");
|
|
1132
|
+
await fsAsync4.mkdir(obsidianDir, { recursive: true });
|
|
1133
|
+
const enabledPluginsPath = path5.join(obsidianDir, "community-plugins.json");
|
|
1134
|
+
let originalEnabledPlugins = [];
|
|
1135
|
+
if (await fileExists(enabledPluginsPath)) {
|
|
1136
|
+
originalEnabledPlugins = JSON.parse(await fsAsync4.readFile(enabledPluginsPath, "utf-8"));
|
|
1137
|
+
}
|
|
1138
|
+
let enabledPlugins = [...originalEnabledPlugins];
|
|
1139
|
+
for (const { path: pluginPath, enabled = true, originalType } of downloadedPlugins) {
|
|
1140
|
+
const manifestPath = path5.join(pluginPath, "manifest.json");
|
|
1141
|
+
const pluginId = JSON.parse(await fsAsync4.readFile(manifestPath, "utf8").catch(() => "{}")).id;
|
|
1142
|
+
if (!pluginId) {
|
|
1143
|
+
throw Error(`${manifestPath} missing or malformed.`);
|
|
1144
|
+
}
|
|
1145
|
+
const pluginDest = path5.join(obsidianDir, "plugins", pluginId);
|
|
1146
|
+
await fsAsync4.mkdir(pluginDest, { recursive: true });
|
|
1147
|
+
const files = {
|
|
1148
|
+
"manifest.json": true,
|
|
1149
|
+
"main.js": true,
|
|
1150
|
+
"styles.css": false
|
|
1151
|
+
};
|
|
1152
|
+
for (const [file, required] of Object.entries(files)) {
|
|
1153
|
+
if (await fileExists(path5.join(pluginPath, file))) {
|
|
1154
|
+
await linkOrCp(path5.join(pluginPath, file), path5.join(pluginDest, file));
|
|
1155
|
+
} else if (required) {
|
|
1156
|
+
throw Error(`${pluginPath}/${file} missing.`);
|
|
1157
|
+
} else {
|
|
1158
|
+
await fsAsync4.rm(path5.join(pluginDest, file), { force: true });
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (await fileExists(path5.join(pluginPath, "data.json"))) {
|
|
1162
|
+
await fsAsync4.cp(path5.join(pluginPath, "data.json"), path5.join(pluginDest, "data.json"));
|
|
1163
|
+
}
|
|
1164
|
+
const pluginAlreadyListed = enabledPlugins.includes(pluginId);
|
|
1165
|
+
if (enabled && !pluginAlreadyListed) {
|
|
1166
|
+
enabledPlugins.push(pluginId);
|
|
1167
|
+
} else if (!enabled && pluginAlreadyListed) {
|
|
1168
|
+
enabledPlugins = enabledPlugins.filter((p) => p != pluginId);
|
|
1169
|
+
}
|
|
1170
|
+
if (originalType == "local") {
|
|
1171
|
+
await fsAsync4.writeFile(path5.join(pluginDest, ".hotreload"), "");
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (!_4.isEqual(enabledPlugins, originalEnabledPlugins)) {
|
|
1175
|
+
await fsAsync4.writeFile(enabledPluginsPath, JSON.stringify(enabledPlugins, void 0, 2));
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1076
1178
|
/** Gets the latest version of a theme. */
|
|
1077
1179
|
async getLatestThemeVersion(repo) {
|
|
1078
1180
|
repo = normalizeGitHubRepo(repo);
|
|
@@ -1086,25 +1188,40 @@ var ObsidianLauncher = class {
|
|
|
1086
1188
|
* @param repo Repo
|
|
1087
1189
|
* @returns path to the downloaded theme
|
|
1088
1190
|
*/
|
|
1089
|
-
async downloadGitHubTheme(repo) {
|
|
1191
|
+
async downloadGitHubTheme(repo, version = "latest") {
|
|
1090
1192
|
repo = normalizeGitHubRepo(repo);
|
|
1091
|
-
const
|
|
1193
|
+
const latest = await this.getLatestThemeVersion(repo);
|
|
1194
|
+
if (version == "latest") {
|
|
1195
|
+
version = latest;
|
|
1196
|
+
}
|
|
1197
|
+
if (!semver2.valid(version)) {
|
|
1198
|
+
throw Error(`Invalid version "${version}"`);
|
|
1199
|
+
}
|
|
1200
|
+
version = semver2.valid(version);
|
|
1092
1201
|
const themeDir = path5.join(this.cacheDir, "obsidian-themes", repo, version);
|
|
1093
1202
|
if (!await fileExists(themeDir)) {
|
|
1094
1203
|
await atomicCreate(themeDir, async (tmpDir) => {
|
|
1095
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`;
|
|
1211
|
+
}
|
|
1096
1212
|
await Promise.all(
|
|
1097
|
-
assetsToDownload.map(
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
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
|
+
}
|
|
1104
1222
|
}
|
|
1105
|
-
|
|
1223
|
+
)
|
|
1106
1224
|
);
|
|
1107
|
-
return tmpDir;
|
|
1108
1225
|
});
|
|
1109
1226
|
}
|
|
1110
1227
|
return themeDir;
|
|
@@ -1114,13 +1231,13 @@ var ObsidianLauncher = class {
|
|
|
1114
1231
|
* @param name name of the theme
|
|
1115
1232
|
* @returns path to the downloaded theme
|
|
1116
1233
|
*/
|
|
1117
|
-
async downloadCommunityTheme(name) {
|
|
1234
|
+
async downloadCommunityTheme(name, version = "latest") {
|
|
1118
1235
|
const communityThemes = await this.getCommunityThemes();
|
|
1119
1236
|
const themeInfo = communityThemes.find((p) => p.name == name);
|
|
1120
1237
|
if (!themeInfo) {
|
|
1121
1238
|
throw Error(`No theme with name ${name} found.`);
|
|
1122
1239
|
}
|
|
1123
|
-
return await this.downloadGitHubTheme(themeInfo.repo);
|
|
1240
|
+
return await this.downloadGitHubTheme(themeInfo.repo, version);
|
|
1124
1241
|
}
|
|
1125
1242
|
/**
|
|
1126
1243
|
* Downloads a list of themes to the cache and returns a list of {@link DownloadedThemeEntry} with the downloaded
|
|
@@ -1147,10 +1264,10 @@ var ObsidianLauncher = class {
|
|
|
1147
1264
|
themePath = path5.resolve(theme.path);
|
|
1148
1265
|
originalType = "local";
|
|
1149
1266
|
} else if ("repo" in theme) {
|
|
1150
|
-
themePath = await this.downloadGitHubTheme(theme.repo);
|
|
1267
|
+
themePath = await this.downloadGitHubTheme(theme.repo, theme.version);
|
|
1151
1268
|
originalType = "github";
|
|
1152
1269
|
} else if ("name" in theme) {
|
|
1153
|
-
themePath = await this.downloadCommunityTheme(theme.name);
|
|
1270
|
+
themePath = await this.downloadCommunityTheme(theme.name, theme.version);
|
|
1154
1271
|
originalType = "community";
|
|
1155
1272
|
} else {
|
|
1156
1273
|
throw Error("You must specify one of theme path, repo, or name");
|
|
@@ -1181,58 +1298,6 @@ var ObsidianLauncher = class {
|
|
|
1181
1298
|
);
|
|
1182
1299
|
}
|
|
1183
1300
|
/**
|
|
1184
|
-
* Installs plugins into an Obsidian vault
|
|
1185
|
-
* @param vault Path to the vault to install the plugins in
|
|
1186
|
-
* @param plugins List plugins to install
|
|
1187
|
-
*/
|
|
1188
|
-
async installPlugins(vault, plugins) {
|
|
1189
|
-
const downloadedPlugins = await this.downloadPlugins(plugins);
|
|
1190
|
-
const obsidianDir = path5.join(vault, ".obsidian");
|
|
1191
|
-
await fsAsync4.mkdir(obsidianDir, { recursive: true });
|
|
1192
|
-
const enabledPluginsPath = path5.join(obsidianDir, "community-plugins.json");
|
|
1193
|
-
let originalEnabledPlugins = [];
|
|
1194
|
-
if (await fileExists(enabledPluginsPath)) {
|
|
1195
|
-
originalEnabledPlugins = JSON.parse(await fsAsync4.readFile(enabledPluginsPath, "utf-8"));
|
|
1196
|
-
}
|
|
1197
|
-
let enabledPlugins = [...originalEnabledPlugins];
|
|
1198
|
-
for (const { path: pluginPath, enabled = true, originalType } of downloadedPlugins) {
|
|
1199
|
-
const manifestPath = path5.join(pluginPath, "manifest.json");
|
|
1200
|
-
const pluginId = JSON.parse(await fsAsync4.readFile(manifestPath, "utf8").catch(() => "{}")).id;
|
|
1201
|
-
if (!pluginId) {
|
|
1202
|
-
throw Error(`${manifestPath} missing or malformed.`);
|
|
1203
|
-
}
|
|
1204
|
-
const pluginDest = path5.join(obsidianDir, "plugins", pluginId);
|
|
1205
|
-
await fsAsync4.mkdir(pluginDest, { recursive: true });
|
|
1206
|
-
const files = {
|
|
1207
|
-
"manifest.json": [true, true],
|
|
1208
|
-
"main.js": [true, true],
|
|
1209
|
-
"styles.css": [false, true],
|
|
1210
|
-
"data.json": [false, false]
|
|
1211
|
-
};
|
|
1212
|
-
for (const [file, [required, deleteIfMissing]] of Object.entries(files)) {
|
|
1213
|
-
if (await fileExists(path5.join(pluginPath, file))) {
|
|
1214
|
-
await linkOrCp(path5.join(pluginPath, file), path5.join(pluginDest, file));
|
|
1215
|
-
} else if (required) {
|
|
1216
|
-
throw Error(`${pluginPath}/${file} missing.`);
|
|
1217
|
-
} else if (deleteIfMissing) {
|
|
1218
|
-
await fsAsync4.rm(path5.join(pluginDest, file), { force: true });
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
const pluginAlreadyListed = enabledPlugins.includes(pluginId);
|
|
1222
|
-
if (enabled && !pluginAlreadyListed) {
|
|
1223
|
-
enabledPlugins.push(pluginId);
|
|
1224
|
-
} else if (!enabled && pluginAlreadyListed) {
|
|
1225
|
-
enabledPlugins = enabledPlugins.filter((p) => p != pluginId);
|
|
1226
|
-
}
|
|
1227
|
-
if (originalType == "local") {
|
|
1228
|
-
await fsAsync4.writeFile(path5.join(pluginDest, ".hotreload"), "");
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
if (!_4.isEqual(enabledPlugins, originalEnabledPlugins)) {
|
|
1232
|
-
await fsAsync4.writeFile(enabledPluginsPath, JSON.stringify(enabledPlugins, void 0, 2));
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
/**
|
|
1236
1301
|
* Installs themes into an Obsidian vault
|
|
1237
1302
|
* @param vault Path to the theme to install the themes in
|
|
1238
1303
|
* @param themes List of themes to install
|
|
@@ -1403,71 +1468,31 @@ var ObsidianLauncher = class {
|
|
|
1403
1468
|
*/
|
|
1404
1469
|
async updateVersionList(original, opts = {}) {
|
|
1405
1470
|
const { maxInstances = 1 } = opts;
|
|
1406
|
-
const
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
let commitHistory = await fetchGitHubAPIPaginated(`repos/${repo}/commits`, {
|
|
1410
|
-
path: "desktop-releases.json",
|
|
1411
|
-
since: original?.metadata?.commitDate
|
|
1412
|
-
});
|
|
1413
|
-
commitHistory.reverse();
|
|
1414
|
-
if (original?.metadata?.commitSha) {
|
|
1415
|
-
commitHistory = _4.takeRightWhile(commitHistory, (c) => c.sha != original.metadata.commitSha);
|
|
1416
|
-
}
|
|
1417
|
-
const fileHistory = await pool(
|
|
1418
|
-
maxInstances,
|
|
1419
|
-
commitHistory,
|
|
1420
|
-
(commit) => fetch(`https://raw.githubusercontent.com/${repo}/${commit.sha}/desktop-releases.json`).then((r) => r.json())
|
|
1471
|
+
const [destkopReleases, commitInfo] = await fetchObsidianDesktopReleases(
|
|
1472
|
+
original?.metadata.commitDate,
|
|
1473
|
+
original?.metadata.commitSha
|
|
1421
1474
|
);
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
const githubReleases = (await fetchGitHubAPIPaginated(`repos/${repo}/releases`)).reverse();
|
|
1430
|
-
for (const release of githubReleases) {
|
|
1431
|
-
if (semver2.valid(release.name)) {
|
|
1432
|
-
const parsed = parseObsidianGithubRelease(release);
|
|
1433
|
-
versions[parsed.version] = _4.merge(versions[parsed.version] ?? {}, parsed);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
const installerKeys = ["appImage", "appImageArm", "tar", "tarArm", "dmg", "exe"];
|
|
1437
|
-
const newInstallers = Object.values(versions).flatMap((v) => installerKeys.map((k) => [v, k]));
|
|
1475
|
+
const gitHubReleases = await fetchObsidianGitHubReleases();
|
|
1476
|
+
let newVersions = updateObsidianVersionList({
|
|
1477
|
+
original: original?.versions,
|
|
1478
|
+
destkopReleases,
|
|
1479
|
+
gitHubReleases
|
|
1480
|
+
});
|
|
1481
|
+
const newInstallers = newVersions.flatMap((v) => INSTALLER_KEYS.map((k) => [v, k])).filter(([v, key]) => v.downloads?.[key] && !v.installers?.[key]?.chrome);
|
|
1438
1482
|
const installerInfos = await pool(maxInstances, newInstallers, async ([v, key]) => {
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
const alreadyExtracted = !!v.installers?.[key]?.chrome;
|
|
1442
|
-
const changed = v.installers?.[key]?.digest != originalVersions[v.version]?.installers[key]?.digest;
|
|
1443
|
-
if (hasAsset && (!alreadyExtracted || changed)) {
|
|
1444
|
-
installerInfo = await getInstallerInfo(key, v.downloads[key]);
|
|
1445
|
-
}
|
|
1446
|
-
return { version: v.version, installerInfo: { [key]: installerInfo } };
|
|
1483
|
+
const installerInfo = await extractInstallerInfo(key, v.downloads[key]);
|
|
1484
|
+
return { version: v.version, key, installerInfo };
|
|
1447
1485
|
});
|
|
1448
|
-
|
|
1449
|
-
versions[installerInfo.version] = _4.merge(versions[installerInfo.version] ?? {}, installerInfo);
|
|
1450
|
-
}
|
|
1451
|
-
let minInstallerVersion = void 0;
|
|
1452
|
-
let maxInstallerVersion = void 0;
|
|
1453
|
-
for (const version of Object.keys(versions).sort(semver2.compare)) {
|
|
1454
|
-
if (!minInstallerVersion && versions[version].downloads.appImage) {
|
|
1455
|
-
minInstallerVersion = version;
|
|
1456
|
-
}
|
|
1457
|
-
if (versions[version].downloads.appImage) {
|
|
1458
|
-
maxInstallerVersion = version;
|
|
1459
|
-
}
|
|
1460
|
-
versions[version] = _4.merge({ minInstallerVersion, maxInstallerVersion }, versions[version]);
|
|
1461
|
-
}
|
|
1486
|
+
newVersions = updateObsidianVersionList({ original: newVersions, installerInfos });
|
|
1462
1487
|
const result = {
|
|
1463
1488
|
metadata: {
|
|
1464
1489
|
schemaVersion: obsidianVersionsSchemaVersion,
|
|
1465
|
-
commitDate:
|
|
1466
|
-
commitSha:
|
|
1490
|
+
commitDate: commitInfo.commitDate,
|
|
1491
|
+
commitSha: commitInfo.commitSha,
|
|
1467
1492
|
timestamp: original?.metadata.timestamp ?? ""
|
|
1468
1493
|
// set down below
|
|
1469
1494
|
},
|
|
1470
|
-
versions:
|
|
1495
|
+
versions: newVersions
|
|
1471
1496
|
};
|
|
1472
1497
|
const dayMs = 24 * 60 * 60 * 1e3;
|
|
1473
1498
|
const timeSinceLastUpdate = (/* @__PURE__ */ new Date()).getTime() - new Date(original?.metadata.timestamp ?? 0).getTime();
|
|
@@ -1516,4 +1541,4 @@ export {
|
|
|
1516
1541
|
watchFiles,
|
|
1517
1542
|
ObsidianLauncher
|
|
1518
1543
|
};
|
|
1519
|
-
//# sourceMappingURL=chunk-
|
|
1544
|
+
//# sourceMappingURL=chunk-OUPK2DAJ.js.map
|