gutterpress 0.11.18-alpha.8 → 0.11.18

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.
@@ -77,7 +77,7 @@ export { connectGenericHost, knownForgeTokenUrl, } from "../lib/remote-auth/gene
77
77
  export type { GenericTokenConnectInput, GenericAuthOptions, } from "../lib/remote-auth/generic-auth.ts";
78
78
  export { diagnoseProjectRemote, parseRemoteOrigin, forgeKindForHost, } from "../lib/remote-auth/diagnose.ts";
79
79
  export type { ProjectRemoteDiagnosis, DiagnoseProjectRemoteOptions, RemoteProtocol, RemoteGuidanceId, ForgeKind, } from "../lib/remote-auth/diagnose.ts";
80
- export { syncProject, refreshRemoteCopies, SYNC_SNAPSHOT_MESSAGE, } from "../lib/remote-auth/sync.ts";
80
+ export { syncProject, refreshRemoteCopies, checkRemoteUpdates, SYNC_SNAPSHOT_MESSAGE, } from "../lib/remote-auth/sync.ts";
81
81
  export { repairOnlineBackup, REPAIR_SNAPSHOT_MESSAGE, type RepairOnlineBackupOptions, type RepairOnlineBackupResult, } from "../lib/remote-auth/repair.ts";
82
82
  export { scorchedEarth, type ScorchedEarthOptions, type ScorchedEarthResult, } from "../lib/remote-auth/scorched-earth.ts";
83
83
  export type { SyncOutcome, SyncProjectOptions, KeptBothFile, } from "../lib/remote-auth/sync.ts";
package/dist/api/index.js CHANGED
@@ -37,6 +37,7 @@ import {
37
37
  autoSnapshotDelayMs,
38
38
  autoSyncDelayMs,
39
39
  checkExtensionUpdates,
40
+ checkRemoteUpdates,
40
41
  cloneRepository,
41
42
  connectGenericHost,
42
43
  connectGoogleDrive,
@@ -136,7 +137,7 @@ import {
136
137
  testRemoteAccess,
137
138
  updateExtensions,
138
139
  validateProjectExtensions
139
- } from "../index-hq85vady.js";
140
+ } from "../index-e1hbmb5a.js";
140
141
  import"../index-ht1bcqdd.js";
141
142
  import {
142
143
  BuildError
@@ -282,6 +283,7 @@ export {
282
283
  connectGoogleDrive,
283
284
  connectGenericHost,
284
285
  cloneRepository,
286
+ checkRemoteUpdates,
285
287
  checkExtensionUpdates,
286
288
  capabilitiesFor,
287
289
  autoSyncDelayMs,
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  runBuild,
3
3
  splitOutPath
4
- } from "./cli-x8bdpf00.js";
4
+ } from "./cli-6q7trt7z.js";
5
5
  import {
6
6
  log
7
- } from "./cli-ffq3sdfh.js";
7
+ } from "./cli-xe29z84b.js";
8
8
  import {
9
9
  UsageError,
10
10
  parseFormat,
11
11
  parsePdfxFlavor,
12
12
  rejectExtraPositionals,
13
13
  rejectUnknownFlags
14
- } from "./cli-6e07bq63.js";
14
+ } from "./cli-rg8jq04a.js";
15
15
  import"./cli-ht1bcqdd.js";
16
16
  import"./cli-ptez7zcc.js";
17
17
  import {
@@ -13,12 +13,12 @@ import {
13
13
  stampCreator,
14
14
  stripAnnotations,
15
15
  warn
16
- } from "./cli-ffq3sdfh.js";
16
+ } from "./cli-xe29z84b.js";
17
17
  import {
18
18
  DEBOUNCE,
19
19
  UsageError,
20
20
  resolvePort
21
- } from "./cli-6e07bq63.js";
21
+ } from "./cli-rg8jq04a.js";
22
22
  import {
23
23
  blackTextToGray
24
24
  } from "./cli-ht1bcqdd.js";
@@ -4760,124 +4760,6 @@ async function addBuiltInStyleSet(projectDir, id) {
4760
4760
  });
4761
4761
  }
4762
4762
 
4763
- // src/lib/extension-restore.ts
4764
- import path6 from "node:path";
4765
- var OFFLINE_REASON = "you appear to be offline (npm can't be reached)";
4766
- function pinnedEntries(manifest) {
4767
- const found = new Map;
4768
- for (const entry of Array.isArray(manifest.extensions) ? manifest.extensions : []) {
4769
- const object3 = typeof entry === "object" && entry !== null ? entry : null;
4770
- const use = typeof entry === "string" ? entry : object3?.use;
4771
- if (typeof use !== "string" || object3?.enabled === false)
4772
- continue;
4773
- let parsed;
4774
- try {
4775
- parsed = parseExtensionSpecifier(use);
4776
- } catch {
4777
- continue;
4778
- }
4779
- if (parsed.kind !== "npm" || !parsed.version || !isExactNpmVersion(parsed.version))
4780
- continue;
4781
- const spec = `${parsed.name}@${parsed.version}`;
4782
- if (found.has(spec))
4783
- continue;
4784
- found.set(spec, {
4785
- name: parsed.name,
4786
- version: parsed.version,
4787
- ...typeof object3?.export === "string" && object3.export ? { export: object3.export } : {}
4788
- });
4789
- }
4790
- return [...found.values()];
4791
- }
4792
- async function restoreAll(projectDir, manifestFile, pins, options) {
4793
- const result = { manifestFile, installed: [], failed: [], warnings: [], offline: false };
4794
- const missing = [];
4795
- for (const pin of pins)
4796
- if (await vendoredCopyNeedsInstall(projectDir, pin.name, pin.version))
4797
- missing.push(pin);
4798
- if (missing.length === 0)
4799
- return result;
4800
- const report = options.onProgress ?? (() => {});
4801
- const specs = missing.map((pin) => `${pin.name}@${pin.version}`);
4802
- report({ type: "start", specs });
4803
- for (const [index, pin] of missing.entries()) {
4804
- const spec = specs[index];
4805
- const step = { type: "package", spec, index, total: missing.length };
4806
- if (result.offline) {
4807
- result.failed.push({ use: spec, message: OFFLINE_REASON });
4808
- report({ ...step, state: "failed", message: OFFLINE_REASON });
4809
- continue;
4810
- }
4811
- report({ ...step, state: "downloading" });
4812
- try {
4813
- result.warnings.push(...await restoreNpmExtension(projectDir, pin.name, pin.version, pin.export, {
4814
- ...options,
4815
- keepVersions: pins.filter((other) => other.name === pin.name).map((other) => other.version)
4816
- }));
4817
- result.installed.push(spec);
4818
- report({ ...step, state: "done" });
4819
- } catch (error2) {
4820
- if (error2 instanceof FetchUnavailableError)
4821
- result.offline = true;
4822
- const message = result.offline ? OFFLINE_REASON : error2 instanceof Error ? error2.message : String(error2);
4823
- result.failed.push({ use: spec, message });
4824
- report({ ...step, state: "failed", message });
4825
- }
4826
- }
4827
- report({ type: "end", installed: result.installed, failed: result.failed });
4828
- return result;
4829
- }
4830
- var inFlight = new Map;
4831
- async function restorePinnedExtensions(projectDir, options = {}) {
4832
- let loaded;
4833
- try {
4834
- loaded = await loadManifestWithPath(options.manifestPath ?? projectDir);
4835
- } catch {
4836
- return { manifestFile: null, installed: [], failed: [], warnings: [], offline: false };
4837
- }
4838
- const { manifest, manifestDir, manifestPath } = loaded;
4839
- const pins = pinnedEntries(manifest);
4840
- if (manifestPath === null || pins.length === 0) {
4841
- return { manifestFile: manifestPath && path6.basename(manifestPath), installed: [], failed: [], warnings: [], offline: false };
4842
- }
4843
- const key = projectKey(manifestDir);
4844
- const running = inFlight.get(key);
4845
- if (running)
4846
- return running;
4847
- const run = restoreAll(manifestDir, path6.basename(manifestPath), pins, options).finally(() => {
4848
- inFlight.delete(key);
4849
- });
4850
- inFlight.set(key, run);
4851
- return run;
4852
- }
4853
- function restoreFailureMessage(failure, manifestFile) {
4854
- return `Could not download ${failure.use} (pinned in ${manifestFile ?? "the manifest"}): ${failure.message.replace(/[.\s]+$/, "")}. ` + `Check your internet connection and try again, or install it yourself with \`gutterpress ext add ${failure.use}\`.`;
4855
- }
4856
- function restoreOfflineMessage(failed, manifestFile) {
4857
- const list = failed.map((f) => f.use).join(", ");
4858
- return `You appear to be offline, and this book's extensions are not installed yet: ${list} ` + `(pinned in ${manifestFile ?? "the manifest"}). Connect to the internet and try again to install them; ` + `once installed, they work offline.`;
4859
- }
4860
- async function restoreForCommand(dir, options) {
4861
- const result = await restorePinnedExtensions(dir, { manifestPath: options.manifestPath });
4862
- const file = result.manifestFile ?? "the manifest";
4863
- for (const spec of result.installed)
4864
- console.error(`Downloaded ${spec} (pinned in ${file})`);
4865
- for (const warning of result.warnings)
4866
- log.warn(warning);
4867
- const offline = result.failed.filter((f) => result.offline && f.message === OFFLINE_REASON);
4868
- const problems = [
4869
- ...result.failed.filter((f) => !offline.includes(f)).map((f) => restoreFailureMessage(f, result.manifestFile)),
4870
- ...offline.length > 0 ? [restoreOfflineMessage(offline, result.manifestFile)] : []
4871
- ];
4872
- if (problems.length === 0)
4873
- return;
4874
- if (options.failFast)
4875
- throw new BuildError(problems.join(`
4876
- `), EXIT_CODES.PIPELINE);
4877
- for (const problem of problems)
4878
- log.warn(problem);
4879
- }
4880
-
4881
4763
  // src/checks/registry.ts
4882
4764
  var checks = new Map;
4883
4765
  function registerCheck(check) {
@@ -5021,17 +4903,17 @@ function evictLru() {
5021
4903
  destroyEntryAfterGrace(evicted);
5022
4904
  }
5023
4905
  }
5024
- async function loadPdf(path7) {
4906
+ async function loadPdf(path6) {
5025
4907
  let st;
5026
4908
  try {
5027
- st = await stat2(path7);
4909
+ st = await stat2(path6);
5028
4910
  } catch {
5029
4911
  return null;
5030
4912
  }
5031
- const hit = docCache.get(path7);
4913
+ const hit = docCache.get(path6);
5032
4914
  if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) {
5033
- docCache.delete(path7);
5034
- docCache.set(path7, hit);
4915
+ docCache.delete(path6);
4916
+ docCache.set(path6, hit);
5035
4917
  try {
5036
4918
  return await hit.doc;
5037
4919
  } catch {
@@ -5040,15 +4922,15 @@ async function loadPdf(path7) {
5040
4922
  }
5041
4923
  if (hit)
5042
4924
  destroyEntry(hit);
5043
- const docPromise = readFile7(path7).then((buf) => getDocumentProxy(new Uint8Array(buf)));
5044
- docCache.delete(path7);
5045
- docCache.set(path7, { mtimeMs: st.mtimeMs, size: st.size, doc: docPromise });
4925
+ const docPromise = readFile7(path6).then((buf) => getDocumentProxy(new Uint8Array(buf)));
4926
+ docCache.delete(path6);
4927
+ docCache.set(path6, { mtimeMs: st.mtimeMs, size: st.size, doc: docPromise });
5046
4928
  evictLru();
5047
4929
  try {
5048
4930
  return await docPromise;
5049
4931
  } catch {
5050
- if (docCache.get(path7)?.doc === docPromise)
5051
- docCache.delete(path7);
4932
+ if (docCache.get(path6)?.doc === docPromise)
4933
+ docCache.delete(path6);
5052
4934
  return null;
5053
4935
  }
5054
4936
  }
@@ -6749,7 +6631,7 @@ import { readFile as readFile13 } from "node:fs/promises";
6749
6631
  // src/lib/style-resolver.ts
6750
6632
  import { existsSync as existsSync8 } from "node:fs";
6751
6633
  import { readdir as readdir2 } from "node:fs/promises";
6752
- import path7 from "node:path";
6634
+ import path6 from "node:path";
6753
6635
  var STYLES_SUBDIR = "styles";
6754
6636
  var FALLBACK_PRIORITY = [
6755
6637
  "styles/book.css",
@@ -6759,7 +6641,7 @@ var FALLBACK_PRIORITY = [
6759
6641
  "css/main.css"
6760
6642
  ];
6761
6643
  function relDisplay(projectDir, absPath) {
6762
- return path7.relative(projectDir, absPath).split(path7.sep).join("/");
6644
+ return path6.relative(projectDir, absPath).split(path6.sep).join("/");
6763
6645
  }
6764
6646
  async function dirEntries(dir) {
6765
6647
  try {
@@ -6769,11 +6651,11 @@ async function dirEntries(dir) {
6769
6651
  }
6770
6652
  }
6771
6653
  function cssFilesFrom(dir, entries) {
6772
- return entries.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".css")).map((e) => path7.join(dir, e.name));
6654
+ return entries.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".css")).map((e) => path6.join(dir, e.name));
6773
6655
  }
6774
6656
  async function discoverCssFiles(projectDir) {
6775
- const stylesDir = path7.join(projectDir, STYLES_SUBDIR);
6776
- const cssDir = path7.join(projectDir, "css");
6657
+ const stylesDir = path6.join(projectDir, STYLES_SUBDIR);
6658
+ const cssDir = path6.join(projectDir, "css");
6777
6659
  const [rootEntries, stylesEntries, cssEntries] = await Promise.all([
6778
6660
  dirEntries(projectDir),
6779
6661
  dirEntries(stylesDir),
@@ -6796,7 +6678,7 @@ async function resolveActiveStyles(projectDir, manifestStyles) {
6796
6678
  if (manifest.length > 0)
6797
6679
  return manifest;
6798
6680
  for (const rel of FALLBACK_PRIORITY) {
6799
- if (existsSync8(path7.join(projectDir, rel)))
6681
+ if (existsSync8(path6.join(projectDir, rel)))
6800
6682
  return [rel];
6801
6683
  }
6802
6684
  const discovered = await discoverCssFiles(projectDir);
@@ -7011,7 +6893,7 @@ import { dirname as dirname3, resolve as resolve5 } from "node:path";
7011
6893
  // src/lib/asset-inline.ts
7012
6894
  import { createHash as createHash2 } from "node:crypto";
7013
6895
  import { readFile as readFile14 } from "node:fs/promises";
7014
- import path8 from "node:path";
6896
+ import path7 from "node:path";
7015
6897
  import postcss3 from "postcss";
7016
6898
  var FONT_EXTS = new Set([".woff2", ".woff", ".ttf", ".otf"]);
7017
6899
  var HASHED_ASSET_DIR = "assets";
@@ -7052,19 +6934,19 @@ function decodeRef(ref) {
7052
6934
  }
7053
6935
  }
7054
6936
  function toPosix(p) {
7055
- return p.split(path8.sep).join("/");
6937
+ return p.split(path7.sep).join("/");
7056
6938
  }
7057
6939
  function escapesProjectRoot(projectDir, absPath) {
7058
- const rel = path8.relative(projectDir, absPath);
7059
- return rel === ".." || rel.startsWith(`..${path8.sep}`) || path8.isAbsolute(rel);
6940
+ const rel = path7.relative(projectDir, absPath);
6941
+ return rel === ".." || rel.startsWith(`..${path7.sep}`) || path7.isAbsolute(rel);
7060
6942
  }
7061
6943
  function proseImageRefError(ref, projectDir) {
7062
6944
  const cleaned = stripUrlSuffix(decodeRef(ref));
7063
- if (/^file:/i.test(cleaned) || path8.isAbsolute(cleaned)) {
6945
+ if (/^file:/i.test(cleaned) || path7.isAbsolute(cleaned)) {
7064
6946
  return `Image reference must be relative to the book folder: ${ref}
7065
6947
  ` + ` Copy the file into your book folder and reference it from there.`;
7066
6948
  }
7067
- if (escapesProjectRoot(projectDir, path8.resolve(projectDir, cleaned))) {
6949
+ if (escapesProjectRoot(projectDir, path7.resolve(projectDir, cleaned))) {
7068
6950
  return `Image reference points outside the book folder: ${ref}
7069
6951
  ` + ` Copy the file into your book folder and reference it from there.`;
7070
6952
  }
@@ -7134,12 +7016,12 @@ function importTarget(atRule) {
7134
7016
  return target;
7135
7017
  }
7136
7018
  async function inlineOne(cssPath, copies, warnings, seen) {
7137
- const abs = path8.resolve(cssPath);
7019
+ const abs = path7.resolve(cssPath);
7138
7020
  if (seen.has(abs))
7139
7021
  return "";
7140
7022
  seen.add(abs);
7141
7023
  const source = await readOrThrow(abs, "stylesheet", "the book manifest");
7142
- const cssDir = path8.dirname(abs);
7024
+ const cssDir = path7.dirname(abs);
7143
7025
  let root;
7144
7026
  try {
7145
7027
  root = postcss3.parse(source.toString("utf-8"), { from: abs });
@@ -7161,8 +7043,8 @@ async function inlineOne(cssPath, copies, warnings, seen) {
7161
7043
  return null;
7162
7044
  }
7163
7045
  const relPath = stripUrlSuffix(decodeRef(raw));
7164
- const absAsset = path8.resolve(cssDir, relPath);
7165
- const ext = path8.extname(absAsset).toLowerCase();
7046
+ const absAsset = path7.resolve(cssDir, relPath);
7047
+ const ext = path7.extname(absAsset).toLowerCase();
7166
7048
  if (FONT_EXTS.has(ext)) {
7167
7049
  const bytes2 = await readOrThrow(absAsset, "font file", abs);
7168
7050
  return dataUri(bytes2, ext);
@@ -7180,7 +7062,7 @@ async function inlineOne(cssPath, copies, warnings, seen) {
7180
7062
  imports.push({ node, target });
7181
7063
  });
7182
7064
  for (const { node, target } of imports) {
7183
- const importedAbs = path8.resolve(cssDir, stripUrlSuffix(decodeRef(target)));
7065
+ const importedAbs = path7.resolve(cssDir, stripUrlSuffix(decodeRef(target)));
7184
7066
  const inlined = await inlineOne(importedAbs, copies, warnings, seen);
7185
7067
  node.replaceWith(wrapImportConditions(node, postcss3.parse(inlined, { from: importedAbs })));
7186
7068
  }
@@ -7192,10 +7074,10 @@ async function inlineStyles(projectDir, stylePaths) {
7192
7074
  const seen = new Set;
7193
7075
  const parts = [];
7194
7076
  for (const rel of stylePaths) {
7195
- const abs = path8.resolve(projectDir, rel);
7077
+ const abs = path7.resolve(projectDir, rel);
7196
7078
  const css = await inlineOne(abs, copies, warnings, seen);
7197
7079
  if (css.trim().length > 0) {
7198
- parts.push(`/* ${toPosix(path8.relative(projectDir, abs))} */
7080
+ parts.push(`/* ${toPosix(path7.relative(projectDir, abs))} */
7199
7081
  ${css.trim()}`);
7200
7082
  }
7201
7083
  }
@@ -7211,7 +7093,7 @@ async function collectStyleDependencies(projectDir, stylePaths) {
7211
7093
  const found = new Set;
7212
7094
  const visited = new Set;
7213
7095
  async function walk(cssPath) {
7214
- const abs = path8.resolve(cssPath);
7096
+ const abs = path7.resolve(cssPath);
7215
7097
  if (visited.has(abs))
7216
7098
  return;
7217
7099
  visited.add(abs);
@@ -7228,14 +7110,14 @@ async function collectStyleDependencies(projectDir, stylePaths) {
7228
7110
  } catch {
7229
7111
  return;
7230
7112
  }
7231
- const cssDir = path8.dirname(abs);
7113
+ const cssDir = path7.dirname(abs);
7232
7114
  root.walkDecls((decl) => {
7233
7115
  if (!decl.value.includes("url("))
7234
7116
  return;
7235
7117
  for (const raw of extractCssUrls(decl.value)) {
7236
7118
  if (!raw || isNonFilesystemRef(raw))
7237
7119
  continue;
7238
- found.add(path8.resolve(cssDir, stripUrlSuffix(decodeRef(raw))));
7120
+ found.add(path7.resolve(cssDir, stripUrlSuffix(decodeRef(raw))));
7239
7121
  }
7240
7122
  });
7241
7123
  const imports = [];
@@ -7245,11 +7127,11 @@ async function collectStyleDependencies(projectDir, stylePaths) {
7245
7127
  imports.push(target);
7246
7128
  });
7247
7129
  for (const target of imports) {
7248
- await walk(path8.resolve(cssDir, stripUrlSuffix(decodeRef(target))));
7130
+ await walk(path7.resolve(cssDir, stripUrlSuffix(decodeRef(target))));
7249
7131
  }
7250
7132
  }
7251
7133
  for (const rel of stylePaths) {
7252
- await walk(path8.resolve(projectDir, rel));
7134
+ await walk(path7.resolve(projectDir, rel));
7253
7135
  }
7254
7136
  return [...found];
7255
7137
  }
@@ -7266,8 +7148,8 @@ async function planImageCopies(projectDir, refs) {
7266
7148
  continue;
7267
7149
  }
7268
7150
  const cleaned = stripUrlSuffix(decodeRef(ref));
7269
- const abs = path8.resolve(projectDir, cleaned);
7270
- const rel = path8.relative(projectDir, abs);
7151
+ const abs = path7.resolve(projectDir, cleaned);
7152
+ const rel = path7.relative(projectDir, abs);
7271
7153
  const dest = toPosix(rel);
7272
7154
  copies.set(dest, { from: abs, to: dest });
7273
7155
  destinations.set(ref, dest);
@@ -7288,9 +7170,9 @@ async function inlineShapeUrls(html, baseDir) {
7288
7170
  if (!raw || isNonFilesystemRef(raw))
7289
7171
  continue;
7290
7172
  const cleaned = stripUrlSuffix(decodeRef(raw));
7291
- if (path8.isAbsolute(cleaned))
7173
+ if (path7.isAbsolute(cleaned))
7292
7174
  continue;
7293
- const abs = path8.resolve(baseDir, cleaned);
7175
+ const abs = path7.resolve(baseDir, cleaned);
7294
7176
  if (escapesProjectRoot(baseDir, abs))
7295
7177
  continue;
7296
7178
  let bytes;
@@ -7299,7 +7181,7 @@ async function inlineShapeUrls(html, baseDir) {
7299
7181
  } catch {
7300
7182
  continue;
7301
7183
  }
7302
- const mime = mimeFor(path8.extname(abs).toLowerCase());
7184
+ const mime = mimeFor(path7.extname(abs).toLowerCase());
7303
7185
  const dataUri2 = `data:${mime};base64,${bytes.toString("base64")}`;
7304
7186
  replacements.set(m[0], `--gp-shape:url("${dataUri2}")`);
7305
7187
  }
@@ -7444,7 +7326,7 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
7444
7326
  registerCheck(check22);
7445
7327
 
7446
7328
  // src/lib/build-staging.ts
7447
- import path9 from "node:path";
7329
+ import path8 from "node:path";
7448
7330
  import os from "node:os";
7449
7331
  import fsp from "node:fs/promises";
7450
7332
 
@@ -7629,8 +7511,8 @@ function placeholderPng(width = 640, height = 480, cell = 32) {
7629
7511
 
7630
7512
  // src/lib/build-staging.ts
7631
7513
  async function shipViewerHtml(htmlFile, outDir) {
7632
- await fsp.mkdir(path9.join(outDir, "engine"), { recursive: true });
7633
- await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"), path9.join(outDir, "engine/gutterpress-viewer.js"));
7514
+ await fsp.mkdir(path8.join(outDir, "engine"), { recursive: true });
7515
+ await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"), path8.join(outDir, "engine/gutterpress-viewer.js"));
7634
7516
  const tag = ` <script src="engine/gutterpress-viewer.js"></script>
7635
7517
  `;
7636
7518
  const html = await fsp.readFile(htmlFile, "utf-8");
@@ -7662,18 +7544,18 @@ async function stageBookAssets(options) {
7662
7544
  return { missing: [...missingPlaceholders.keys()].sort() };
7663
7545
  }
7664
7546
  async function copyReferencedAssets(copies, outDir) {
7665
- const dirs = new Set(copies.map((c) => path9.dirname(path9.resolve(outDir, c.to))));
7547
+ const dirs = new Set(copies.map((c) => path8.dirname(path8.resolve(outDir, c.to))));
7666
7548
  await Promise.all([...dirs].map((d) => fsp.mkdir(d, { recursive: true })));
7667
7549
  const missing = new Map;
7668
7550
  await Promise.all(copies.map(async (c) => {
7669
- const dest = path9.resolve(outDir, c.to);
7551
+ const dest = path8.resolve(outDir, c.to);
7670
7552
  try {
7671
7553
  await fsp.copyFile(c.from, dest);
7672
7554
  } catch (err) {
7673
7555
  if (err?.code === "ENOENT") {
7674
7556
  const placeholder = placeholderOutputPath(c.to);
7675
- const placeholderDest = path9.resolve(outDir, placeholder);
7676
- await fsp.mkdir(path9.dirname(placeholderDest), { recursive: true });
7557
+ const placeholderDest = path8.resolve(outDir, placeholder);
7558
+ await fsp.mkdir(path8.dirname(placeholderDest), { recursive: true });
7677
7559
  await fsp.writeFile(placeholderDest, placeholderPng());
7678
7560
  missing.set(c.to, placeholder);
7679
7561
  return;
@@ -7703,7 +7585,7 @@ function dropRelativeLinkHrefs(html) {
7703
7585
  });
7704
7586
  }
7705
7587
  async function createStageRoot() {
7706
- return fsp.mkdtemp(path9.join(os.tmpdir(), "gutterpress-stage-"));
7588
+ return fsp.mkdtemp(path8.join(os.tmpdir(), "gutterpress-stage-"));
7707
7589
  }
7708
7590
 
7709
7591
  // src/checks/source/dangling-links.ts
@@ -8015,7 +7897,7 @@ registerCheck(check26);
8015
7897
 
8016
7898
  // src/checks/source/merge-markers.ts
8017
7899
  import { readFile as readFile19 } from "node:fs/promises";
8018
- import path10 from "node:path";
7900
+ import path9 from "node:path";
8019
7901
 
8020
7902
  // src/checks/asset/extensions.ts
8021
7903
  var RASTER_INSPECTABLE_EXTS = [
@@ -8099,7 +7981,7 @@ var check27 = {
8099
7981
  ignore: [...ASSET_SCAN_IGNORE_GLOBS]
8100
7982
  });
8101
7983
  for (const sibling of siblings.sort()) {
8102
- const rel = path10.relative(ctx.inputDir, sibling).split(path10.sep).join("/");
7984
+ const rel = path9.relative(ctx.inputDir, sibling).split(path9.sep).join("/");
8103
7985
  results.push(finding(check27.id, {
8104
7986
  severity: "warning",
8105
7987
  code: "kept-both-versions",
@@ -8122,24 +8004,24 @@ import { stat as stat4 } from "node:fs/promises";
8122
8004
  import { stat as stat3, readFile as readFile20 } from "node:fs/promises";
8123
8005
  var DEFAULT_DPI = 72;
8124
8006
  var cache = new Map;
8125
- async function inspectImage(path11) {
8007
+ async function inspectImage(path10) {
8126
8008
  let st;
8127
8009
  try {
8128
- st = await stat3(path11);
8010
+ st = await stat3(path10);
8129
8011
  } catch {
8130
8012
  return null;
8131
8013
  }
8132
- const hit = cache.get(path11);
8014
+ const hit = cache.get(path10);
8133
8015
  if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
8134
8016
  return hit.info;
8135
8017
  let info2 = null;
8136
8018
  try {
8137
- const buf = await readFile20(path11);
8019
+ const buf = await readFile20(path10);
8138
8020
  info2 = parseImage(buf);
8139
8021
  } catch {
8140
8022
  info2 = null;
8141
8023
  }
8142
- cache.set(path11, { mtimeMs: st.mtimeMs, size: st.size, info: info2 });
8024
+ cache.set(path10, { mtimeMs: st.mtimeMs, size: st.size, info: info2 });
8143
8025
  return info2;
8144
8026
  }
8145
8027
  function parseImage(b) {
@@ -8880,14 +8762,14 @@ import { existsSync as existsSync11 } from "node:fs";
8880
8762
  import { dirname as dirname4, join as join3, resolve as resolve7 } from "node:path";
8881
8763
 
8882
8764
  // src/lib/output-paths.ts
8883
- import path11 from "node:path";
8765
+ import path10 from "node:path";
8884
8766
  var DIST_DIRNAME = "dist";
8885
8767
  var BOOK_HTML = "book.html";
8886
8768
  function bookSlug(title) {
8887
8769
  return slugify(title ?? "", "book");
8888
8770
  }
8889
8771
  function resolveOutputDir(manifestDir, title) {
8890
- return path11.resolve(manifestDir, DIST_DIRNAME, bookSlug(title));
8772
+ return path10.resolve(manifestDir, DIST_DIRNAME, bookSlug(title));
8891
8773
  }
8892
8774
  function artifactName(title, format) {
8893
8775
  return `${bookSlug(title)}-${format}.pdf`;
@@ -8896,6 +8778,124 @@ function artifactName(title, format) {
8896
8778
  // src/lib/desktop.ts
8897
8779
  var BOOK_HTML_FILENAME = "book.html";
8898
8780
 
8781
+ // src/lib/extension-restore.ts
8782
+ import path11 from "node:path";
8783
+ var OFFLINE_REASON = "you appear to be offline (npm can't be reached)";
8784
+ function pinnedEntries(manifest) {
8785
+ const found = new Map;
8786
+ for (const entry of Array.isArray(manifest.extensions) ? manifest.extensions : []) {
8787
+ const object3 = typeof entry === "object" && entry !== null ? entry : null;
8788
+ const use = typeof entry === "string" ? entry : object3?.use;
8789
+ if (typeof use !== "string" || object3?.enabled === false)
8790
+ continue;
8791
+ let parsed;
8792
+ try {
8793
+ parsed = parseExtensionSpecifier(use);
8794
+ } catch {
8795
+ continue;
8796
+ }
8797
+ if (parsed.kind !== "npm" || !parsed.version || !isExactNpmVersion(parsed.version))
8798
+ continue;
8799
+ const spec = `${parsed.name}@${parsed.version}`;
8800
+ if (found.has(spec))
8801
+ continue;
8802
+ found.set(spec, {
8803
+ name: parsed.name,
8804
+ version: parsed.version,
8805
+ ...typeof object3?.export === "string" && object3.export ? { export: object3.export } : {}
8806
+ });
8807
+ }
8808
+ return [...found.values()];
8809
+ }
8810
+ async function restoreAll(projectDir, manifestFile, pins, options) {
8811
+ const result = { manifestFile, installed: [], failed: [], warnings: [], offline: false };
8812
+ const missing = [];
8813
+ for (const pin of pins)
8814
+ if (await vendoredCopyNeedsInstall(projectDir, pin.name, pin.version))
8815
+ missing.push(pin);
8816
+ if (missing.length === 0)
8817
+ return result;
8818
+ const report = options.onProgress ?? (() => {});
8819
+ const specs = missing.map((pin) => `${pin.name}@${pin.version}`);
8820
+ report({ type: "start", specs });
8821
+ for (const [index, pin] of missing.entries()) {
8822
+ const spec = specs[index];
8823
+ const step = { type: "package", spec, index, total: missing.length };
8824
+ if (result.offline) {
8825
+ result.failed.push({ use: spec, message: OFFLINE_REASON });
8826
+ report({ ...step, state: "failed", message: OFFLINE_REASON });
8827
+ continue;
8828
+ }
8829
+ report({ ...step, state: "downloading" });
8830
+ try {
8831
+ result.warnings.push(...await restoreNpmExtension(projectDir, pin.name, pin.version, pin.export, {
8832
+ ...options,
8833
+ keepVersions: pins.filter((other) => other.name === pin.name).map((other) => other.version)
8834
+ }));
8835
+ result.installed.push(spec);
8836
+ report({ ...step, state: "done" });
8837
+ } catch (error2) {
8838
+ if (error2 instanceof FetchUnavailableError)
8839
+ result.offline = true;
8840
+ const message = result.offline ? OFFLINE_REASON : error2 instanceof Error ? error2.message : String(error2);
8841
+ result.failed.push({ use: spec, message });
8842
+ report({ ...step, state: "failed", message });
8843
+ }
8844
+ }
8845
+ report({ type: "end", installed: result.installed, failed: result.failed });
8846
+ return result;
8847
+ }
8848
+ var inFlight = new Map;
8849
+ async function restorePinnedExtensions(projectDir, options = {}) {
8850
+ let loaded;
8851
+ try {
8852
+ loaded = await loadManifestWithPath(options.manifestPath ?? projectDir);
8853
+ } catch {
8854
+ return { manifestFile: null, installed: [], failed: [], warnings: [], offline: false };
8855
+ }
8856
+ const { manifest, manifestDir, manifestPath } = loaded;
8857
+ const pins = pinnedEntries(manifest);
8858
+ if (manifestPath === null || pins.length === 0) {
8859
+ return { manifestFile: manifestPath && path11.basename(manifestPath), installed: [], failed: [], warnings: [], offline: false };
8860
+ }
8861
+ const key = projectKey(manifestDir);
8862
+ const running = inFlight.get(key);
8863
+ if (running)
8864
+ return running;
8865
+ const run = restoreAll(manifestDir, path11.basename(manifestPath), pins, options).finally(() => {
8866
+ inFlight.delete(key);
8867
+ });
8868
+ inFlight.set(key, run);
8869
+ return run;
8870
+ }
8871
+ function restoreFailureMessage(failure, manifestFile) {
8872
+ return `Could not download ${failure.use} (pinned in ${manifestFile ?? "the manifest"}): ${failure.message.replace(/[.\s]+$/, "")}. ` + `Check your internet connection and try again, or install it yourself with \`gutterpress ext add ${failure.use}\`.`;
8873
+ }
8874
+ function restoreOfflineMessage(failed, manifestFile) {
8875
+ const list = failed.map((f) => f.use).join(", ");
8876
+ return `You appear to be offline, and this book's extensions are not installed yet: ${list} ` + `(pinned in ${manifestFile ?? "the manifest"}). Connect to the internet and try again to install them; ` + `once installed, they work offline.`;
8877
+ }
8878
+ async function restoreForCommand(dir, options) {
8879
+ const result = await restorePinnedExtensions(dir, { manifestPath: options.manifestPath });
8880
+ const file = result.manifestFile ?? "the manifest";
8881
+ for (const spec of result.installed)
8882
+ console.error(`Downloaded ${spec} (pinned in ${file})`);
8883
+ for (const warning of result.warnings)
8884
+ log.warn(warning);
8885
+ const offline = result.failed.filter((f) => result.offline && f.message === OFFLINE_REASON);
8886
+ const problems = [
8887
+ ...result.failed.filter((f) => !offline.includes(f)).map((f) => restoreFailureMessage(f, result.manifestFile)),
8888
+ ...offline.length > 0 ? [restoreOfflineMessage(offline, result.manifestFile)] : []
8889
+ ];
8890
+ if (problems.length === 0)
8891
+ return;
8892
+ if (options.failFast)
8893
+ throw new BuildError(problems.join(`
8894
+ `), EXIT_CODES.PIPELINE);
8895
+ for (const problem of problems)
8896
+ log.warn(problem);
8897
+ }
8898
+
8899
8899
  // src/lib/markdown/index.ts
8900
8900
  import { readdir as readdir3, readFile as readFile22, writeFile as writeFile2, mkdir as mkdir4 } from "node:fs/promises";
8901
8901
 
@@ -10315,6 +10315,7 @@ var EMPTY_BOOK_HTML = `<!doctype html>
10315
10315
  </html>
10316
10316
  `;
10317
10317
  async function renderPreviewBook(inputPath, config, opts) {
10318
+ await restoreForCommand(inputPath, { failFast: false });
10318
10319
  const { plugins, pluginStyles } = await loadPluginsWithCss(config.extensions, inputPath, (ref, err) => warn(`Skipping plugin "${ref}" in preview — ${err.message}`));
10319
10320
  return renderChapters(inputPath, {
10320
10321
  title: config.title ?? "Document",
@@ -14181,4 +14182,4 @@ function normalizeProjectKey(projectDir) {
14181
14182
  }
14182
14183
  // src/lib/publish/manifest-publish.ts
14183
14184
  import { isMap as isMap4 } from "yaml";
14184
- export { isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, loadManifestWithPath, BUILT_IN_STYLE_SET_IDS, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, addBuiltInStyleSet, restoreForCommand, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listPublishProviders, publishProviderFor, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential };
14185
+ export { isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, loadManifestWithPath, BUILT_IN_STYLE_SET_IDS, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, addBuiltInStyleSet, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listPublishProviders, publishProviderFor, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential };
@@ -4,7 +4,7 @@ import {
4
4
  // package.json
5
5
  var package_default = {
6
6
  name: "gutterpress",
7
- version: "0.11.18-alpha.8",
7
+ version: "0.11.18",
8
8
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
9
9
  author: "itlackey",
10
10
  license: "MPL-2.0",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "./cli-6e07bq63.js";
3
+ } from "./cli-rg8jq04a.js";
4
4
  import {
5
5
  execCapture,
6
6
  findTool,
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  UsageError,
7
7
  package_default,
8
8
  rejectUnknownFlags
9
- } from "./cli-6e07bq63.js";
9
+ } from "./cli-rg8jq04a.js";
10
10
  import {
11
11
  EXIT_CODES
12
12
  } from "./cli-46ycxe6r.js";
@@ -18,14 +18,14 @@ import {
18
18
  import { defineCommand, parseArgs, runMain } from "citty";
19
19
  import { statSync } from "node:fs";
20
20
  var SUBCOMMANDS = {
21
- new: () => import("./new-vwvxqm52.js").then((m) => m.default),
22
- preview: () => import("./preview-ramgpx1p.js").then((m) => m.default),
23
- build: () => import("./build-avpzp4d1.js").then((m) => m.default),
24
- publish: () => import("./publish-fv4qe0gp.js").then((m) => m.default),
25
- validate: () => import("./validate-c59d41r6.js").then((m) => m.default),
26
- preflight: () => import("./preflight-g459gt04.js").then((m) => m.default),
27
- doctor: () => import("./doctor-5ew91mgq.js").then((m) => m.default),
28
- ext: () => import("./ext-02x1bpd8.js").then((m) => m.default)
21
+ new: () => import("./new-rnnavkc8.js").then((m) => m.default),
22
+ preview: () => import("./preview-f9csmejj.js").then((m) => m.default),
23
+ build: () => import("./build-67bj810g.js").then((m) => m.default),
24
+ publish: () => import("./publish-40yvnb62.js").then((m) => m.default),
25
+ validate: () => import("./validate-pt921n11.js").then((m) => m.default),
26
+ preflight: () => import("./preflight-4769yg8n.js").then((m) => m.default),
27
+ doctor: () => import("./doctor-gcqb6xtj.js").then((m) => m.default),
28
+ ext: () => import("./ext-pkaghr3f.js").then((m) => m.default)
29
29
  };
30
30
  var VERSION = package_default.version;
31
31
  var main = defineCommand({
@@ -50,7 +50,7 @@ async function preflightRequiredInvocations(rawArgs) {
50
50
  const [command, ...commandArgs] = rawArgs;
51
51
  try {
52
52
  if (command === "new") {
53
- const { newArgs } = await import("./new-vwvxqm52.js");
53
+ const { newArgs } = await import("./new-rnnavkc8.js");
54
54
  rejectUnknownFlags(commandArgs, newArgs, "new");
55
55
  const parsed = parseArgs(commandArgs, {
56
56
  ...newArgs,
@@ -62,7 +62,7 @@ async function preflightRequiredInvocations(rawArgs) {
62
62
  return;
63
63
  }
64
64
  if (command === "preflight") {
65
- const { preflightArgs } = await import("./preflight-g459gt04.js");
65
+ const { preflightArgs } = await import("./preflight-4769yg8n.js");
66
66
  rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
67
67
  const parsed = parseArgs(commandArgs, {
68
68
  ...preflightArgs,
@@ -82,7 +82,7 @@ async function preflightRequiredInvocations(rawArgs) {
82
82
  }
83
83
  if (subcommand.startsWith("-"))
84
84
  return;
85
- const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-02x1bpd8.js");
85
+ const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-pkaghr3f.js");
86
86
  if (!EXT_SUBCOMMANDS.includes(subcommand)) {
87
87
  throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
88
88
  }
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getSystemDiagnostics,
3
3
  log
4
- } from "./cli-ffq3sdfh.js";
4
+ } from "./cli-xe29z84b.js";
5
5
  import {
6
6
  UsageError,
7
7
  rejectExtraPositionals,
8
8
  rejectUnknownFlags
9
- } from "./cli-6e07bq63.js";
9
+ } from "./cli-rg8jq04a.js";
10
10
  import"./cli-46ycxe6r.js";
11
11
  import"./cli-7fkhgtfn.js";
12
12
  import"./cli-37x76zdn.js";
@@ -12,15 +12,15 @@ import {
12
12
  searchNpmExtensions,
13
13
  setExtensionEnabled,
14
14
  updateExtensions
15
- } from "./cli-x8bdpf00.js";
16
- import"./cli-ffq3sdfh.js";
15
+ } from "./cli-6q7trt7z.js";
16
+ import"./cli-xe29z84b.js";
17
17
  import {
18
18
  UsageError,
19
19
  exitForUsage,
20
20
  rejectExtraPositionals,
21
21
  rejectUnknownFlags,
22
22
  resolveProjectDir
23
- } from "./cli-6e07bq63.js";
23
+ } from "./cli-rg8jq04a.js";
24
24
  import"./cli-ht1bcqdd.js";
25
25
  import"./cli-ptez7zcc.js";
26
26
  import {
@@ -5916,7 +5916,7 @@ import git from "isomorphic-git";
5916
5916
  // package.json
5917
5917
  var package_default = {
5918
5918
  name: "gutterpress",
5919
- version: "0.11.18-alpha.8",
5919
+ version: "0.11.18",
5920
5920
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5921
5921
  author: "itlackey",
5922
5922
  license: "MPL-2.0",
@@ -10866,6 +10866,7 @@ var EMPTY_BOOK_HTML = `<!doctype html>
10866
10866
  </html>
10867
10867
  `;
10868
10868
  async function renderPreviewBook(inputPath, config, opts) {
10869
+ await restoreForCommand(inputPath, { failFast: false });
10869
10870
  const { plugins, pluginStyles } = await loadPluginsWithCss(config.extensions, inputPath, (ref, err) => warn2(`Skipping plugin "${ref}" in preview — ${err.message}`));
10870
10871
  return renderChapters(inputPath, {
10871
10872
  title: config.title ?? "Document",
@@ -15083,6 +15084,47 @@ async function refreshRemoteCopies(options) {
15083
15084
  return { refreshed: false, reason: "offline" };
15084
15085
  }
15085
15086
  }
15087
+ async function checkRemoteUpdates(options) {
15088
+ const http3 = options.httpClient ?? defaultGitHttp;
15089
+ try {
15090
+ const dir = await repoDirFor(options.projectDir);
15091
+ const transport = await resolveTransport(dir, {
15092
+ ...options.credential ? { credential: options.credential } : {},
15093
+ ...options.tokenStore ? { tokenStore: options.tokenStore } : {}
15094
+ });
15095
+ const branch = await currentBranchOrThrow(dir);
15096
+ const refs = await git4.listServerRefs({
15097
+ http: http3,
15098
+ url: transport.url,
15099
+ prefix: `refs/heads/${branch}`,
15100
+ ...onAuthFor(transport.credential),
15101
+ ...transport.credential ? { onAuthFailure: () => ({ cancel: true }) } : {}
15102
+ });
15103
+ const remoteTip = refs.find((r) => r.ref === `refs/heads/${branch}`)?.oid;
15104
+ if (!remoteTip)
15105
+ return { available: false };
15106
+ const known = async (ref) => {
15107
+ try {
15108
+ return await git4.resolveRef({ fs: gitFs, dir, ref });
15109
+ } catch {
15110
+ return null;
15111
+ }
15112
+ };
15113
+ const tracking = await known(`refs/remotes/${transport.remote}/${branch}`);
15114
+ const local = await known(branch);
15115
+ return { available: remoteTip !== tracking && remoteTip !== local };
15116
+ } catch (e) {
15117
+ const message = e?.message ?? "";
15118
+ if (message === MSG_NO_REMOTE || message === MSG_SSH_REMOTE) {
15119
+ return { available: false, reason: "no-remote" };
15120
+ }
15121
+ const status = e?.data?.statusCode;
15122
+ if (status === 401 || status === 403 || status === 404) {
15123
+ return { available: false, reason: "auth" };
15124
+ }
15125
+ return { available: false, reason: "offline" };
15126
+ }
15127
+ }
15086
15128
 
15087
15129
  // src/lib/remote-auth/online-backup.ts
15088
15130
  var API_BASE3 = "https://api.github.com";
@@ -17360,4 +17402,4 @@ async function setPublishProviderConfig(projectDir, providerId, values) {
17360
17402
  await writeManifestDoc(file, doc);
17361
17403
  return settingsFromDoc(doc);
17362
17404
  }
17363
- export { BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, SYNC_SNAPSHOT_MESSAGE, log, resolveActiveStyles, listProjectStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, DEFAULT_NPM_REGISTRY, npmRegistryUrl, ensureProjectGitignore, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listNpmVersions, checkExtensionUpdates, updateExtensions, listBuiltInStyleSets, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, restorePinnedExtensions, restoreFailureMessage, 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, 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, isValidRepositoryName, setUpOnlineBackup, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, REPAIR_SNAPSHOT_MESSAGE, repairOnlineBackup, scorchedEarth, listPublishProviders, publishProviderFor, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig };
17405
+ export { BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, SYNC_SNAPSHOT_MESSAGE, log, resolveActiveStyles, listProjectStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, DEFAULT_NPM_REGISTRY, npmRegistryUrl, ensureProjectGitignore, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listNpmVersions, checkExtensionUpdates, updateExtensions, listBuiltInStyleSets, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, restorePinnedExtensions, restoreFailureMessage, 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, 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, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig };
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  autoSyncDelayMs,
39
39
  checkCss,
40
40
  checkExtensionUpdates,
41
+ checkRemoteUpdates,
41
42
  checkToolAvailability,
42
43
  cloneRepository,
43
44
  connectGenericHost,
@@ -153,7 +154,7 @@ import {
153
154
  testRemoteAccess,
154
155
  updateExtensions,
155
156
  validateProjectExtensions
156
- } from "./index-hq85vady.js";
157
+ } from "./index-e1hbmb5a.js";
157
158
  import"./index-ht1bcqdd.js";
158
159
  import {
159
160
  BUILD_DIAGNOSTIC_CODES
@@ -318,6 +319,7 @@ export {
318
319
  connectGenericHost,
319
320
  cloneRepository,
320
321
  checkToolAvailability,
322
+ checkRemoteUpdates,
321
323
  checkExtensionUpdates,
322
324
  checkCss,
323
325
  capabilitiesFor,
@@ -61,3 +61,24 @@ export declare function refreshRemoteCopies(options: {
61
61
  refreshed: boolean;
62
62
  reason?: "no-remote" | "auth" | "offline";
63
63
  }>;
64
+ /**
65
+ * Has the book's branch moved online since this copy last heard from it?
66
+ * Reads only the server's advertised refs (`listServerRefs`: no objects
67
+ * downloaded, no history walked, nothing written), so it is cheap enough to
68
+ * poll and safe on a dirty tree. The branch's online tip is compared with the
69
+ * remote-tracking ref (the last tip this copy fetched or pushed) and with the
70
+ * local tip: if it matches neither, someone else sent changes the author has
71
+ * not got. A local copy ahead of the remote is not "updates available".
72
+ *
73
+ * Best-effort like refreshRemoteCopies: it never throws; it reports why it
74
+ * could not tell.
75
+ */
76
+ export declare function checkRemoteUpdates(options: {
77
+ projectDir: string;
78
+ credential?: HostCredential;
79
+ tokenStore?: TokenStore;
80
+ httpClient?: typeof defaultGitHttp;
81
+ }): Promise<{
82
+ available: boolean;
83
+ reason?: "no-remote" | "auth" | "offline";
84
+ }>;
@@ -7,15 +7,15 @@ import {
7
7
  TARGET_IDS,
8
8
  scaffoldExtension,
9
9
  scaffoldProject
10
- } from "./cli-x8bdpf00.js";
10
+ } from "./cli-6q7trt7z.js";
11
11
  import {
12
12
  resolveGhostscript
13
- } from "./cli-ffq3sdfh.js";
13
+ } from "./cli-xe29z84b.js";
14
14
  import {
15
15
  UsageError,
16
16
  rejectExtraPositionals,
17
17
  rejectUnknownFlags
18
- } from "./cli-6e07bq63.js";
18
+ } from "./cli-rg8jq04a.js";
19
19
  import"./cli-ht1bcqdd.js";
20
20
  import"./cli-ptez7zcc.js";
21
21
  import {
@@ -2,15 +2,15 @@ import {
2
2
  executeValidation,
3
3
  publishTargetFor,
4
4
  reportMissingTools
5
- } from "./cli-x8bdpf00.js";
5
+ } from "./cli-6q7trt7z.js";
6
6
  import {
7
7
  log
8
- } from "./cli-ffq3sdfh.js";
8
+ } from "./cli-xe29z84b.js";
9
9
  import {
10
10
  UsageError,
11
11
  rejectExtraPositionals,
12
12
  rejectUnknownFlags
13
- } from "./cli-6e07bq63.js";
13
+ } from "./cli-rg8jq04a.js";
14
14
  import"./cli-ht1bcqdd.js";
15
15
  import"./cli-ptez7zcc.js";
16
16
  import {
@@ -3,14 +3,13 @@ import {
3
3
  } from "./cli-75qkn0k5.js";
4
4
  import {
5
5
  openPath,
6
- restoreForCommand,
7
6
  runBuild,
8
7
  splitOutPath,
9
8
  startPreviewServer
10
- } from "./cli-x8bdpf00.js";
9
+ } from "./cli-6q7trt7z.js";
11
10
  import {
12
11
  log
13
- } from "./cli-ffq3sdfh.js";
12
+ } from "./cli-xe29z84b.js";
14
13
  import {
15
14
  UsageError,
16
15
  parseFormat,
@@ -18,7 +17,7 @@ import {
18
17
  rejectExtraPositionals,
19
18
  rejectUnknownFlags,
20
19
  resolvePort
21
- } from "./cli-6e07bq63.js";
20
+ } from "./cli-rg8jq04a.js";
22
21
  import"./cli-ht1bcqdd.js";
23
22
  import"./cli-ptez7zcc.js";
24
23
  import {
@@ -54,7 +53,6 @@ var preview_default = defineCommand({
54
53
  if (typeof args.manifest === "string") {
55
54
  throw new UsageError("--manifest is only supported by preview --format pdf or pdfx; live HTML preview discovers the book manifest from its input directory.");
56
55
  }
57
- await restoreForCommand(inputPath ?? process.cwd(), { failFast: false });
58
56
  await startPreviewServer({
59
57
  input: inputPath,
60
58
  port: resolvePort(args.port),
@@ -11,16 +11,16 @@ import {
11
11
  publishProviderFor,
12
12
  resolvePublishFormat,
13
13
  runPublish
14
- } from "./cli-x8bdpf00.js";
14
+ } from "./cli-6q7trt7z.js";
15
15
  import {
16
16
  FileTokenStore,
17
17
  log
18
- } from "./cli-ffq3sdfh.js";
18
+ } from "./cli-xe29z84b.js";
19
19
  import {
20
20
  UsageError,
21
21
  rejectExtraPositionals,
22
22
  rejectUnknownFlags
23
- } from "./cli-6e07bq63.js";
23
+ } from "./cli-rg8jq04a.js";
24
24
  import"./cli-ht1bcqdd.js";
25
25
  import"./cli-ptez7zcc.js";
26
26
  import {
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  executeAndReport
3
- } from "./cli-x8bdpf00.js";
3
+ } from "./cli-6q7trt7z.js";
4
4
  import {
5
5
  log
6
- } from "./cli-ffq3sdfh.js";
6
+ } from "./cli-xe29z84b.js";
7
7
  import {
8
8
  UsageError,
9
9
  rejectExtraPositionals,
10
10
  rejectUnknownFlags
11
- } from "./cli-6e07bq63.js";
11
+ } from "./cli-rg8jq04a.js";
12
12
  import"./cli-ht1bcqdd.js";
13
13
  import"./cli-ptez7zcc.js";
14
14
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gutterpress",
3
- "version": "0.11.18-alpha.8",
3
+ "version": "0.11.18",
4
4
  "description": "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5
5
  "author": "itlackey",
6
6
  "license": "MPL-2.0",