gutterpress 0.11.23-alpha.5 → 0.11.23-alpha.7
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 +1 -1
- package/dist/api/index.d.ts +4 -2
- package/dist/api/index.js +14 -6
- package/dist/{build-4qv9wvrx.js → build-w572wq10.js} +7 -7
- package/dist/{cli-a35w7wzg.js → cli-31anr0r9.js} +107 -56
- package/dist/{cli-pth362d8.js → cli-3cvq3n1c.js} +18 -16
- package/dist/{cli-yxfznmsq.js → cli-5ba6bm6z.js} +1 -1
- package/dist/{cli-w97xcpag.js → cli-6pv84kf3.js} +4 -4
- package/dist/{cli-2t3krcfy.js → cli-eatdqx3z.js} +1 -1
- package/dist/{cli-nk0efg2x.js → cli-mmgtqnmr.js} +1 -1
- package/dist/{cli-de8ydmm7.js → cli-wyg7qstd.js} +3 -3
- package/dist/cli.js +12 -12
- package/dist/{doctor-5nh3t346.js → doctor-2f3zqmza.js} +3 -3
- package/dist/{ext-nhcep6h6.js → ext-0cgky8n3.js} +7 -7
- package/dist/{extension-restore-x3mtn844.js → extension-restore-e7je32pr.js} +4 -4
- package/dist/{extension-restore-55kp0ksq.js → extension-restore-vghc58vx.js} +2 -2
- package/dist/{index-h6yk5tjx.js → index-cw03e9p4.js} +107 -56
- package/dist/{index-9ye22m7r.js → index-fp7xwvpr.js} +3 -3
- package/dist/{index-t3jkavdz.js → index-hphcn7th.js} +3 -3
- package/dist/{index-agrkfbkv.js → index-nfysshcc.js} +252 -69
- package/dist/index.js +14 -6
- package/dist/lib/remote-auth/repair.d.ts +2 -2
- package/dist/lib/remote-auth/revisions.d.ts +64 -0
- package/dist/lib/remote-auth/scorched-earth.d.ts +2 -2
- package/dist/lib/remote-auth/sync-messages.d.ts +28 -27
- package/dist/lib/remote-auth/sync-types.d.ts +3 -0
- package/dist/lib/remote-auth/sync.d.ts +7 -8
- package/dist/lib/remote-auth/transport.d.ts +8 -1
- package/dist/lib/source-provider.d.ts +80 -35
- package/dist/{new-5a4w2vkh.js → new-88rkrv71.js} +10 -10
- package/dist/{plugins-mm8270tn.js → plugins-3aj0wc5h.js} +1 -1
- package/dist/{plugins-cfzf26b8.js → plugins-sprxsz5d.js} +3 -3
- package/dist/{preflight-jma71n0j.js → preflight-11dg1qkk.js} +7 -7
- package/dist/{preview-kmx4h7gv.js → preview-nftp782j.js} +7 -7
- package/dist/{publish-62843020.js → publish-6tgxfck9.js} +7 -7
- package/dist/render.js +2 -2
- package/dist/{source-provider-knmcam50.js → source-provider-n7vehh6t.js} +15 -3
- package/dist/{source-provider-98k5deda.js → source-provider-tgzet3j5.js} +15 -3
- package/dist/{validate-vvmrd85g.js → validate-pktqrye5.js} +7 -7
- package/package.json +1 -1
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
validatePageMargin,
|
|
38
38
|
withFetchTimeout,
|
|
39
39
|
writeManifestDoc
|
|
40
|
-
} from "./index-
|
|
40
|
+
} from "./index-hphcn7th.js";
|
|
41
41
|
import {
|
|
42
42
|
EXTENSION_MANIFEST_FILENAME,
|
|
43
43
|
GUTTERPRESS_CSS,
|
|
@@ -73,7 +73,7 @@ import {
|
|
|
73
73
|
setLogLevel,
|
|
74
74
|
slugify,
|
|
75
75
|
warn
|
|
76
|
-
} from "./index-
|
|
76
|
+
} from "./index-fp7xwvpr.js";
|
|
77
77
|
import {
|
|
78
78
|
blackTextToGray
|
|
79
79
|
} from "./index-ht1bcqdd.js";
|
|
@@ -115,18 +115,23 @@ import {
|
|
|
115
115
|
spawnCapture
|
|
116
116
|
} from "./index-bt0qws5c.js";
|
|
117
117
|
import {
|
|
118
|
+
NO_VERSION_HISTORY,
|
|
119
|
+
assertFreeRevisionName,
|
|
118
120
|
errorLogData,
|
|
119
121
|
gitFs,
|
|
120
122
|
gitScopeFor,
|
|
121
123
|
hasPendingChanges,
|
|
122
124
|
hasUncommittedChanges,
|
|
125
|
+
isContained,
|
|
123
126
|
listWorkdirChanges,
|
|
127
|
+
readRevisionRefs,
|
|
124
128
|
resolveGitAuthor,
|
|
125
129
|
resolveLogger,
|
|
130
|
+
revisionNameFrom,
|
|
126
131
|
snapshotStagingMarkerPath,
|
|
127
132
|
snapshotWorkingTreeUnlocked,
|
|
128
133
|
withRepoLock
|
|
129
|
-
} from "./index-
|
|
134
|
+
} from "./index-cw03e9p4.js";
|
|
130
135
|
import {
|
|
131
136
|
EXTENSIONS_DIR,
|
|
132
137
|
resolveVendoredPluginInstallRoot,
|
|
@@ -153,21 +158,21 @@ import { randomBytes } from "node:crypto";
|
|
|
153
158
|
import { readdir as readdir2, readFile as readFile2, writeFile, mkdir } from "node:fs/promises";
|
|
154
159
|
|
|
155
160
|
// src/lib/remote-auth/sync-messages.ts
|
|
156
|
-
var MSG_UP_TO_DATE = "
|
|
157
|
-
var MSG_UP_TO_DATE_PULLED = "
|
|
161
|
+
var MSG_UP_TO_DATE = "You're up to date with the online copy.";
|
|
162
|
+
var MSG_UP_TO_DATE_PULLED = "You're up to date. The latest changes from the online copy were downloaded to this computer.";
|
|
158
163
|
var MSG_SYNCED = "Your changes are online.";
|
|
159
164
|
var MSG_SYNCED_MERGED = "Your changes are online, combined with changes from the online copy.";
|
|
160
|
-
var MSG_OFFLINE = "Your changes are saved on this computer. Gutterpress couldn't reach the online
|
|
161
|
-
var MSG_AUTH = "The online
|
|
162
|
-
var MSG_INSECURE_TRANSPORT = "This book's online address isn't secure, so the saved
|
|
163
|
-
var MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer — try
|
|
164
|
-
var MSG_HISTORY_UNREADABLE =
|
|
165
|
-
var MSG_UNRELATED =
|
|
166
|
-
var MSG_NO_REMOTE = "This book
|
|
167
|
-
var MSG_SSH_REMOTE = "This book's online
|
|
168
|
-
var MSG_NO_BRANCH = "This book
|
|
169
|
-
var SYNC_SNAPSHOT_MESSAGE = "Automatic
|
|
170
|
-
var SYNC_LATE_EDIT_MESSAGE = "Saved the edit you made while
|
|
165
|
+
var MSG_OFFLINE = "Your changes are saved on this computer. Gutterpress couldn't reach the online copy — try again when you're back online.";
|
|
166
|
+
var MSG_AUTH = "The online copy didn't accept the saved sign-in. Sign in again and try again.";
|
|
167
|
+
var MSG_INSECURE_TRANSPORT = "This book's online copy is at an address that isn't secure, so the saved sign-in wasn't sent — sign-ins are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to back up.";
|
|
168
|
+
var MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer — try again in a moment.";
|
|
169
|
+
var MSG_HISTORY_UNREADABLE = `This book's versions can't be read, so backing up can't continue. Your writing is safe — every file is still here on this computer. Use "Repair the online copy" (in Book setup → Online copy) to get versions and backing up working again.`;
|
|
170
|
+
var MSG_UNRELATED = `This book and the online copy have no versions in common, so they can't be combined. Either the online copy's address points at a different book, or this book's own versions were lost and started over. Your writing is safe — every file is still here on this computer. Check the online copy's address; if it is correct, use "Repair the online copy" (in Book setup → Online copy).`;
|
|
171
|
+
var MSG_NO_REMOTE = "This book doesn't have an online copy yet.";
|
|
172
|
+
var MSG_SSH_REMOTE = "This book's online copy uses an SSH address (git@…), which Gutterpress can't back up to. Switch it to the web (HTTPS) address to back up from here.";
|
|
173
|
+
var MSG_NO_BRANCH = "This book isn't on a named revision, so it can't be backed up right now.";
|
|
174
|
+
var SYNC_SNAPSHOT_MESSAGE = "Automatic version before backing up";
|
|
175
|
+
var SYNC_LATE_EDIT_MESSAGE = "Saved the edit you made while backing up";
|
|
171
176
|
function onlineSiblingPath(filepath) {
|
|
172
177
|
const dot = filepath.lastIndexOf(".");
|
|
173
178
|
const slash = filepath.lastIndexOf("/");
|
|
@@ -4019,8 +4024,8 @@ function withoutOnlineTag(relPath) {
|
|
|
4019
4024
|
}
|
|
4020
4025
|
var check27 = {
|
|
4021
4026
|
id: "source.sync.merge-markers",
|
|
4022
|
-
name: "Combined
|
|
4023
|
-
description: "Finds passages and files still holding two
|
|
4027
|
+
name: "Combined Takes",
|
|
4028
|
+
description: "Finds passages and files still holding two takes (yours and the online copy's) after getting the latest changes",
|
|
4024
4029
|
category: "source",
|
|
4025
4030
|
phase: "pre-build",
|
|
4026
4031
|
async run(ctx) {
|
|
@@ -4046,7 +4051,7 @@ var check27 = {
|
|
|
4046
4051
|
results.push(finding(check27.id, {
|
|
4047
4052
|
severity: "error",
|
|
4048
4053
|
code: "two-versions-passage",
|
|
4049
|
-
message: "This passage has two
|
|
4054
|
+
message: "This passage has two takes — yours and the online copy's. Choose one in the editor's Two takes panel, or keep the text you want and delete the marker lines.",
|
|
4050
4055
|
file,
|
|
4051
4056
|
line: i + 1
|
|
4052
4057
|
}));
|
|
@@ -4055,7 +4060,7 @@ var check27 = {
|
|
|
4055
4060
|
results.push(finding(check27.id, {
|
|
4056
4061
|
severity: "error",
|
|
4057
4062
|
code: "leftover-version-marker",
|
|
4058
|
-
message: "This marker line is left over from combining two
|
|
4063
|
+
message: "This marker line is left over from combining two takes — delete it.",
|
|
4059
4064
|
file,
|
|
4060
4065
|
line: i + 1
|
|
4061
4066
|
}));
|
|
@@ -4076,7 +4081,7 @@ var check27 = {
|
|
|
4076
4081
|
results.push(finding(check27.id, {
|
|
4077
4082
|
severity: "warning",
|
|
4078
4083
|
code: "kept-both-versions",
|
|
4079
|
-
message: `Two
|
|
4084
|
+
message: `Two takes of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
|
|
4080
4085
|
file: sibling
|
|
4081
4086
|
}));
|
|
4082
4087
|
}
|
|
@@ -7356,7 +7361,7 @@ async function scaffoldProject(options) {
|
|
|
7356
7361
|
let versionHistoryError;
|
|
7357
7362
|
if (requested === "local-git") {
|
|
7358
7363
|
try {
|
|
7359
|
-
const { providerFor } = await import("./source-provider-
|
|
7364
|
+
const { providerFor } = await import("./source-provider-tgzet3j5.js");
|
|
7360
7365
|
const provider = providerFor({ type: "local-folder", path: projectDir });
|
|
7361
7366
|
await provider.initVersionHistory({
|
|
7362
7367
|
projectDir,
|
|
@@ -7368,7 +7373,7 @@ async function scaffoldProject(options) {
|
|
|
7368
7373
|
versionHistory = "none";
|
|
7369
7374
|
const { findEnclosingRepoDir } = await import("./project-source-ekcyp63q.js");
|
|
7370
7375
|
const enclosingRepoDir = await findEnclosingRepoDir(projectDir);
|
|
7371
|
-
versionHistoryError = enclosingRepoDir ? `This book was added to ${path14.basename(enclosingRepoDir)}'s shared version history; save a
|
|
7376
|
+
versionHistoryError = enclosingRepoDir ? `This book was added to ${path14.basename(enclosingRepoDir)}'s shared version history; save a version to record it.` : e instanceof Error ? e.message : String(e);
|
|
7372
7377
|
}
|
|
7373
7378
|
}
|
|
7374
7379
|
const result = {
|
|
@@ -7447,7 +7452,7 @@ styles:
|
|
|
7447
7452
|
let versionHistoryError;
|
|
7448
7453
|
if (requested === "local-git") {
|
|
7449
7454
|
try {
|
|
7450
|
-
const { providerFor } = await import("./source-provider-
|
|
7455
|
+
const { providerFor } = await import("./source-provider-tgzet3j5.js");
|
|
7451
7456
|
const provider = providerFor({ type: "local-folder", path: dir });
|
|
7452
7457
|
await provider.initVersionHistory({
|
|
7453
7458
|
projectDir: dir,
|
|
@@ -8644,7 +8649,7 @@ function isCredentialTransmissionSafe(url) {
|
|
|
8644
8649
|
class InsecureTransportError extends Error {
|
|
8645
8650
|
code = "InsecureTransport";
|
|
8646
8651
|
constructor() {
|
|
8647
|
-
super("This online address isn't secure, so the saved
|
|
8652
|
+
super("This online copy's address isn't secure, so the saved sign-in wasn't sent — " + "sign-ins are never sent over an insecure address. Switch the address " + "to a secure one (starting with https) to use a saved sign-in.");
|
|
8648
8653
|
this.name = "InsecureTransportError";
|
|
8649
8654
|
}
|
|
8650
8655
|
}
|
|
@@ -8726,7 +8731,7 @@ function failureOutcome(e, snapshotId) {
|
|
|
8726
8731
|
}
|
|
8727
8732
|
return {
|
|
8728
8733
|
status: "error",
|
|
8729
|
-
message: "
|
|
8734
|
+
message: "Backing up didn't finish. Your work is saved on this computer — please try again.",
|
|
8730
8735
|
...base
|
|
8731
8736
|
};
|
|
8732
8737
|
}
|
|
@@ -8805,8 +8810,11 @@ async function fetchRemoteTip(dir, branch, transport, http2, cache2) {
|
|
|
8805
8810
|
const code = e?.code;
|
|
8806
8811
|
if (code === "NoRefspecError")
|
|
8807
8812
|
return null;
|
|
8813
|
+
if (code === "NotFoundError" && e.data?.what === branch) {
|
|
8814
|
+
return null;
|
|
8815
|
+
}
|
|
8808
8816
|
if (code === "NotFoundError" || e?.data?.statusCode === 404) {
|
|
8809
|
-
const err = new Error("The online
|
|
8817
|
+
const err = new Error("The online copy couldn't be reached with the saved sign-in. Sign in again and try again.", { cause: e });
|
|
8810
8818
|
err.code = "HttpError";
|
|
8811
8819
|
err.data = { statusCode: 401 };
|
|
8812
8820
|
throw err;
|
|
@@ -8814,6 +8822,16 @@ async function fetchRemoteTip(dir, branch, transport, http2, cache2) {
|
|
|
8814
8822
|
throw e;
|
|
8815
8823
|
}
|
|
8816
8824
|
}
|
|
8825
|
+
async function serverTip(transport, branch, http2) {
|
|
8826
|
+
const refs = await git2.listServerRefs({
|
|
8827
|
+
http: http2,
|
|
8828
|
+
url: transport.url,
|
|
8829
|
+
prefix: `refs/heads/${branch}`,
|
|
8830
|
+
...onAuthFor(transport.credential),
|
|
8831
|
+
...transport.credential ? { onAuthFailure: () => ({ cancel: true }) } : {}
|
|
8832
|
+
});
|
|
8833
|
+
return refs.find((r) => r.ref === `refs/heads/${branch}`)?.oid;
|
|
8834
|
+
}
|
|
8817
8835
|
function short(oid) {
|
|
8818
8836
|
return oid ? oid.slice(0, 8) : "none";
|
|
8819
8837
|
}
|
|
@@ -9827,7 +9845,7 @@ var API_BASE2 = "https://api.github.com";
|
|
|
9827
9845
|
var REQUEST_TIMEOUT_MS3 = 15000;
|
|
9828
9846
|
var PER_PAGE = 100;
|
|
9829
9847
|
var MAX_PAGES = 50;
|
|
9830
|
-
var RECONNECT_MESSAGE2 = "Your GitHub
|
|
9848
|
+
var RECONNECT_MESSAGE2 = "Your GitHub sign-in has expired. Sign in to GitHub again and try again.";
|
|
9831
9849
|
async function apiGet(fetchImpl, url, token) {
|
|
9832
9850
|
const res = await withFetchTimeout({ timeoutMs: REQUEST_TIMEOUT_MS3, offlineMessage: OFFLINE_MESSAGE }, (signal) => fetchImpl(url, { method: "GET", headers: githubApiHeaders(token), signal }));
|
|
9833
9851
|
if (res.status === 401 || res.status === 403) {
|
|
@@ -10341,6 +10359,7 @@ async function syncProject(options) {
|
|
|
10341
10359
|
return {
|
|
10342
10360
|
status: "up-to-date",
|
|
10343
10361
|
message: pulled ? MSG_UP_TO_DATE_PULLED : MSG_UP_TO_DATE,
|
|
10362
|
+
pushDeferred: true,
|
|
10344
10363
|
...convergeExtras(),
|
|
10345
10364
|
...base()
|
|
10346
10365
|
};
|
|
@@ -10435,14 +10454,7 @@ async function checkRemoteUpdates(options) {
|
|
|
10435
10454
|
...options.tokenStore ? { tokenStore: options.tokenStore } : {}
|
|
10436
10455
|
});
|
|
10437
10456
|
const branch = await currentBranchOrThrow(dir);
|
|
10438
|
-
const
|
|
10439
|
-
http: http3,
|
|
10440
|
-
url: transport.url,
|
|
10441
|
-
prefix: `refs/heads/${branch}`,
|
|
10442
|
-
...onAuthFor(transport.credential),
|
|
10443
|
-
...transport.credential ? { onAuthFailure: () => ({ cancel: true }) } : {}
|
|
10444
|
-
});
|
|
10445
|
-
const remoteTip = refs.find((r) => r.ref === `refs/heads/${branch}`)?.oid;
|
|
10457
|
+
const remoteTip = await serverTip(transport, branch, http3);
|
|
10446
10458
|
if (!remoteTip)
|
|
10447
10459
|
return { available: false };
|
|
10448
10460
|
const known = async (ref) => {
|
|
@@ -10473,20 +10485,20 @@ var API_BASE3 = "https://api.github.com";
|
|
|
10473
10485
|
var REQUEST_TIMEOUT_MS4 = 15000;
|
|
10474
10486
|
var MAX_NAME_LENGTH = 100;
|
|
10475
10487
|
var MSG = {
|
|
10476
|
-
notConnected: "Connect your GitHub account first (Settings > Accounts), then set up online
|
|
10477
|
-
auth: "GitHub didn't accept the saved
|
|
10488
|
+
notConnected: "Connect your GitHub account first (Settings > Accounts), then set up the online copy.",
|
|
10489
|
+
auth: "GitHub didn't accept the saved sign-in. Sign in to GitHub again in Settings > Accounts and try again.",
|
|
10478
10490
|
offline: "Gutterpress couldn't reach GitHub. Check your internet connection and try again. Nothing was changed.",
|
|
10479
|
-
noHistory: "Turn on version history for this book first.
|
|
10491
|
+
noHistory: "Turn on version history for this book first. The online copy holds the versions Gutterpress keeps on this computer.",
|
|
10480
10492
|
alreadyHasRemote: "This book already has an online copy.",
|
|
10481
10493
|
noName: "Give the online copy a name.",
|
|
10482
|
-
badName: "
|
|
10483
|
-
notFound: "Gutterpress couldn't find that
|
|
10484
|
-
noPermission: "Your GitHub account isn't allowed to add to that
|
|
10485
|
-
noCreatePermission: "GitHub wouldn't let Gutterpress create
|
|
10486
|
-
notEmpty: "That
|
|
10487
|
-
pushFailed: "Your book couldn't be
|
|
10494
|
+
badName: "An online copy's name can only use letters, numbers, hyphens (-), underscores (_) and dots (.), and be at most 100 characters.",
|
|
10495
|
+
notFound: "Gutterpress couldn't find that online copy on GitHub, or your account can't see it. Choose another one.",
|
|
10496
|
+
noPermission: "Your GitHub account isn't allowed to add to that online copy (it may be archived or read-only). Choose another one.",
|
|
10497
|
+
noCreatePermission: "GitHub wouldn't let Gutterpress create an online copy with this sign-in. Create an empty one on github.com yourself (GitHub calls it a repository), then choose it from your existing empty online copies here.",
|
|
10498
|
+
notEmpty: "That online copy already has files in it, and a new online copy needs to be empty. Choose an empty one, or create a new one.",
|
|
10499
|
+
pushFailed: "Your book couldn't be sent to GitHub. Your writing is safe on this computer. Please try again."
|
|
10488
10500
|
};
|
|
10489
|
-
var nameTaken = (name) => `You already have
|
|
10501
|
+
var nameTaken = (name) => `You already have an online copy named "${name}" on GitHub. Pick a different name, or choose that one from your existing empty online copies.`;
|
|
10490
10502
|
|
|
10491
10503
|
class BackupFailure extends Error {
|
|
10492
10504
|
reason;
|
|
@@ -10528,7 +10540,7 @@ async function createRepository(fetchImpl, token, name, isPrivate) {
|
|
|
10528
10540
|
name,
|
|
10529
10541
|
private: isPrivate,
|
|
10530
10542
|
auto_init: false,
|
|
10531
|
-
description: "Online
|
|
10543
|
+
description: "Online copy made with Gutterpress"
|
|
10532
10544
|
});
|
|
10533
10545
|
if (res.status === 201 || res.status === 200)
|
|
10534
10546
|
return await res.json();
|
|
@@ -10541,7 +10553,7 @@ async function createRepository(fetchImpl, token, name, isPrivate) {
|
|
|
10541
10553
|
const taken = (body.errors ?? []).some((e) => /already exists/i.test(e.message ?? ""));
|
|
10542
10554
|
throw new BackupFailure("name-taken", taken ? nameTaken(name) : MSG.badName);
|
|
10543
10555
|
}
|
|
10544
|
-
throw new BackupFailure("push-failed", `GitHub couldn't create the
|
|
10556
|
+
throw new BackupFailure("push-failed", `GitHub couldn't create the online copy (error ${res.status}). Please try again.`);
|
|
10545
10557
|
}
|
|
10546
10558
|
async function loadEmptyRepository(fetchImpl, credential, owner, name) {
|
|
10547
10559
|
const path21 = `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`;
|
|
@@ -10561,7 +10573,7 @@ async function loadEmptyRepository(fetchImpl, credential, owner, name) {
|
|
|
10561
10573
|
}
|
|
10562
10574
|
const branches = await listGitHubBranches(credential, owner, name, { fetchImpl }).catch((e) => {
|
|
10563
10575
|
const msg = e instanceof Error ? e.message : "";
|
|
10564
|
-
throw new BackupFailure(/
|
|
10576
|
+
throw new BackupFailure(/sign in/i.test(msg) ? "auth" : /reach/i.test(msg) ? "offline" : "push-failed", msg || MSG.pushFailed);
|
|
10565
10577
|
});
|
|
10566
10578
|
if (branches.length > 0)
|
|
10567
10579
|
throw new BackupFailure("not-empty", MSG.notEmpty, toRemoteRepository(repo));
|
|
@@ -10652,14 +10664,14 @@ async function assertCloneTarget(dir) {
|
|
|
10652
10664
|
}
|
|
10653
10665
|
function friendlyCloneError(e) {
|
|
10654
10666
|
if (isInsecureTransportError(e)) {
|
|
10655
|
-
return new Error("That
|
|
10667
|
+
return new Error("That address isn't secure, so the saved sign-in wasn't sent — sign-ins are never sent over an insecure address. Use a secure address (starting with https), or a local loopback address for a server on this computer.", { cause: e });
|
|
10656
10668
|
}
|
|
10657
10669
|
const msg = e instanceof Error ? e.message : String(e);
|
|
10658
10670
|
if (/401|403|auth|credential/i.test(msg)) {
|
|
10659
|
-
return new Error("GitHub didn't accept the
|
|
10671
|
+
return new Error("GitHub didn't accept the saved sign-in for this book. Sign in to GitHub again and try again.", { cause: e });
|
|
10660
10672
|
}
|
|
10661
10673
|
if (/404|not found/i.test(msg)) {
|
|
10662
|
-
return new Error("That
|
|
10674
|
+
return new Error("That online copy couldn't be found. It may have been renamed or you may no longer have access.", { cause: e });
|
|
10663
10675
|
}
|
|
10664
10676
|
if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|network|fetch failed/i.test(msg)) {
|
|
10665
10677
|
return new Error(OFFLINE_MESSAGE, { cause: e });
|
|
@@ -10678,10 +10690,10 @@ async function cloneRepository(options) {
|
|
|
10678
10690
|
try {
|
|
10679
10691
|
parsed = new URL(options.url);
|
|
10680
10692
|
} catch {
|
|
10681
|
-
throw new Error("That
|
|
10693
|
+
throw new Error("That address is not a valid web address.");
|
|
10682
10694
|
}
|
|
10683
10695
|
if (!/^https?:$/.test(parsed.protocol)) {
|
|
10684
|
-
throw new Error("Only
|
|
10696
|
+
throw new Error("Only secure web (https) addresses are supported. SSH addresses (git@…) can't be used here.");
|
|
10685
10697
|
}
|
|
10686
10698
|
const { cleanUrl, credential: urlCredential } = extractUrlCredential(options.url);
|
|
10687
10699
|
const credential = options.credential ?? urlCredential;
|
|
@@ -10740,11 +10752,11 @@ function isSshRemoteUrl(url) {
|
|
|
10740
10752
|
return /^[\w.-]+@[\w.][\w.-]*:/.test(trimmed) || /^[\w.-]+@\[/.test(trimmed);
|
|
10741
10753
|
}
|
|
10742
10754
|
var FAILURE_MESSAGES = {
|
|
10743
|
-
auth: "The Git server didn't accept the saved
|
|
10744
|
-
"not-found": "The server answered, but no
|
|
10755
|
+
auth: "The Git server didn't accept the saved sign-in. Sign in to this server again with a current access token and try again.",
|
|
10756
|
+
"not-found": "The server answered, but no online copy was found at that address. It may have been moved, renamed, or you may no longer have access.",
|
|
10745
10757
|
unreachable: "Couldn't reach the Git server. Check your internet connection (and VPN, if this is a private server), then try again.",
|
|
10746
|
-
"ssh-unsupported": "This book's online
|
|
10747
|
-
"insecure-transport": "This address isn't secure, so the saved
|
|
10758
|
+
"ssh-unsupported": "This book's online copy uses an SSH address (git@…), which Gutterpress can't check or back up to. Everything on this computer still works — back up with your usual Git tool.",
|
|
10759
|
+
"insecure-transport": "This address isn't secure, so the saved sign-in wasn't sent — sign-ins are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to use a saved sign-in.",
|
|
10748
10760
|
tls: "The server's security certificate couldn't be verified. If this is a private server with its own certificate, ask its administrator about trusting it (NODE_EXTRA_CA_CERTS).",
|
|
10749
10761
|
unknown: "The connection test failed unexpectedly. The server may not be a Git server, or it may be temporarily unavailable."
|
|
10750
10762
|
};
|
|
@@ -10848,7 +10860,7 @@ function connectError(result, host) {
|
|
|
10848
10860
|
case "tls":
|
|
10849
10861
|
return new Error(result.message);
|
|
10850
10862
|
case "not-found":
|
|
10851
|
-
return new Error("The server answered, but that
|
|
10863
|
+
return new Error("The server answered, but that online copy wasn't found. Check its address and that the token has access to it.");
|
|
10852
10864
|
default:
|
|
10853
10865
|
return new Error(`Couldn't verify the connection to ${host}. Check the server address and try again.`);
|
|
10854
10866
|
}
|
|
@@ -10982,11 +10994,182 @@ async function diagnoseProjectRemote(projectDir, options = {}) {
|
|
|
10982
10994
|
guidance
|
|
10983
10995
|
};
|
|
10984
10996
|
}
|
|
10985
|
-
// src/lib/remote-auth/
|
|
10997
|
+
// src/lib/remote-auth/revisions.ts
|
|
10986
10998
|
import git9 from "isomorphic-git";
|
|
10999
|
+
function onlineFailure(e, unchanged) {
|
|
11000
|
+
const setup = setupErrorMessage(e);
|
|
11001
|
+
if (setup)
|
|
11002
|
+
return new Error(setup, { cause: e });
|
|
11003
|
+
switch (classifyTransportFailure(e)) {
|
|
11004
|
+
case "network_unavailable":
|
|
11005
|
+
return new Error(`Couldn't reach the online copy, so ${unchanged}. Try again when you're back online.`, { cause: e });
|
|
11006
|
+
case "auth_required":
|
|
11007
|
+
return new Error(`The online copy didn't accept the saved sign-in, so ${unchanged}. Sign in again and try again.`, { cause: e });
|
|
11008
|
+
case "insecure_transport":
|
|
11009
|
+
return new Error(MSG_INSECURE_TRANSPORT, { cause: e });
|
|
11010
|
+
default:
|
|
11011
|
+
return new Error(`The online copy didn't allow that, so ${unchanged}. Some online copies don't allow changing their main revision.`, { cause: e });
|
|
11012
|
+
}
|
|
11013
|
+
}
|
|
11014
|
+
function localTip(refs, name) {
|
|
11015
|
+
const tip = refs.local.get(name);
|
|
11016
|
+
if (tip)
|
|
11017
|
+
return tip;
|
|
11018
|
+
throw new Error(refs.online.has(name) ? `"${name}" is only in the online copy so far. Switch to it first.` : `Couldn't find a revision named "${name}" — it may have been renamed or deleted.`);
|
|
11019
|
+
}
|
|
11020
|
+
async function reachOnline(dir, name, tip, catchUp, options, unchanged) {
|
|
11021
|
+
const http3 = options.httpClient ?? defaultGitHttp;
|
|
11022
|
+
let transport;
|
|
11023
|
+
let onlineTip;
|
|
11024
|
+
try {
|
|
11025
|
+
transport = await resolveTransport(dir, options);
|
|
11026
|
+
onlineTip = await serverTip(transport, name, http3);
|
|
11027
|
+
} catch (e) {
|
|
11028
|
+
throw onlineFailure(e, unchanged);
|
|
11029
|
+
}
|
|
11030
|
+
if (!onlineTip)
|
|
11031
|
+
return null;
|
|
11032
|
+
if (!await isContained(dir, onlineTip, tip, {})) {
|
|
11033
|
+
throw new Error(`The online copy has versions of "${name}" that this computer doesn't have yet. ${catchUp}`);
|
|
11034
|
+
}
|
|
11035
|
+
return { transport, http: http3 };
|
|
11036
|
+
}
|
|
11037
|
+
async function sendRef(dir, { transport, http: http3 }, ref, remoteName, remove2 = false) {
|
|
11038
|
+
await git9.push({
|
|
11039
|
+
fs: gitFs,
|
|
11040
|
+
http: http3,
|
|
11041
|
+
dir,
|
|
11042
|
+
remote: transport.remote,
|
|
11043
|
+
ref,
|
|
11044
|
+
remoteRef: `refs/heads/${remoteName}`,
|
|
11045
|
+
delete: remove2,
|
|
11046
|
+
...onAuthFor(transport.credential)
|
|
11047
|
+
});
|
|
11048
|
+
if (remove2)
|
|
11049
|
+
await git9.deleteRef({ fs: gitFs, dir, ref: `refs/remotes/${transport.remote}/${remoteName}` });
|
|
11050
|
+
}
|
|
11051
|
+
async function renameRevision(options) {
|
|
11052
|
+
const source = await detectProjectSource(options.projectDir);
|
|
11053
|
+
if (source.type !== "local-git-folder")
|
|
11054
|
+
throw new Error(NO_VERSION_HISTORY);
|
|
11055
|
+
const dir = gitScopeFor(source);
|
|
11056
|
+
const { from } = options;
|
|
11057
|
+
const to = revisionNameFrom(options.to);
|
|
11058
|
+
return withRepoLock(dir, async () => {
|
|
11059
|
+
const refs = await readRevisionRefs(dir);
|
|
11060
|
+
const onlineOnly = !refs.local.has(from) && refs.online.has(from);
|
|
11061
|
+
const tip = onlineOnly ? refs.online.get(from) : localTip(refs, from);
|
|
11062
|
+
const current = refs.current === from ? to : refs.current;
|
|
11063
|
+
if (to === from)
|
|
11064
|
+
return { current, name: to };
|
|
11065
|
+
await assertFreeRevisionName(dir, to);
|
|
11066
|
+
const unchanged = `"${from}" wasn't renamed`;
|
|
11067
|
+
const catchUp = onlineOnly ? `They arrived after the list was last checked, so ${unchanged}. Try again.` : `${refs.current === from ? "Get" : `Switch to "${from}" and get`} the latest first, then try again.`;
|
|
11068
|
+
const online = refs.online.has(from) ? await reachOnline(dir, from, tip, catchUp, options, unchanged) : null;
|
|
11069
|
+
if (onlineOnly && !online) {
|
|
11070
|
+
throw new Error(`Couldn't find a revision named "${from}" in the online copy — it may have been renamed or deleted.`);
|
|
11071
|
+
}
|
|
11072
|
+
if (online) {
|
|
11073
|
+
const here = onlineOnly ? `refs/remotes/${online.transport.remote}/${from}` : `refs/heads/${from}`;
|
|
11074
|
+
try {
|
|
11075
|
+
await sendRef(dir, online, here, to);
|
|
11076
|
+
} catch (e) {
|
|
11077
|
+
throw onlineFailure(e, unchanged);
|
|
11078
|
+
}
|
|
11079
|
+
try {
|
|
11080
|
+
await sendRef(dir, online, here, from, true);
|
|
11081
|
+
} catch (e) {
|
|
11082
|
+
const undone = await sendRef(dir, online, `refs/remotes/${online.transport.remote}/${to}`, to, true).then(() => true, () => false);
|
|
11083
|
+
throw undone ? onlineFailure(e, unchanged) : new Error(`"${from}" couldn't be renamed in the online copy, which now has both "${from}" and "${to}". ` + `Your work is safe on this computer. Delete the one you don't want, or try again later.`, { cause: e });
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
if (!onlineOnly)
|
|
11087
|
+
await git9.renameBranch({ fs: gitFs, dir, ref: to, oldref: from });
|
|
11088
|
+
return { current, name: to };
|
|
11089
|
+
});
|
|
11090
|
+
}
|
|
11091
|
+
async function countVersionsOnlyIn(dir, tip, others, limit = 5000) {
|
|
11092
|
+
const cache2 = {};
|
|
11093
|
+
const waiting = new Map;
|
|
11094
|
+
const visited = new Set;
|
|
11095
|
+
const reach = async (oid, shared) => {
|
|
11096
|
+
const known = waiting.get(oid);
|
|
11097
|
+
if (known) {
|
|
11098
|
+
known.shared ||= shared;
|
|
11099
|
+
return;
|
|
11100
|
+
}
|
|
11101
|
+
if (visited.has(oid))
|
|
11102
|
+
return;
|
|
11103
|
+
const read = await git9.readCommit({ fs: gitFs, dir, cache: cache2, oid }).catch(() => {
|
|
11104
|
+
return;
|
|
11105
|
+
});
|
|
11106
|
+
if (read) {
|
|
11107
|
+
waiting.set(oid, { time: read.commit.committer.timestamp, parents: read.commit.parent, shared });
|
|
11108
|
+
}
|
|
11109
|
+
};
|
|
11110
|
+
await git9.readCommit({ fs: gitFs, dir, cache: cache2, oid: tip });
|
|
11111
|
+
await reach(tip, false);
|
|
11112
|
+
for (const oid of others)
|
|
11113
|
+
await reach(oid, true);
|
|
11114
|
+
let onlyHere = 0;
|
|
11115
|
+
for (let walked = 0;walked < limit; walked++) {
|
|
11116
|
+
const entries = [...waiting];
|
|
11117
|
+
if (entries.every(([, v]) => v.shared))
|
|
11118
|
+
break;
|
|
11119
|
+
const [oid, version] = entries.reduce((a, b) => b[1].time > a[1].time ? b : a);
|
|
11120
|
+
waiting.delete(oid);
|
|
11121
|
+
visited.add(oid);
|
|
11122
|
+
if (!version.shared)
|
|
11123
|
+
onlyHere++;
|
|
11124
|
+
for (const parent of version.parents)
|
|
11125
|
+
await reach(parent, version.shared);
|
|
11126
|
+
}
|
|
11127
|
+
return onlyHere;
|
|
11128
|
+
}
|
|
11129
|
+
async function deleteRevision(options) {
|
|
11130
|
+
const source = await detectProjectSource(options.projectDir);
|
|
11131
|
+
if (source.type !== "local-git-folder")
|
|
11132
|
+
throw new Error(NO_VERSION_HISTORY);
|
|
11133
|
+
const dir = gitScopeFor(source);
|
|
11134
|
+
const { name } = options;
|
|
11135
|
+
return withRepoLock(dir, async () => {
|
|
11136
|
+
const refs = await readRevisionRefs(dir);
|
|
11137
|
+
if (name === refs.current) {
|
|
11138
|
+
throw new Error(`You're working on "${name}" right now. Switch to another revision first, then delete it.`);
|
|
11139
|
+
}
|
|
11140
|
+
const onlineOnly = !refs.local.has(name) && refs.online.has(name);
|
|
11141
|
+
const tip = onlineOnly ? refs.online.get(name) : localTip(refs, name);
|
|
11142
|
+
const everywhere = onlineOnly || options.everywhere === true && refs.online.has(name);
|
|
11143
|
+
const unchanged = `"${name}" wasn't deleted`;
|
|
11144
|
+
const catchUp = onlineOnly ? `They arrived after the list was last checked, so ${unchanged}. Try again.` : `Switch to "${name}" and get the latest first, then try again.`;
|
|
11145
|
+
const online = everywhere ? await reachOnline(dir, name, tip, catchUp, options, unchanged) : null;
|
|
11146
|
+
if (!options.discard) {
|
|
11147
|
+
const others = [
|
|
11148
|
+
...[...refs.local].filter(([n]) => n !== name),
|
|
11149
|
+
...[...refs.online].filter(([n]) => !(everywhere && n === name))
|
|
11150
|
+
].map(([, otherTip]) => otherTip);
|
|
11151
|
+
const versionsOnlyHere = await countVersionsOnlyIn(dir, tip, others);
|
|
11152
|
+
if (versionsOnlyHere > 0)
|
|
11153
|
+
return { deleted: false, versionsOnlyHere };
|
|
11154
|
+
}
|
|
11155
|
+
if (online) {
|
|
11156
|
+
const ref = onlineOnly ? `refs/remotes/${online.transport.remote}/${name}` : `refs/heads/${name}`;
|
|
11157
|
+
try {
|
|
11158
|
+
await sendRef(dir, online, ref, name, true);
|
|
11159
|
+
} catch (e) {
|
|
11160
|
+
throw onlineFailure(e, unchanged);
|
|
11161
|
+
}
|
|
11162
|
+
}
|
|
11163
|
+
if (!onlineOnly)
|
|
11164
|
+
await git9.deleteBranch({ fs: gitFs, dir, ref: name });
|
|
11165
|
+
return { deleted: true };
|
|
11166
|
+
});
|
|
11167
|
+
}
|
|
11168
|
+
// src/lib/remote-auth/repair.ts
|
|
11169
|
+
import git10 from "isomorphic-git";
|
|
10987
11170
|
import { cp as cp4, mkdir as mkdir13, rename as rename3, rm as rm4 } from "node:fs/promises";
|
|
10988
11171
|
import path22 from "node:path";
|
|
10989
|
-
var REPAIR_SNAPSHOT_MESSAGE = "Repaired online
|
|
11172
|
+
var REPAIR_SNAPSHOT_MESSAGE = "Repaired the online copy";
|
|
10990
11173
|
async function moveDir(from, to) {
|
|
10991
11174
|
await mkdir13(path22.dirname(to), { recursive: true });
|
|
10992
11175
|
try {
|
|
@@ -11008,7 +11191,7 @@ async function repairOnlineBackup(options) {
|
|
|
11008
11191
|
const transport = await resolveTransport(dir, options);
|
|
11009
11192
|
let branch;
|
|
11010
11193
|
try {
|
|
11011
|
-
branch = await
|
|
11194
|
+
branch = await git10.currentBranch({ fs: gitFs, dir }) ?? undefined;
|
|
11012
11195
|
} catch {
|
|
11013
11196
|
branch = undefined;
|
|
11014
11197
|
}
|
|
@@ -11034,10 +11217,10 @@ async function repairOnlineBackup(options) {
|
|
|
11034
11217
|
const ref = fresh.branch ?? "HEAD";
|
|
11035
11218
|
let missing = [];
|
|
11036
11219
|
try {
|
|
11037
|
-
const tracked = await
|
|
11220
|
+
const tracked = await git10.listFiles({ fs: gitFs, dir, ref });
|
|
11038
11221
|
missing = tracked.filter((f) => !gitFs.existsSync(path22.join(dir, f)));
|
|
11039
11222
|
if (missing.length > 0) {
|
|
11040
|
-
await
|
|
11223
|
+
await git10.checkout({ fs: gitFs, dir, ref, filepaths: missing, force: true });
|
|
11041
11224
|
logger.info("restore", "restored files missing on this computer", { files: missing });
|
|
11042
11225
|
}
|
|
11043
11226
|
} catch (e) {
|
|
@@ -11064,7 +11247,7 @@ async function repairOnlineBackup(options) {
|
|
|
11064
11247
|
return { outcome, movedGitTo, restoredFiles };
|
|
11065
11248
|
}
|
|
11066
11249
|
// src/lib/remote-auth/scorched-earth.ts
|
|
11067
|
-
import
|
|
11250
|
+
import git11 from "isomorphic-git";
|
|
11068
11251
|
import { cp as cp5, mkdir as mkdir14, readdir as readdir10, rm as rm5 } from "node:fs/promises";
|
|
11069
11252
|
import path23 from "node:path";
|
|
11070
11253
|
async function emptyDir(dir) {
|
|
@@ -11090,7 +11273,7 @@ async function scorchedEarth(options) {
|
|
|
11090
11273
|
const transport2 = await resolveTransport(dir, options);
|
|
11091
11274
|
let branch2;
|
|
11092
11275
|
try {
|
|
11093
|
-
branch2 = await
|
|
11276
|
+
branch2 = await git11.currentBranch({ fs: gitFs, dir }) ?? undefined;
|
|
11094
11277
|
} catch {
|
|
11095
11278
|
branch2 = undefined;
|
|
11096
11279
|
}
|
|
@@ -12476,7 +12659,7 @@ async function resolvePublishProvider(id, book) {
|
|
|
12476
12659
|
const ext = await bookExtensions(book);
|
|
12477
12660
|
const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
|
|
12478
12661
|
if (npmEntry) {
|
|
12479
|
-
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-
|
|
12662
|
+
const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-vghc58vx.js");
|
|
12480
12663
|
await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
|
|
12481
12664
|
}
|
|
12482
12665
|
const publishers = await discover(ext);
|
|
@@ -12514,7 +12697,7 @@ function infoFor(p) {
|
|
|
12514
12697
|
}
|
|
12515
12698
|
function pluginProvider(p) {
|
|
12516
12699
|
let loading;
|
|
12517
|
-
const load = () => loading ??= import("./plugins-
|
|
12700
|
+
const load = () => loading ??= import("./plugins-3aj0wc5h.js").then((m) => m.loadPublishFunction(p));
|
|
12518
12701
|
return {
|
|
12519
12702
|
info: infoFor(p),
|
|
12520
12703
|
async authenticate() {
|
|
@@ -12977,4 +13160,4 @@ async function savePublishSettings(projectDir, providerId, values, selections) {
|
|
|
12977
13160
|
await setPublishProviderConfig(projectDir, providerId, changes);
|
|
12978
13161
|
return readPublishSettings(projectDir, selections);
|
|
12979
13162
|
}
|
|
12980
|
-
export { SYNC_SNAPSHOT_MESSAGE, resolveActiveStyles, listProjectStyles, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, readPageSetup, setPageSetup, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets, saveSnippet, deleteSnippet, listMergedSnippets, listMarkerComponents, listLocationVariants, importExtensionFromFile, importExtensionFromUrl, MAX_SEARCH_BYTES, SEARCH_KEYWORDS, searchNpmExtensions, setManifestFields, readManifestFields, setActiveStyles, VERSION_CHANGES_SHOWN, describeVersions, listOnlineVersions, compareVersionFile, exportVersionTree, AUTO_SNAPSHOT_DEFAULT_MINUTES, AUTO_SYNC_MIN_MINUTES, AUTO_SYNC_MAX_MINUTES, AUTO_SYNC_DEFAULT_MINUTES, AUTO_SYNC_PUSH_INTERVAL_MINUTES, autoSnapshotDelayMs, autoSyncDelayMs, isGitInternalPath, resolveGitHubClientId, GITHUB_HOST, GitHubAuthProvider, GDRIVE_HOST, resolveGoogleClientId, resolveGoogleClientSecret, GOOGLE_NOT_CONFIGURED_MESSAGE, requireGoogleClientCredentials, pkceChallengeFromVerifier, GoogleAuthProvider, revokeGoogleCredential, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listGitHubRepositories, listRepoBooks, listGitHubBranches, syncProject, refreshRemoteCopies, checkRemoteUpdates, isValidRepositoryName, setUpOnlineBackup, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig, savePublishSettings };
|
|
13163
|
+
export { SYNC_SNAPSHOT_MESSAGE, resolveActiveStyles, listProjectStyles, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, readPageSetup, setPageSetup, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets, saveSnippet, deleteSnippet, listMergedSnippets, listMarkerComponents, listLocationVariants, importExtensionFromFile, importExtensionFromUrl, MAX_SEARCH_BYTES, SEARCH_KEYWORDS, searchNpmExtensions, setManifestFields, readManifestFields, setActiveStyles, VERSION_CHANGES_SHOWN, describeVersions, listOnlineVersions, compareVersionFile, exportVersionTree, AUTO_SNAPSHOT_DEFAULT_MINUTES, AUTO_SYNC_MIN_MINUTES, AUTO_SYNC_MAX_MINUTES, AUTO_SYNC_DEFAULT_MINUTES, AUTO_SYNC_PUSH_INTERVAL_MINUTES, autoSnapshotDelayMs, autoSyncDelayMs, isGitInternalPath, resolveGitHubClientId, GITHUB_HOST, GitHubAuthProvider, GDRIVE_HOST, resolveGoogleClientId, resolveGoogleClientSecret, GOOGLE_NOT_CONFIGURED_MESSAGE, requireGoogleClientCredentials, pkceChallengeFromVerifier, GoogleAuthProvider, revokeGoogleCredential, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listGitHubRepositories, listRepoBooks, listGitHubBranches, syncProject, refreshRemoteCopies, checkRemoteUpdates, isValidRepositoryName, setUpOnlineBackup, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, renameRevision, deleteRevision, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig, savePublishSettings };
|