gutterpress 0.11.23-alpha.4 → 0.11.23-alpha.6

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 (52) hide show
  1. package/README.md +1 -1
  2. package/dist/api/index.d.ts +5 -3
  3. package/dist/api/index.js +17 -7
  4. package/dist/{build-c48eer6f.js → build-py643c5a.js} +8 -8
  5. package/dist/{cli-93mkaw6v.js → cli-09jj8w5g.js} +63 -31
  6. package/dist/{cli-a35w7wzg.js → cli-31anr0r9.js} +107 -56
  7. package/dist/{cli-y6d1arem.js → cli-55br530w.js} +4 -4
  8. package/dist/{cli-54xt7pjg.js → cli-58fsk6d7.js} +2 -2
  9. package/dist/{cli-d9mm3vvh.js → cli-bnf7eb38.js} +1 -1
  10. package/dist/{cli-gnpea9dx.js → cli-qzkq9em8.js} +1 -1
  11. package/dist/{cli-pspjxnc8.js → cli-tf9tw6at.js} +1 -1
  12. package/dist/{cli-rqh295j9.js → cli-x777v272.js} +3 -3
  13. package/dist/cli.js +12 -12
  14. package/dist/{doctor-ptb346k8.js → doctor-tyrzqphh.js} +3 -3
  15. package/dist/{engine-4rzdx6af.js → engine-4rabsgwq.js} +1 -1
  16. package/dist/{engine-6txhxmmv.js → engine-wm4fggac.js} +2 -2
  17. package/dist/{ext-gy8szg3x.js → ext-70kt3eft.js} +8 -8
  18. package/dist/{extension-restore-m8tj43e7.js → extension-restore-fjfn1hrk.js} +4 -4
  19. package/dist/{extension-restore-dymzcasz.js → extension-restore-whag4sm0.js} +2 -2
  20. package/dist/{index-8ywkd2sv.js → index-1zqkfzcd.js} +3 -3
  21. package/dist/{index-5kh4c78h.js → index-2vxzxyyj.js} +2 -2
  22. package/dist/{index-kaks3mm2.js → index-cmakyf43.js} +3 -3
  23. package/dist/{index-h6yk5tjx.js → index-cw03e9p4.js} +107 -56
  24. package/dist/{index-ea94zbq4.js → index-ejbfetdn.js} +327 -86
  25. package/dist/{index-yc3xj266.js → index-wm4x7xbf.js} +1 -1
  26. package/dist/index.js +18 -8
  27. package/dist/lib/publish/connect.d.ts +8 -1
  28. package/dist/lib/publish/manifest-publish.d.ts +26 -2
  29. package/dist/lib/publish/selections.d.ts +12 -1
  30. package/dist/lib/publish/types.d.ts +15 -5
  31. package/dist/lib/remote-auth/repair.d.ts +2 -2
  32. package/dist/lib/remote-auth/revisions.d.ts +60 -0
  33. package/dist/lib/remote-auth/scorched-earth.d.ts +2 -2
  34. package/dist/lib/remote-auth/sync-messages.d.ts +28 -27
  35. package/dist/lib/remote-auth/sync-types.d.ts +3 -0
  36. package/dist/lib/remote-auth/sync.d.ts +7 -8
  37. package/dist/lib/remote-auth/transport.d.ts +8 -1
  38. package/dist/lib/source-provider.d.ts +80 -35
  39. package/dist/{manifest.schema-w86c1xw1.json → manifest.schema-93sd7zje.json} +13 -6
  40. package/dist/{new-t2t28ccb.js → new-b27kyr02.js} +11 -11
  41. package/dist/{plugins-fec8t2bh.js → plugins-racab3cj.js} +1 -1
  42. package/dist/{plugins-sqa4msdk.js → plugins-vpx7c53c.js} +3 -3
  43. package/dist/{preflight-srq8fktp.js → preflight-akdqz9v8.js} +8 -8
  44. package/dist/{preview-zsp9hstd.js → preview-bck9f88w.js} +8 -8
  45. package/dist/{preview-interface-nnmrxyqm.js → preview-interface-0xdh0r5b.js} +3 -66
  46. package/dist/{publish-hhy31gr9.js → publish-keyp8rh4.js} +8 -8
  47. package/dist/render.js +2 -2
  48. package/dist/schema/manifest.types.d.ts +18 -3
  49. package/dist/{source-provider-knmcam50.js → source-provider-n7vehh6t.js} +15 -3
  50. package/dist/{source-provider-98k5deda.js → source-provider-tgzet3j5.js} +15 -3
  51. package/dist/{validate-r9jdz85h.js → validate-z28cvr5x.js} +8 -8
  52. package/package.json +1 -1
@@ -37,7 +37,7 @@ import {
37
37
  validatePageMargin,
38
38
  withFetchTimeout,
39
39
  writeManifestDoc
40
- } from "./index-kaks3mm2.js";
40
+ } from "./index-cmakyf43.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-8ywkd2sv.js";
76
+ } from "./index-1zqkfzcd.js";
77
77
  import {
78
78
  blackTextToGray
79
79
  } from "./index-ht1bcqdd.js";
@@ -84,7 +84,7 @@ import {
84
84
  getAssetPath,
85
85
  lastPageMode,
86
86
  resolvePage
87
- } from "./index-5kh4c78h.js";
87
+ } from "./index-2vxzxyyj.js";
88
88
  import {
89
89
  BuildError
90
90
  } from "./index-46ycxe6r.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
  }
@@ -5527,7 +5532,7 @@ class PdfOutput {
5527
5532
  const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
5528
5533
  await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
5529
5534
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
5530
- const { buildNativePdf } = await import("./engine-6txhxmmv.js");
5535
+ const { buildNativePdf } = await import("./engine-wm4fggac.js");
5531
5536
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
5532
5537
  title: config.title,
5533
5538
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -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
  }
@@ -10858,6 +10870,9 @@ async function connectGenericHost(input, options = {}) {
10858
10870
  if (!host) {
10859
10871
  throw new Error("Enter your Git server's web address (like https://git.example.com).");
10860
10872
  }
10873
+ if (host === GITHUB_HOST) {
10874
+ throw new Error("GitHub has its own sign-in. Use Connect GitHub instead of a Git server.");
10875
+ }
10861
10876
  const token = input.token?.trim();
10862
10877
  if (!token) {
10863
10878
  throw new Error("Paste an access token from your Git server to connect.");
@@ -10979,11 +10994,171 @@ async function diagnoseProjectRemote(projectDir, options = {}) {
10979
10994
  guidance
10980
10995
  };
10981
10996
  }
10982
- // src/lib/remote-auth/repair.ts
10997
+ // src/lib/remote-auth/revisions.ts
10983
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, open, 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. ` + `${open ? "Get" : `Switch to "${name}" and get`} the latest first, then try again.`);
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 tip = localTip(refs, from);
11061
+ const current = refs.current === from ? to : refs.current;
11062
+ if (to === from)
11063
+ return { current, name: to };
11064
+ await assertFreeRevisionName(dir, to);
11065
+ const unchanged = `"${from}" wasn't renamed`;
11066
+ const online = refs.online.has(from) ? await reachOnline(dir, from, tip, refs.current === from, options, unchanged) : null;
11067
+ if (online) {
11068
+ const here = `refs/heads/${from}`;
11069
+ try {
11070
+ await sendRef(dir, online, here, to);
11071
+ } catch (e) {
11072
+ throw onlineFailure(e, unchanged);
11073
+ }
11074
+ try {
11075
+ await sendRef(dir, online, here, from, true);
11076
+ } catch (e) {
11077
+ const undone = await sendRef(dir, online, `refs/remotes/${online.transport.remote}/${to}`, to, true).then(() => true, () => false);
11078
+ 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 });
11079
+ }
11080
+ }
11081
+ await git9.renameBranch({ fs: gitFs, dir, ref: to, oldref: from });
11082
+ return { current, name: to };
11083
+ });
11084
+ }
11085
+ async function countVersionsOnlyIn(dir, tip, others, limit = 5000) {
11086
+ const cache2 = {};
11087
+ const waiting = new Map;
11088
+ const visited = new Set;
11089
+ const reach = async (oid, shared) => {
11090
+ const known = waiting.get(oid);
11091
+ if (known) {
11092
+ known.shared ||= shared;
11093
+ return;
11094
+ }
11095
+ if (visited.has(oid))
11096
+ return;
11097
+ const read = await git9.readCommit({ fs: gitFs, dir, cache: cache2, oid }).catch(() => {
11098
+ return;
11099
+ });
11100
+ if (read) {
11101
+ waiting.set(oid, { time: read.commit.committer.timestamp, parents: read.commit.parent, shared });
11102
+ }
11103
+ };
11104
+ await git9.readCommit({ fs: gitFs, dir, cache: cache2, oid: tip });
11105
+ await reach(tip, false);
11106
+ for (const oid of others)
11107
+ await reach(oid, true);
11108
+ let onlyHere = 0;
11109
+ for (let walked = 0;walked < limit; walked++) {
11110
+ const entries = [...waiting];
11111
+ if (entries.every(([, v]) => v.shared))
11112
+ break;
11113
+ const [oid, version] = entries.reduce((a, b) => b[1].time > a[1].time ? b : a);
11114
+ waiting.delete(oid);
11115
+ visited.add(oid);
11116
+ if (!version.shared)
11117
+ onlyHere++;
11118
+ for (const parent of version.parents)
11119
+ await reach(parent, version.shared);
11120
+ }
11121
+ return onlyHere;
11122
+ }
11123
+ async function deleteRevision(options) {
11124
+ const source = await detectProjectSource(options.projectDir);
11125
+ if (source.type !== "local-git-folder")
11126
+ throw new Error(NO_VERSION_HISTORY);
11127
+ const dir = gitScopeFor(source);
11128
+ const { name } = options;
11129
+ return withRepoLock(dir, async () => {
11130
+ const refs = await readRevisionRefs(dir);
11131
+ if (name === refs.current) {
11132
+ throw new Error(`You're working on "${name}" right now. Switch to another revision first, then delete it.`);
11133
+ }
11134
+ const tip = localTip(refs, name);
11135
+ const everywhere = options.everywhere === true && refs.online.has(name);
11136
+ const online = everywhere ? await reachOnline(dir, name, tip, false, options, `"${name}" wasn't deleted`) : null;
11137
+ if (!options.discard) {
11138
+ const others = [
11139
+ ...[...refs.local].filter(([n]) => n !== name),
11140
+ ...[...refs.online].filter(([n]) => !(everywhere && n === name))
11141
+ ].map(([, otherTip]) => otherTip);
11142
+ const versionsOnlyHere = await countVersionsOnlyIn(dir, tip, others);
11143
+ if (versionsOnlyHere > 0)
11144
+ return { deleted: false, versionsOnlyHere };
11145
+ }
11146
+ if (online) {
11147
+ try {
11148
+ await sendRef(dir, online, `refs/heads/${name}`, name, true);
11149
+ } catch (e) {
11150
+ throw onlineFailure(e, `"${name}" wasn't deleted`);
11151
+ }
11152
+ }
11153
+ await git9.deleteBranch({ fs: gitFs, dir, ref: name });
11154
+ return { deleted: true };
11155
+ });
11156
+ }
11157
+ // src/lib/remote-auth/repair.ts
11158
+ import git10 from "isomorphic-git";
10984
11159
  import { cp as cp4, mkdir as mkdir13, rename as rename3, rm as rm4 } from "node:fs/promises";
10985
11160
  import path22 from "node:path";
10986
- var REPAIR_SNAPSHOT_MESSAGE = "Repaired online backup";
11161
+ var REPAIR_SNAPSHOT_MESSAGE = "Repaired the online copy";
10987
11162
  async function moveDir(from, to) {
10988
11163
  await mkdir13(path22.dirname(to), { recursive: true });
10989
11164
  try {
@@ -11005,7 +11180,7 @@ async function repairOnlineBackup(options) {
11005
11180
  const transport = await resolveTransport(dir, options);
11006
11181
  let branch;
11007
11182
  try {
11008
- branch = await git9.currentBranch({ fs: gitFs, dir }) ?? undefined;
11183
+ branch = await git10.currentBranch({ fs: gitFs, dir }) ?? undefined;
11009
11184
  } catch {
11010
11185
  branch = undefined;
11011
11186
  }
@@ -11031,10 +11206,10 @@ async function repairOnlineBackup(options) {
11031
11206
  const ref = fresh.branch ?? "HEAD";
11032
11207
  let missing = [];
11033
11208
  try {
11034
- const tracked = await git9.listFiles({ fs: gitFs, dir, ref });
11209
+ const tracked = await git10.listFiles({ fs: gitFs, dir, ref });
11035
11210
  missing = tracked.filter((f) => !gitFs.existsSync(path22.join(dir, f)));
11036
11211
  if (missing.length > 0) {
11037
- await git9.checkout({ fs: gitFs, dir, ref, filepaths: missing, force: true });
11212
+ await git10.checkout({ fs: gitFs, dir, ref, filepaths: missing, force: true });
11038
11213
  logger.info("restore", "restored files missing on this computer", { files: missing });
11039
11214
  }
11040
11215
  } catch (e) {
@@ -11061,7 +11236,7 @@ async function repairOnlineBackup(options) {
11061
11236
  return { outcome, movedGitTo, restoredFiles };
11062
11237
  }
11063
11238
  // src/lib/remote-auth/scorched-earth.ts
11064
- import git10 from "isomorphic-git";
11239
+ import git11 from "isomorphic-git";
11065
11240
  import { cp as cp5, mkdir as mkdir14, readdir as readdir10, rm as rm5 } from "node:fs/promises";
11066
11241
  import path23 from "node:path";
11067
11242
  async function emptyDir(dir) {
@@ -11087,7 +11262,7 @@ async function scorchedEarth(options) {
11087
11262
  const transport2 = await resolveTransport(dir, options);
11088
11263
  let branch2;
11089
11264
  try {
11090
- branch2 = await git10.currentBranch({ fs: gitFs, dir }) ?? undefined;
11265
+ branch2 = await git11.currentBranch({ fs: gitFs, dir }) ?? undefined;
11091
11266
  } catch {
11092
11267
  branch2 = undefined;
11093
11268
  }
@@ -12417,7 +12592,7 @@ function publishProviderFor(id) {
12417
12592
  }
12418
12593
 
12419
12594
  // src/lib/publish/plugin-provider.ts
12420
- var RESERVED_SETTINGS = new Set(["format", "credential"]);
12595
+ var RESERVED_SETTINGS = new Set(["format", "credential", "enabled"]);
12421
12596
  var isBuiltIn = (id) => listPublishProviders().some((p) => p.id === id);
12422
12597
  var messageOf = (e) => e instanceof Error ? e.message : String(e);
12423
12598
  async function bookExtensions(book) {
@@ -12473,7 +12648,7 @@ async function resolvePublishProvider(id, book) {
12473
12648
  const ext = await bookExtensions(book);
12474
12649
  const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
12475
12650
  if (npmEntry) {
12476
- const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-dymzcasz.js");
12651
+ const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-whag4sm0.js");
12477
12652
  await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
12478
12653
  }
12479
12654
  const publishers = await discover(ext);
@@ -12511,7 +12686,7 @@ function infoFor(p) {
12511
12686
  }
12512
12687
  function pluginProvider(p) {
12513
12688
  let loading;
12514
- const load = () => loading ??= import("./plugins-fec8t2bh.js").then((m) => m.loadPublishFunction(p));
12689
+ const load = () => loading ??= import("./plugins-racab3cj.js").then((m) => m.loadPublishFunction(p));
12515
12690
  return {
12516
12691
  info: infoFor(p),
12517
12692
  async authenticate() {
@@ -12578,7 +12753,10 @@ async function requestFor(provider, options, deps) {
12578
12753
  const { manifest, manifestDir } = await loadManifestWithPath(options.manifestPath ?? options.projectDir, { explicit: options.manifestPath !== undefined });
12579
12754
  const config = resolveConfig({}, manifest);
12580
12755
  const publishSettings = manifest.publish ?? {};
12581
- const providerConfig = publishSettings[provider.info.id] ?? {};
12756
+ const providerConfig = {
12757
+ ...publishSettings[provider.info.id] ?? {},
12758
+ ...(await deps.selections?.settings(options.projectDir))?.[provider.info.id] ?? {}
12759
+ };
12582
12760
  const bookAccount = typeof providerConfig.credential === "string" ? providerConfig.credential.trim() : "";
12583
12761
  const effectiveAccount = bookAccount || deps.credentialAccount;
12584
12762
  const effectiveDeps = effectiveAccount ? { ...deps, credentialAccount: effectiveAccount } : deps;
@@ -12754,11 +12932,20 @@ async function connectPublishProvider(options, deps) {
12754
12932
  label: account || info10.label,
12755
12933
  createdAt: Date.now()
12756
12934
  };
12757
- const req = await resolvePublishRequest({
12935
+ const shop = (options.shop ?? "").trim();
12936
+ if (!options.projectDir && info10.id === "shopify" && !shop) {
12937
+ throw new Error("Enter your Shopify store's address (like my-store.myshopify.com) to check the key.");
12938
+ }
12939
+ const req = options.projectDir ? await resolvePublishRequest({
12758
12940
  projectDir: options.projectDir,
12759
12941
  providerId: info10.id,
12760
12942
  ...options.manifestPath ? { manifestPath: options.manifestPath } : {}
12761
- }, deps);
12943
+ }, deps) : {
12944
+ project: { projectDir: "", title: "", authors: [] },
12945
+ config: shop ? { shop } : {},
12946
+ artifact: { path: "", format: info10.format },
12947
+ deps
12948
+ };
12762
12949
  const trialDeps = {
12763
12950
  ...deps,
12764
12951
  env: {},
@@ -12822,7 +13009,7 @@ class PublishSelectionsStore {
12822
13009
  resolve(providerId, projectDir) {
12823
13010
  return this.enqueue(async () => {
12824
13011
  const data = await this.read();
12825
- const proj = projectDir ? data.projects[normalizeProjectKey(projectDir)]?.[providerId] : undefined;
13012
+ const proj = projectDir ? data.projects[normalizeProjectKey(projectDir)]?.[providerId]?.credential : undefined;
12826
13013
  return proj && proj.trim() || data.global[providerId]?.trim() || undefined;
12827
13014
  });
12828
13015
  }
@@ -12830,7 +13017,7 @@ class PublishSelectionsStore {
12830
13017
  return this.enqueue(async () => {
12831
13018
  const data = await this.read();
12832
13019
  const global = data.global[providerId];
12833
- const project = projectDir ? data.projects[normalizeProjectKey(projectDir)]?.[providerId] : undefined;
13020
+ const project = projectDir ? data.projects[normalizeProjectKey(projectDir)]?.[providerId]?.credential : undefined;
12834
13021
  return {
12835
13022
  ...project ? { project } : {},
12836
13023
  ...global ? { global } : {}
@@ -12849,17 +13036,35 @@ class PublishSelectionsStore {
12849
13036
  });
12850
13037
  }
12851
13038
  setProject(projectDir, providerId, account) {
13039
+ return this.setSettings(projectDir, providerId, { credential: account });
13040
+ }
13041
+ settings(projectDir) {
13042
+ return this.enqueue(async () => {
13043
+ const data = await this.read();
13044
+ return data.projects[normalizeProjectKey(projectDir)] ?? {};
13045
+ });
13046
+ }
13047
+ setSettings(projectDir, providerId, values) {
12852
13048
  return this.enqueue(async () => {
12853
13049
  const data = await this.read();
12854
13050
  const key = normalizeProjectKey(projectDir);
12855
- const a = (account ?? "").trim();
12856
- const bucket = data.projects[key] ?? {};
12857
- if (a)
12858
- bucket[providerId] = a;
13051
+ const book = data.projects[key] ?? {};
13052
+ const mine = { ...book[providerId] };
13053
+ for (const [k, v] of Object.entries(values)) {
13054
+ const text = (v ?? "").trim();
13055
+ if (text)
13056
+ mine[k] = text;
13057
+ else
13058
+ delete mine[k];
13059
+ }
13060
+ if (JSON.stringify(mine) === JSON.stringify(book[providerId] ?? {}))
13061
+ return;
13062
+ if (Object.keys(mine).length > 0)
13063
+ book[providerId] = mine;
12859
13064
  else
12860
- delete bucket[providerId];
12861
- if (Object.keys(bucket).length > 0)
12862
- data.projects[key] = bucket;
13065
+ delete book[providerId];
13066
+ if (Object.keys(book).length > 0)
13067
+ data.projects[key] = book;
12863
13068
  else
12864
13069
  delete data.projects[key];
12865
13070
  await this.write(data);
@@ -12870,6 +13075,7 @@ function normalizeProjectKey(projectDir) {
12870
13075
  return projectDir.replace(/[\\/]+$/, "");
12871
13076
  }
12872
13077
  // src/lib/publish/manifest-publish.ts
13078
+ import path31 from "node:path";
12873
13079
  import { isMap as isMap4 } from "yaml";
12874
13080
  function settingsFromDoc(doc) {
12875
13081
  const node = doc.get("publish", true);
@@ -12884,9 +13090,13 @@ function settingsFromDoc(doc) {
12884
13090
  }
12885
13091
  return out;
12886
13092
  }
12887
- async function readPublishSettings(projectDir) {
13093
+ async function readPublishSettings(projectDir, selections) {
12888
13094
  const { doc } = await loadManifestDoc(projectDir);
12889
- return settingsFromDoc(doc);
13095
+ const settings = settingsFromDoc(doc);
13096
+ for (const [id, mine] of Object.entries(await selections?.settings(projectDir) ?? {})) {
13097
+ settings[id] = { ...settings[id], ...mine };
13098
+ }
13099
+ return settings;
12890
13100
  }
12891
13101
  async function setPublishProviderConfig(projectDir, providerId, values) {
12892
13102
  const { doc, file } = await loadManifestDoc(projectDir);
@@ -12908,4 +13118,35 @@ async function setPublishProviderConfig(projectDir, providerId, values) {
12908
13118
  await writeManifestDoc(file, doc);
12909
13119
  return settingsFromDoc(doc);
12910
13120
  }
12911
- 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 };
13121
+ function insideBook(projectDir, dir) {
13122
+ const rel = path31.relative(projectDir, path31.resolve(projectDir, dir));
13123
+ if (rel === ".." || rel.startsWith(`..${path31.sep}`) || path31.isAbsolute(rel))
13124
+ return null;
13125
+ return rel.split(path31.sep).join("/") || ".";
13126
+ }
13127
+ async function savePublishSettings(projectDir, providerId, values, selections) {
13128
+ const shared = {};
13129
+ const mine = {};
13130
+ for (const [key, value] of Object.entries(values)) {
13131
+ const text = value === undefined || value === null ? "" : String(value).trim();
13132
+ const isDir = providerId === "local" && key === "dir";
13133
+ const inside = isDir && text ? insideBook(projectDir, text) : null;
13134
+ if (inside !== null) {
13135
+ shared[key] = inside;
13136
+ mine[key] = "";
13137
+ } else if (isDir || key === "credential") {
13138
+ mine[key] = isDir && text ? path31.resolve(projectDir, text) : text;
13139
+ shared[key] = "";
13140
+ } else {
13141
+ shared[key] = value;
13142
+ }
13143
+ }
13144
+ if (Object.keys(mine).length > 0)
13145
+ await selections.setSettings(projectDir, providerId, mine);
13146
+ const inManifest = (await readPublishSettings(projectDir))[providerId] ?? {};
13147
+ const changes = Object.fromEntries(Object.entries(shared).filter(([key, value]) => value !== "" || (key in inManifest)));
13148
+ if (Object.keys(changes).length > 0)
13149
+ await setPublishProviderConfig(projectDir, providerId, changes);
13150
+ return readPublishSettings(projectDir, selections);
13151
+ }
13152
+ 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 };