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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/api/index.d.ts +4 -2
  3. package/dist/api/index.js +14 -6
  4. package/dist/{build-4qv9wvrx.js → build-w572wq10.js} +7 -7
  5. package/dist/{cli-a35w7wzg.js → cli-31anr0r9.js} +107 -56
  6. package/dist/{cli-pth362d8.js → cli-3cvq3n1c.js} +18 -16
  7. package/dist/{cli-yxfznmsq.js → cli-5ba6bm6z.js} +1 -1
  8. package/dist/{cli-w97xcpag.js → cli-6pv84kf3.js} +4 -4
  9. package/dist/{cli-2t3krcfy.js → cli-eatdqx3z.js} +1 -1
  10. package/dist/{cli-nk0efg2x.js → cli-mmgtqnmr.js} +1 -1
  11. package/dist/{cli-de8ydmm7.js → cli-wyg7qstd.js} +3 -3
  12. package/dist/cli.js +12 -12
  13. package/dist/{doctor-5nh3t346.js → doctor-2f3zqmza.js} +3 -3
  14. package/dist/{ext-nhcep6h6.js → ext-0cgky8n3.js} +7 -7
  15. package/dist/{extension-restore-x3mtn844.js → extension-restore-e7je32pr.js} +4 -4
  16. package/dist/{extension-restore-55kp0ksq.js → extension-restore-vghc58vx.js} +2 -2
  17. package/dist/{index-h6yk5tjx.js → index-cw03e9p4.js} +107 -56
  18. package/dist/{index-9ye22m7r.js → index-fp7xwvpr.js} +3 -3
  19. package/dist/{index-t3jkavdz.js → index-hphcn7th.js} +3 -3
  20. package/dist/{index-agrkfbkv.js → index-nfysshcc.js} +252 -69
  21. package/dist/index.js +14 -6
  22. package/dist/lib/remote-auth/repair.d.ts +2 -2
  23. package/dist/lib/remote-auth/revisions.d.ts +64 -0
  24. package/dist/lib/remote-auth/scorched-earth.d.ts +2 -2
  25. package/dist/lib/remote-auth/sync-messages.d.ts +28 -27
  26. package/dist/lib/remote-auth/sync-types.d.ts +3 -0
  27. package/dist/lib/remote-auth/sync.d.ts +7 -8
  28. package/dist/lib/remote-auth/transport.d.ts +8 -1
  29. package/dist/lib/source-provider.d.ts +80 -35
  30. package/dist/{new-5a4w2vkh.js → new-88rkrv71.js} +10 -10
  31. package/dist/{plugins-mm8270tn.js → plugins-3aj0wc5h.js} +1 -1
  32. package/dist/{plugins-cfzf26b8.js → plugins-sprxsz5d.js} +3 -3
  33. package/dist/{preflight-jma71n0j.js → preflight-11dg1qkk.js} +7 -7
  34. package/dist/{preview-kmx4h7gv.js → preview-nftp782j.js} +7 -7
  35. package/dist/{publish-62843020.js → publish-6tgxfck9.js} +7 -7
  36. package/dist/render.js +2 -2
  37. package/dist/{source-provider-knmcam50.js → source-provider-n7vehh6t.js} +15 -3
  38. package/dist/{source-provider-98k5deda.js → source-provider-tgzet3j5.js} +15 -3
  39. package/dist/{validate-vvmrd85g.js → validate-pktqrye5.js} +7 -7
  40. package/package.json +1 -1
@@ -37,7 +37,7 @@ import {
37
37
  validatePageMargin,
38
38
  withFetchTimeout,
39
39
  writeManifestDoc
40
- } from "./index-t3jkavdz.js";
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-9ye22m7r.js";
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-h6yk5tjx.js";
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 = "Everything is in sync.";
157
- var MSG_UP_TO_DATE_PULLED = "Everything is in sync. The latest online changes were downloaded to this computer.";
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 repository — try syncing again when you're back online.";
161
- var MSG_AUTH = "The online repository didn't accept the saved connection. Reconnect and try again.";
162
- var MSG_INSECURE_TRANSPORT = "This book's online address isn't secure, so the saved connection wasn't sent — connections 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 sync.";
163
- var MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer — try Sync again in a moment.";
164
- var MSG_HISTORY_UNREADABLE = "This book's version history can't be read, so syncing can't continue. Your writing is safe — every file is still here on this computer. Use Repair online backup (in Where your work is kept) to get history and syncing working again.";
165
- var MSG_UNRELATED = "This book and the online copy have no history in common, so they can't be combined. Either the online address points at a different book, or this book's own history was lost and started over. Your writing is safe — every file is still here on this computer. Check the online address; if it is correct, use Repair online backup (in Where your work is kept).";
166
- var MSG_NO_REMOTE = "This book isn't connected to an online repository yet.";
167
- var MSG_SSH_REMOTE = "This book's online address uses SSH (git@…), which Gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
168
- var MSG_NO_BRANCH = "This book's version history isn't on a named branch, so it can't be synced right now.";
169
- var SYNC_SNAPSHOT_MESSAGE = "Automatic backup of your work";
170
- var SYNC_LATE_EDIT_MESSAGE = "Saved the edit you made while syncing";
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 Versions",
4023
- description: "Finds passages and files still holding two versions (yours and the online version) after a sync",
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 versions — yours and the online version. Choose one in the editor's Two versions panel, or keep the text you want and delete the marker lines.",
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 versions — delete it.",
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 versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
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-98k5deda.js");
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 snapshot to record it.` : e instanceof Error ? e.message : String(e);
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-98k5deda.js");
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 connection wasn't sent — " + "connections are never sent over an insecure address. Switch the address " + "to a secure one (starting with https) to sync with a saved connection.");
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: "Syncing didn't complete. Your work is saved on this computer — please try again.",
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 repository couldn't be accessed with the saved connection. Reconnect and try again.", { cause: e });
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 connection has expired. Reconnect GitHub and try again.";
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 refs = await git5.listServerRefs({
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 backup.",
10477
- auth: "GitHub didn't accept the saved connection. Reconnect GitHub in Settings > Accounts and try again.",
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. Online backup copies the versions Gutterpress keeps on this computer.",
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: "A repository name can only use letters, numbers, hyphens (-), underscores (_) and dots (.), and be at most 100 characters.",
10483
- notFound: "Gutterpress couldn't find that repository on GitHub, or your account can't see it. Choose another one.",
10484
- noPermission: "Your GitHub account isn't allowed to add to that repository (it may be archived or read-only). Choose another one.",
10485
- noCreatePermission: "GitHub wouldn't let Gutterpress create a repository with this connection. Create an empty repository on github.com yourself, then choose it from your existing repositories here.",
10486
- notEmpty: "That repository already has files in it, and online backup needs an empty one. Choose an empty repository, or create a new one.",
10487
- pushFailed: "Your book couldn't be copied to GitHub. Your writing is safe on this computer. Please try again."
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 a repository named "${name}" on GitHub. Pick a different name, or choose that repository from your existing empty repositories.`;
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 backup made with Gutterpress"
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 repository (error ${res.status}). Please try again.`);
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(/reconnect/i.test(msg) ? "auth" : /reach/i.test(msg) ? "offline" : "push-failed", msg || MSG.pushFailed);
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 repository address isn't secure, so the saved connection wasn't sent — connections 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 });
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 connection for this repository. Reconnect GitHub and try again.", { cause: e });
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 repository couldn't be found. It may have been renamed or you may no longer have access.", { cause: e });
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 repository address is not a valid web URL.");
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 HTTPS repository addresses are supported. SSH addresses (git@…) can't be used here.");
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 connection. Reconnect to this server with a current access token and try again.",
10744
- "not-found": "The server answered, but no repository was found at that address. It may have been moved, renamed, or you may no longer have access.",
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 address uses SSH (git@…), which Gutterpress can't check or sync with. Everything on this computer still works — sync with your usual Git tool.",
10747
- "insecure-transport": "This address isn't secure, so the saved connection wasn't sent — connections 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 connection.",
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 repository wasn't found. Check the repository address and that the token has access to it.");
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/repair.ts
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 backup";
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 git9.currentBranch({ fs: gitFs, dir }) ?? undefined;
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 git9.listFiles({ fs: gitFs, dir, ref });
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 git9.checkout({ fs: gitFs, dir, ref, filepaths: missing, force: true });
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 git10 from "isomorphic-git";
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 git10.currentBranch({ fs: gitFs, dir }) ?? undefined;
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-55kp0ksq.js");
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-mm8270tn.js").then((m) => m.loadPublishFunction(p));
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 };