gutterpress 0.10.0 → 0.10.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -16
- package/dist/api/index.d.ts +10 -19
- package/dist/api/index.js +8 -64
- package/dist/{audit-cvarpa72.js → audit-dce99css.js} +5 -5
- package/dist/{build-6r502chw.js → build-bj4x0727.js} +5 -5
- package/dist/checks/source/index.d.ts +1 -0
- package/dist/checks/source/merge-markers.d.ts +3 -0
- package/dist/{cli-cqtggsng.js → cli-0z6zf98d.js} +2 -47
- package/dist/{cli-eq5naw4m.js → cli-7n848af2.js} +370 -1249
- package/dist/{cli-zfcryxg8.js → cli-eewynen9.js} +97 -60
- package/dist/{cli-k1065rkg.js → cli-w692g6dx.js} +75 -29
- package/dist/{cli-149edp6b.js → cli-ydpf09d9.js} +1 -1
- package/dist/cli.js +14 -15
- package/dist/{doctor-akvxbtjb.js → doctor-7nd6n2aa.js} +2 -2
- package/dist/{engine-ft4cr3ep.js → engine-57vw8zpd.js} +1 -1
- package/dist/{engine-b159tbns.js → engine-8kdwy0jz.js} +2 -2
- package/dist/{gutterpress-viewer-te8g5grx.js → gutterpress-viewer-tma1qwz2.js} +57 -15
- package/dist/{index-ycpvr0am.js → index-7hrvksrb.js} +4 -4
- package/dist/{index-mdefp0y5.js → index-dtrftayr.js} +75 -27
- package/dist/{index-ge7q9xj3.js → index-h1x7krkg.js} +771 -1482
- package/dist/{index-wq3r5pj7.js → index-p0ebr7dh.js} +94 -57
- package/dist/index.js +9 -65
- package/dist/lib/git-fs.d.ts +43 -0
- package/dist/lib/host-policy.d.ts +14 -2
- package/dist/lib/remote-auth/clone.d.ts +0 -21
- package/dist/lib/remote-auth/converge-merge.d.ts +24 -7
- package/dist/lib/remote-auth/generic-auth.d.ts +0 -22
- package/dist/lib/remote-auth/git-http.d.ts +1 -1
- package/dist/lib/remote-auth/github-auth.d.ts +2 -22
- package/dist/lib/remote-auth/operation-log.d.ts +4 -9
- package/dist/lib/remote-auth/sync-messages.d.ts +35 -12
- package/dist/lib/remote-auth/sync-types.d.ts +30 -101
- package/dist/lib/remote-auth/sync.d.ts +19 -21
- package/dist/lib/remote-auth/token-store.d.ts +0 -10
- package/dist/lib/remote-auth/transport.d.ts +42 -40
- package/dist/lib/source-provider.d.ts +12 -0
- package/dist/{lint-p2sw53d9.js → lint-c8ze8sqw.js} +5 -5
- package/dist/{new-hvq0x91q.js → new-xy2qc300.js} +5 -5
- package/dist/{plugin-pssmk0dx.js → plugin-g9hwzjsm.js} +5 -5
- package/dist/{preflight-4j00yd0g.js → preflight-prcvdvmk.js} +5 -5
- package/dist/{preview-bridge-fz7vpk8m.js → preview-bridge-2nnrs2hy.js} +15 -0
- package/dist/{preview-d4s6gk6p.js → preview-h479apj9.js} +5 -5
- package/dist/{preview-interface-0ssk8bmm.js → preview-interface-2f5v96hd.js} +411 -88
- package/dist/{preview-shell-c5mfa3q0.js → preview-shell-kyjq59d9.js} +80 -25
- package/dist/{publish-tkc26en7.js → publish-emk644a8.js} +5 -5
- package/dist/{source-provider-3tcj6qg2.js → source-provider-2j5x2hyw.js} +1 -1
- package/dist/{source-provider-vanafrt9.js → source-provider-kn41jmbs.js} +1 -1
- package/dist/{validate-w9vfefrw.js → validate-be54bdc6.js} +5 -5
- package/package.json +1 -1
- package/dist/lib/app-heartbeat.d.ts +0 -102
- package/dist/lib/remote-auth/image-clash.d.ts +0 -17
- package/dist/lib/remote-auth/recovery/classify.d.ts +0 -97
- package/dist/lib/remote-auth/recovery/inspect.d.ts +0 -75
- package/dist/lib/remote-auth/recovery/locks.d.ts +0 -20
- package/dist/lib/remote-auth/recovery/repair.d.ts +0 -29
- package/dist/lib/remote-auth/recovery/types.d.ts +0 -39
- package/dist/repair-2va4w12t.js +0 -137
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
launchChromium,
|
|
5
5
|
marginBoxAlign,
|
|
6
6
|
marginBoxRectPt
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-7hrvksrb.js";
|
|
8
8
|
|
|
9
9
|
// src/engine/compiler/build.ts
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
@@ -1282,6 +1282,62 @@ var DESC_JS = `(el) => {
|
|
|
1282
1282
|
const src = el.tagName === "IMG" ? " src=" + (el.getAttribute("src") || "").slice(-40) : "";
|
|
1283
1283
|
return el.tagName.toLowerCase() + cls + src;
|
|
1284
1284
|
}`;
|
|
1285
|
+
var ABS_CONTAINING_BLOCK_JS = `(cs) => {
|
|
1286
|
+
if (cs.position !== "static") return true;
|
|
1287
|
+
for (const prop of [
|
|
1288
|
+
"transform", "translate", "rotate", "scale", "perspective",
|
|
1289
|
+
"filter", "backdropFilter",
|
|
1290
|
+
]) {
|
|
1291
|
+
const value = cs[prop];
|
|
1292
|
+
if (value && value !== "none") return true;
|
|
1293
|
+
}
|
|
1294
|
+
if (/\\b(layout|paint|strict|content)\\b/.test(cs.contain)) return true;
|
|
1295
|
+
if (cs.containerType && cs.containerType !== "normal") return true;
|
|
1296
|
+
return /\\b(transform|translate|rotate|scale|perspective|filter)\\b/.test(cs.willChange);
|
|
1297
|
+
}`;
|
|
1298
|
+
var CLIP_EDGES_JS = `((establishesAbsContainingBlock) => {
|
|
1299
|
+
const open = () => ({ left: -Infinity, right: Infinity, top: -Infinity, bottom: Infinity });
|
|
1300
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
1301
|
+
const propagates = (a, rootOverflow) =>
|
|
1302
|
+
a === document.documentElement ||
|
|
1303
|
+
(a === document.body && rootOverflow === "visible");
|
|
1304
|
+
return (el) => {
|
|
1305
|
+
const edges = open();
|
|
1306
|
+
const chain = [];
|
|
1307
|
+
const elStyle = getComputedStyle(el);
|
|
1308
|
+
const fixed = elStyle.position === "fixed";
|
|
1309
|
+
let binds = !fixed && elStyle.position !== "absolute";
|
|
1310
|
+
for (let a = el.parentElement; a; a = a.parentElement) {
|
|
1311
|
+
const cs = getComputedStyle(a);
|
|
1312
|
+
if (!fixed && !binds && establishesAbsContainingBlock(cs)) binds = true;
|
|
1313
|
+
const own = open();
|
|
1314
|
+
const clipsX =
|
|
1315
|
+
binds && cs.overflowX !== "visible" && !propagates(a, rootStyle.overflowX);
|
|
1316
|
+
const clipsY =
|
|
1317
|
+
binds && cs.overflowY !== "visible" && !propagates(a, rootStyle.overflowY);
|
|
1318
|
+
if (clipsX || clipsY) {
|
|
1319
|
+
const r = a.getBoundingClientRect();
|
|
1320
|
+
const clipMargin = parseFloat(cs.overflowClipMargin) || 0;
|
|
1321
|
+
if (clipsX) {
|
|
1322
|
+
const grow = cs.overflowX === "clip" ? clipMargin : 0;
|
|
1323
|
+
own.left = r.left + parseFloat(cs.borderLeftWidth) - grow;
|
|
1324
|
+
own.right = r.right - parseFloat(cs.borderRightWidth) + grow;
|
|
1325
|
+
edges.left = Math.max(edges.left, own.left);
|
|
1326
|
+
edges.right = Math.min(edges.right, own.right);
|
|
1327
|
+
}
|
|
1328
|
+
if (clipsY) {
|
|
1329
|
+
const grow = cs.overflowY === "clip" ? clipMargin : 0;
|
|
1330
|
+
own.top = r.top + parseFloat(cs.borderTopWidth) - grow;
|
|
1331
|
+
own.bottom = r.bottom - parseFloat(cs.borderBottomWidth) + grow;
|
|
1332
|
+
edges.top = Math.max(edges.top, own.top);
|
|
1333
|
+
edges.bottom = Math.min(edges.bottom, own.bottom);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
chain.push({ ancestor: a, style: cs, edges: own });
|
|
1337
|
+
}
|
|
1338
|
+
return { edges, chain };
|
|
1339
|
+
};
|
|
1340
|
+
})(${ABS_CONTAINING_BLOCK_JS})`;
|
|
1285
1341
|
function pagePseudoContexts(model) {
|
|
1286
1342
|
const used = new Set(pseudoVariants(model));
|
|
1287
1343
|
const standard = new Set(["left", "right", "recto", "verso", "first", "blank"]);
|
|
@@ -1762,25 +1818,11 @@ ${mapCss}`;
|
|
|
1762
1818
|
return reasons;
|
|
1763
1819
|
};
|
|
1764
1820
|
|
|
1765
|
-
//
|
|
1766
|
-
//
|
|
1767
|
-
// .
|
|
1768
|
-
//
|
|
1769
|
-
|
|
1770
|
-
// the page text; the wrapper's clip never touched it.
|
|
1771
|
-
const establishesAbsContainingBlock = (cs) => {
|
|
1772
|
-
if (cs.position !== "static") return true;
|
|
1773
|
-
for (const prop of [
|
|
1774
|
-
"transform", "translate", "rotate", "scale", "perspective",
|
|
1775
|
-
"filter", "backdropFilter",
|
|
1776
|
-
]) {
|
|
1777
|
-
const value = cs[prop];
|
|
1778
|
-
if (value && value !== "none") return true;
|
|
1779
|
-
}
|
|
1780
|
-
if (/\\b(layout|paint|strict|content)\\b/.test(cs.contain)) return true;
|
|
1781
|
-
if (cs.containerType && cs.containerType !== "normal") return true;
|
|
1782
|
-
return /\\b(transform|translate|rotate|scale|perspective|filter)\\b/.test(cs.willChange);
|
|
1783
|
-
};
|
|
1821
|
+
// Which ancestors clip a .gp-behind, and where their edges fall —
|
|
1822
|
+
// the same measured rule the pre-print width check clamps against
|
|
1823
|
+
// (see CLIP_EDGES_JS). This pass walks its \`chain\` so it can name
|
|
1824
|
+
// the ancestor doing the cutting.
|
|
1825
|
+
const clipEdges = ${CLIP_EDGES_JS};
|
|
1784
1826
|
|
|
1785
1827
|
for (const el of document.querySelectorAll("*")) {
|
|
1786
1828
|
const cs = getComputedStyle(el);
|
|
@@ -1823,47 +1865,25 @@ ${mapCss}`;
|
|
|
1823
1865
|
const boundary = el.closest(".page, .spread");
|
|
1824
1866
|
if (boundary) {
|
|
1825
1867
|
const elRect = el.getBoundingClientRect();
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
// never clip it (measured; see the pass comment above).
|
|
1830
|
-
let clipBinds = cs.position !== "absolute" && cs.position !== "fixed";
|
|
1831
|
-
for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
1832
|
-
const ancestorStyle = getComputedStyle(ancestor);
|
|
1833
|
-
if (!clipBinds && establishesAbsContainingBlock(ancestorStyle)) clipBinds = true;
|
|
1868
|
+
for (const link of clipEdges(el).chain) {
|
|
1869
|
+
const ancestor = link.ancestor;
|
|
1870
|
+
const ancestorStyle = link.style;
|
|
1834
1871
|
const reasons = stackingReasons(ancestor, ancestorStyle);
|
|
1835
1872
|
// Clipping never reorders layers — it can only CUT the
|
|
1836
1873
|
// art (measured, pass comment above): warn only where the
|
|
1837
|
-
// border box crosses
|
|
1838
|
-
//
|
|
1839
|
-
// the
|
|
1840
|
-
//
|
|
1841
|
-
// forms parse NaN -> 0, i.e. the ungrown padding box).
|
|
1842
|
-
// 1px epsilon: the measured cut lands exactly at the
|
|
1843
|
-
// edge, and sub-pixel layout rounding is not an overhang.
|
|
1874
|
+
// border box crosses THIS ancestor's own clip edges, which
|
|
1875
|
+
// are ±Infinity on any axis it does not bind. 1px epsilon:
|
|
1876
|
+
// the measured cut lands exactly at the edge, and sub-pixel
|
|
1877
|
+
// layout rounding is not an overhang.
|
|
1844
1878
|
const cuts = [];
|
|
1845
|
-
if (
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
cuts.push(Math.round(left - elRect.left) + "px past its left clip edge");
|
|
1854
|
-
if (elRect.right > right + 1)
|
|
1855
|
-
cuts.push(Math.round(elRect.right - right) + "px past its right clip edge");
|
|
1856
|
-
}
|
|
1857
|
-
if (ancestorStyle.overflowY !== "visible") {
|
|
1858
|
-
const grow = ancestorStyle.overflowY === "clip" ? clipMargin : 0;
|
|
1859
|
-
const top = r.top + parseFloat(ancestorStyle.borderTopWidth) - grow;
|
|
1860
|
-
const bottom = r.bottom - parseFloat(ancestorStyle.borderBottomWidth) + grow;
|
|
1861
|
-
if (elRect.top < top - 1)
|
|
1862
|
-
cuts.push(Math.round(top - elRect.top) + "px past its top clip edge");
|
|
1863
|
-
if (elRect.bottom > bottom + 1)
|
|
1864
|
-
cuts.push(Math.round(elRect.bottom - bottom) + "px past its bottom clip edge");
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1879
|
+
if (elRect.left < link.edges.left - 1)
|
|
1880
|
+
cuts.push(Math.round(link.edges.left - elRect.left) + "px past its left clip edge");
|
|
1881
|
+
if (elRect.right > link.edges.right + 1)
|
|
1882
|
+
cuts.push(Math.round(elRect.right - link.edges.right) + "px past its right clip edge");
|
|
1883
|
+
if (elRect.top < link.edges.top - 1)
|
|
1884
|
+
cuts.push(Math.round(link.edges.top - elRect.top) + "px past its top clip edge");
|
|
1885
|
+
if (elRect.bottom > link.edges.bottom + 1)
|
|
1886
|
+
cuts.push(Math.round(elRect.bottom - link.edges.bottom) + "px past its bottom clip edge");
|
|
1867
1887
|
if ((reasons.length || cuts.length) && !seenLayerTraps.has(ancestor)) {
|
|
1868
1888
|
seenLayerTraps.add(ancestor);
|
|
1869
1889
|
const effects = [];
|
|
@@ -1968,6 +1988,20 @@ async function findWidthOffenders(page, model, bleedSlugExtensionPt, restoreView
|
|
|
1968
1988
|
const boxes = JSON.parse(await page.evaluate(`(() => {
|
|
1969
1989
|
const LIMIT = ${limitPx} + 1;
|
|
1970
1990
|
const desc = ${DESC_JS};
|
|
1991
|
+
// Shrink-to-fit only reacts to overflow that ESCAPES: a clipping or
|
|
1992
|
+
// scrolling ancestor contains its subtree's overflow, so the
|
|
1993
|
+
// document's own width never grows and the book prints at 1.0x.
|
|
1994
|
+
// MEASURED (Chrome 151, a marker word read off \`pdftotext -bbox\`
|
|
1995
|
+
// against an unshrunk control): a 900px box inside a 300px
|
|
1996
|
+
// \`overflow: hidden\` shell printed unshrunk, as did the same box
|
|
1997
|
+
// under clip/auto/scroll — and a real 208-page book whose two
|
|
1998
|
+
// "offenders" both sat under \`overflow-x: clip\` printed
|
|
1999
|
+
// coordinate-identical with them restored. WHICH ancestors clip, and
|
|
2000
|
+
// where their edges fall, is CLIP_EDGES_JS's measured rule — shared
|
|
2001
|
+
// with the layer-containment audit, which measures overhang past the
|
|
2002
|
+
// same edges. Only the x edges matter here: shrink-to-fit is a width
|
|
2003
|
+
// response.
|
|
2004
|
+
const clipEdges = ${CLIP_EDGES_JS};
|
|
1971
2005
|
const out = [];
|
|
1972
2006
|
for (const el of document.querySelectorAll("*")) {
|
|
1973
2007
|
const r = el.getBoundingClientRect();
|
|
@@ -1977,6 +2011,9 @@ async function findWidthOffenders(page, model, bleedSlugExtensionPt, restoreView
|
|
|
1977
2011
|
// width) — both trigger the same whole-document shrink-to-fit,
|
|
1978
2012
|
// measured: a left bleed alone cost 16%.
|
|
1979
2013
|
if (right <= LIMIT && r.width <= LIMIT && r.left >= -1) continue;
|
|
2014
|
+
// ...but only where it actually escapes its clipping ancestors.
|
|
2015
|
+
const clip = clipEdges(el).edges;
|
|
2016
|
+
if (Math.min(right, clip.right) <= LIMIT && Math.max(r.left, clip.left) >= -1) continue;
|
|
1980
2017
|
let deepest = true;
|
|
1981
2018
|
for (const c of el.children) {
|
|
1982
2019
|
if (c.getBoundingClientRect().width >= r.width - 1) { deepest = false; break; }
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
APP_HEARTBEAT_FRESH_MS,
|
|
3
2
|
AUTO_SNAPSHOT_DEFAULT_MINUTES,
|
|
4
3
|
AUTO_SNAPSHOT_MAX_MINUTES,
|
|
5
4
|
AUTO_SNAPSHOT_MIN_MINUTES,
|
|
6
5
|
AUTO_SYNC_DEFAULT_MINUTES,
|
|
7
6
|
AUTO_SYNC_MAX_MINUTES,
|
|
8
7
|
AUTO_SYNC_MIN_MINUTES,
|
|
8
|
+
AUTO_SYNC_PUSH_INTERVAL_MINUTES,
|
|
9
9
|
BUILT_IN_TEMPLATE_IDS,
|
|
10
10
|
BUILT_IN_THEME_IDS,
|
|
11
11
|
FileTokenStore,
|
|
12
12
|
GITHUB_HOST,
|
|
13
|
-
GenericTokenAuthProvider,
|
|
14
13
|
GitHubAuthProvider,
|
|
15
14
|
MANIFEST_FILENAMES,
|
|
16
15
|
PLUGINS_DIR,
|
|
@@ -18,9 +17,7 @@ import {
|
|
|
18
17
|
PRESET_IDS,
|
|
19
18
|
PublishSelectionsStore,
|
|
20
19
|
RECOMMENDED_PLUGINS,
|
|
21
|
-
RepoNeedsRecoveryError,
|
|
22
20
|
SNIPPETS_DIR,
|
|
23
|
-
STALE_LOCK_MIN_AGE_MS,
|
|
24
21
|
SYNC_SNAPSHOT_MESSAGE,
|
|
25
22
|
TARGETS,
|
|
26
23
|
TARGET_IDS,
|
|
@@ -28,14 +25,11 @@ import {
|
|
|
28
25
|
addLocalPlugin,
|
|
29
26
|
addNpmPlugin,
|
|
30
27
|
adoptFolder,
|
|
31
|
-
appHeartbeatPath,
|
|
32
28
|
applyTheme,
|
|
33
29
|
autoSnapshotDelayMs,
|
|
34
30
|
autoSyncDelayMs,
|
|
35
|
-
buildPreflightDiagnostics,
|
|
36
31
|
checkCss,
|
|
37
32
|
checkToolAvailability,
|
|
38
|
-
classifyFromHealth,
|
|
39
33
|
cloneRepository,
|
|
40
34
|
connectGenericHost,
|
|
41
35
|
connectPublishProvider,
|
|
@@ -55,20 +49,13 @@ import {
|
|
|
55
49
|
getPreviousTheme,
|
|
56
50
|
getSystemDiagnostics,
|
|
57
51
|
hasProjectManifest,
|
|
58
|
-
heartbeatTtlMs,
|
|
59
52
|
importTemplateFromFolder,
|
|
60
53
|
importThemeFromFile,
|
|
61
54
|
importThemeFromFolder,
|
|
62
55
|
importThemeFromUrl,
|
|
63
56
|
inspectImage,
|
|
64
|
-
inspectRepo,
|
|
65
|
-
isAppHeartbeatFresh,
|
|
66
57
|
isGitInternalPath,
|
|
67
|
-
isLikelyRepoCorruption,
|
|
68
|
-
isRepoNeedsRecoveryError,
|
|
69
58
|
isSshRemoteUrl,
|
|
70
|
-
isUnbornRepo,
|
|
71
|
-
keepImageVersion,
|
|
72
59
|
knownForgeTokenUrl,
|
|
73
60
|
listBuiltInTemplates,
|
|
74
61
|
listBuiltInThemes,
|
|
@@ -85,29 +72,17 @@ import {
|
|
|
85
72
|
loadManifest,
|
|
86
73
|
loadManifestWithPath,
|
|
87
74
|
log,
|
|
88
|
-
migrateUrlCredential,
|
|
89
|
-
normalizeForgeHost,
|
|
90
75
|
openPath,
|
|
91
76
|
parseRemoteOrigin,
|
|
92
|
-
preflightStructuralReason,
|
|
93
|
-
provenancePath,
|
|
94
77
|
publishConnectionStatus,
|
|
95
78
|
publishCredentialKey,
|
|
96
79
|
publishProviderFor,
|
|
97
80
|
publishTargetFor,
|
|
98
|
-
pullChanges,
|
|
99
|
-
pushChanges,
|
|
100
|
-
readAppHeartbeat,
|
|
101
|
-
readImageVersion,
|
|
102
81
|
readManifestFields,
|
|
103
|
-
readProjectProvenance,
|
|
104
82
|
readPublishSettings,
|
|
105
83
|
readSnippet,
|
|
106
84
|
readThemeCss,
|
|
107
|
-
redactCredential,
|
|
108
|
-
removeAppHeartbeat,
|
|
109
85
|
removeProjectTheme,
|
|
110
|
-
repairRepo,
|
|
111
86
|
reportMissingTools,
|
|
112
87
|
resolveActiveStyles,
|
|
113
88
|
resolveBuiltInTheme,
|
|
@@ -137,18 +112,16 @@ import {
|
|
|
137
112
|
substituteVariables,
|
|
138
113
|
syncProject,
|
|
139
114
|
testRemoteAccess,
|
|
140
|
-
validateProjectPlugins
|
|
141
|
-
|
|
142
|
-
writeAppHeartbeat
|
|
143
|
-
} from "./index-ge7q9xj3.js";
|
|
115
|
+
validateProjectPlugins
|
|
116
|
+
} from "./index-h1x7krkg.js";
|
|
144
117
|
import {
|
|
145
118
|
BUILD_DIAGNOSTIC_CODES
|
|
146
|
-
} from "./index-
|
|
119
|
+
} from "./index-p0ebr7dh.js";
|
|
147
120
|
import {
|
|
148
121
|
BuildError,
|
|
149
122
|
DEFAULT_PRINT_OPTS,
|
|
150
123
|
readyProbeExpr
|
|
151
|
-
} from "./index-
|
|
124
|
+
} from "./index-7hrvksrb.js";
|
|
152
125
|
import {
|
|
153
126
|
AUTO_SNAPSHOT_MESSAGE,
|
|
154
127
|
HISTORY_PAGE_LIMIT,
|
|
@@ -156,9 +129,8 @@ import {
|
|
|
156
129
|
isNoChangesError,
|
|
157
130
|
providerFor,
|
|
158
131
|
resolveLogger,
|
|
159
|
-
restoreVersionWithBackup
|
|
160
|
-
|
|
161
|
-
} from "./index-mdefp0y5.js";
|
|
132
|
+
restoreVersionWithBackup
|
|
133
|
+
} from "./index-dtrftayr.js";
|
|
162
134
|
import {
|
|
163
135
|
capabilitiesFor,
|
|
164
136
|
detectProjectSource,
|
|
@@ -168,8 +140,6 @@ import {
|
|
|
168
140
|
} from "./index-v5mp7a6q.js";
|
|
169
141
|
import"./index-37x76zdn.js";
|
|
170
142
|
export {
|
|
171
|
-
writeAppHeartbeat,
|
|
172
|
-
verifyRepoReadable,
|
|
173
143
|
validateProjectPlugins,
|
|
174
144
|
testRemoteAccess,
|
|
175
145
|
syncProject,
|
|
@@ -177,7 +147,6 @@ export {
|
|
|
177
147
|
startPreviewServer,
|
|
178
148
|
splitOutPath,
|
|
179
149
|
slugifyProjectName,
|
|
180
|
-
shortOid,
|
|
181
150
|
setPublishProviderConfig,
|
|
182
151
|
setPluginEnabled,
|
|
183
152
|
setManifestFields,
|
|
@@ -205,31 +174,19 @@ export {
|
|
|
205
174
|
reportMissingTools,
|
|
206
175
|
repoSubPath,
|
|
207
176
|
repoRootForSource,
|
|
208
|
-
repairRepo,
|
|
209
177
|
removeProjectTheme,
|
|
210
|
-
removeAppHeartbeat,
|
|
211
|
-
redactCredential,
|
|
212
178
|
readyProbeExpr,
|
|
213
179
|
readThemeCss,
|
|
214
180
|
readSnippet,
|
|
215
181
|
readPublishSettings,
|
|
216
|
-
readProjectProvenance,
|
|
217
182
|
readManifestFields,
|
|
218
|
-
readImageVersion,
|
|
219
|
-
readAppHeartbeat,
|
|
220
|
-
pushChanges,
|
|
221
|
-
pullChanges,
|
|
222
183
|
publishTargetFor,
|
|
223
184
|
publishProviderFor,
|
|
224
185
|
publishCredentialKey,
|
|
225
186
|
publishConnectionStatus,
|
|
226
187
|
providerFor,
|
|
227
|
-
provenancePath,
|
|
228
|
-
preflightStructuralReason,
|
|
229
188
|
parseRemoteOrigin,
|
|
230
189
|
openPath,
|
|
231
|
-
normalizeForgeHost,
|
|
232
|
-
migrateUrlCredential,
|
|
233
190
|
log,
|
|
234
191
|
loadManifestWithPath,
|
|
235
192
|
loadManifest,
|
|
@@ -246,21 +203,14 @@ export {
|
|
|
246
203
|
listBuiltInThemes,
|
|
247
204
|
listBuiltInTemplates,
|
|
248
205
|
knownForgeTokenUrl,
|
|
249
|
-
keepImageVersion,
|
|
250
|
-
isUnbornRepo,
|
|
251
206
|
isSshRemoteUrl,
|
|
252
|
-
isRepoNeedsRecoveryError,
|
|
253
207
|
isNoChangesError,
|
|
254
|
-
isLikelyRepoCorruption,
|
|
255
208
|
isGitInternalPath,
|
|
256
|
-
isAppHeartbeatFresh,
|
|
257
|
-
inspectRepo,
|
|
258
209
|
inspectImage,
|
|
259
210
|
importThemeFromUrl,
|
|
260
211
|
importThemeFromFolder,
|
|
261
212
|
importThemeFromFile,
|
|
262
213
|
importTemplateFromFolder,
|
|
263
|
-
heartbeatTtlMs,
|
|
264
214
|
hasProjectManifest,
|
|
265
215
|
getSystemDiagnostics,
|
|
266
216
|
getPreviousTheme,
|
|
@@ -282,15 +232,12 @@ export {
|
|
|
282
232
|
connectPublishProvider,
|
|
283
233
|
connectGenericHost,
|
|
284
234
|
cloneRepository,
|
|
285
|
-
classifyFromHealth,
|
|
286
235
|
checkToolAvailability,
|
|
287
236
|
checkCss,
|
|
288
237
|
capabilitiesFor,
|
|
289
|
-
buildPreflightDiagnostics,
|
|
290
238
|
autoSyncDelayMs,
|
|
291
239
|
autoSnapshotDelayMs,
|
|
292
240
|
applyTheme,
|
|
293
|
-
appHeartbeatPath,
|
|
294
241
|
adoptFolder,
|
|
295
242
|
addNpmPlugin,
|
|
296
243
|
addLocalPlugin,
|
|
@@ -298,9 +245,7 @@ export {
|
|
|
298
245
|
TARGET_IDS,
|
|
299
246
|
TARGETS,
|
|
300
247
|
SYNC_SNAPSHOT_MESSAGE,
|
|
301
|
-
STALE_LOCK_MIN_AGE_MS,
|
|
302
248
|
SNIPPETS_DIR,
|
|
303
|
-
RepoNeedsRecoveryError,
|
|
304
249
|
RESTORE_BACKUP_MESSAGE,
|
|
305
250
|
RECOMMENDED_PLUGINS,
|
|
306
251
|
PublishSelectionsStore,
|
|
@@ -310,7 +255,6 @@ export {
|
|
|
310
255
|
MANIFEST_FILENAMES,
|
|
311
256
|
HISTORY_PAGE_LIMIT,
|
|
312
257
|
GitHubAuthProvider,
|
|
313
|
-
GenericTokenAuthProvider,
|
|
314
258
|
GITHUB_HOST,
|
|
315
259
|
FileTokenStore,
|
|
316
260
|
DEFAULT_PRINT_OPTS,
|
|
@@ -318,12 +262,12 @@ export {
|
|
|
318
262
|
BUILT_IN_THEME_IDS,
|
|
319
263
|
BUILT_IN_TEMPLATE_IDS,
|
|
320
264
|
BUILD_DIAGNOSTIC_CODES,
|
|
265
|
+
AUTO_SYNC_PUSH_INTERVAL_MINUTES,
|
|
321
266
|
AUTO_SYNC_MIN_MINUTES,
|
|
322
267
|
AUTO_SYNC_MAX_MINUTES,
|
|
323
268
|
AUTO_SYNC_DEFAULT_MINUTES,
|
|
324
269
|
AUTO_SNAPSHOT_MIN_MINUTES,
|
|
325
270
|
AUTO_SNAPSHOT_MESSAGE,
|
|
326
271
|
AUTO_SNAPSHOT_MAX_MINUTES,
|
|
327
|
-
AUTO_SNAPSHOT_DEFAULT_MINUTES
|
|
328
|
-
APP_HEARTBEAT_FRESH_MS
|
|
272
|
+
AUTO_SNAPSHOT_DEFAULT_MINUTES
|
|
329
273
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `node:fs`, with crash-atomic writes for git's MUTABLE metadata.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. isomorphic-git writes `.git/index`, `HEAD`, `packed-refs`
|
|
5
|
+
* and loose refs with a plain `fs.writeFile` — an open-truncate-write. If the
|
|
6
|
+
* process dies between the truncate and the write (the writer force-quits the
|
|
7
|
+
* app, the OS kills it, the machine sleeps badly) the file is left EMPTY or
|
|
8
|
+
* half-written, and the repository can no longer be read. That is the one
|
|
9
|
+
* class of history damage Gutterpress itself causes; every other class needs
|
|
10
|
+
* an outside actor. Since isomorphic-git takes `fs` as a parameter, replacing
|
|
11
|
+
* the write with temp-file + `rename` removes the truncation window entirely
|
|
12
|
+
* — a reader sees either the old file or the new one, never a torn one.
|
|
13
|
+
*
|
|
14
|
+
* SCOPE. Only git's mutable metadata is redirected. Working-tree files and
|
|
15
|
+
* content-addressed objects keep the plain write: objects are written once
|
|
16
|
+
* under a hash of their contents (a torn object is a NEW file that simply
|
|
17
|
+
* fails to inflate, it never destroys a good one), and the working tree is
|
|
18
|
+
* the author's own files, which `rename` semantics would not improve.
|
|
19
|
+
*
|
|
20
|
+
* NOT A DURABILITY BARRIER. There is deliberately no `fsync`: this closes the
|
|
21
|
+
* PROCESS-DEATH window, where the page cache survives and `rename` is atomic
|
|
22
|
+
* to every reader. Surviving sudden power loss would mean fsync-ing the temp
|
|
23
|
+
* file and its directory on every ref write, which is a real cost on the
|
|
24
|
+
* snapshot hot path and guards a failure the app does not cause.
|
|
25
|
+
*
|
|
26
|
+
* Everything not named below passes through to `node:fs` unchanged, so
|
|
27
|
+
* `import { gitFs as fs }` is a drop-in for `import * as fs from "node:fs"`.
|
|
28
|
+
*/
|
|
29
|
+
import * as nodeFs from "node:fs";
|
|
30
|
+
/** True when `file` is git metadata that must be replaced, never truncated. */
|
|
31
|
+
export declare function needsAtomicWrite(file: unknown): file is string;
|
|
32
|
+
/**
|
|
33
|
+
* `node:fs` with the two writers above swapped in — the object callers import
|
|
34
|
+
* as `fs` and hand to isomorphic-git. Everything else passes through
|
|
35
|
+
* untouched.
|
|
36
|
+
*
|
|
37
|
+
* WHY an object and not `export * from "node:fs"`: bun build with
|
|
38
|
+
* `--packages=external` compiles a star re-export of a BUILTIN into a
|
|
39
|
+
* `__reExport(exports, node_fs)` call whose `node_fs` binding is never
|
|
40
|
+
* emitted, so the built lib throws `ReferenceError: node_fs is not defined`
|
|
41
|
+
* on import. A plain spread has no such hazard.
|
|
42
|
+
*/
|
|
43
|
+
export declare const gitFs: typeof nodeFs;
|
|
@@ -40,6 +40,17 @@ export interface AutoSyncPolicy {
|
|
|
40
40
|
export declare const AUTO_SYNC_MIN_MINUTES = 1;
|
|
41
41
|
export declare const AUTO_SYNC_MAX_MINUTES: number;
|
|
42
42
|
export declare const AUTO_SYNC_DEFAULT_MINUTES = 2;
|
|
43
|
+
/**
|
|
44
|
+
* Minutes between PUSH-enabled auto-sync passes (owner decision 2026-08-23).
|
|
45
|
+
* Every ~2-minute tick still PULLS — a collaborator's work keeps arriving
|
|
46
|
+
* promptly — but only a tick whose push window has elapsed also pushes, and
|
|
47
|
+
* the desktop pushes once more on project close/app exit. This is what keeps
|
|
48
|
+
* an actively-typing author from minting a snapshot-and-push every 2 minutes
|
|
49
|
+
* (the "commit wall"): between push windows, ticks with an unmoved remote
|
|
50
|
+
* commit nothing at all. Deliberately a constant, NOT a settings knob — the
|
|
51
|
+
* sync cadence is hidden policy, same as the tick interval's default.
|
|
52
|
+
*/
|
|
53
|
+
export declare const AUTO_SYNC_PUSH_INTERVAL_MINUTES = 15;
|
|
43
54
|
/**
|
|
44
55
|
* Resolve the debounce delay (ms) for the host's auto-snapshot timer, or
|
|
45
56
|
* `null` when automatic snapshots are disabled. Pure — the testable core of
|
|
@@ -61,8 +72,9 @@ export declare function autoSnapshotDelayMs(policy: Partial<AutoSnapshotPolicy>
|
|
|
61
72
|
* the default and is then clamped into [AUTO_SYNC_MIN_MINUTES,
|
|
62
73
|
* AUTO_SYNC_MAX_MINUTES].
|
|
63
74
|
*
|
|
64
|
-
* Note: the host orchestrator
|
|
65
|
-
*
|
|
75
|
+
* Note: this is the host orchestrator's ONLY sync trigger — the file-change
|
|
76
|
+
* debounce that used to sit beside it was removed (it could never fire before
|
|
77
|
+
* this interval already had).
|
|
66
78
|
*/
|
|
67
79
|
export declare function autoSyncDelayMs(policy: Partial<AutoSyncPolicy> | undefined): number | null;
|
|
68
80
|
/**
|
|
@@ -7,22 +7,6 @@ export interface CloneProgressEvent {
|
|
|
7
7
|
loaded: number;
|
|
8
8
|
total?: number;
|
|
9
9
|
}
|
|
10
|
-
/**
|
|
11
|
-
* Provider provenance recorded next to a cloned project (ADR 0006 D4):
|
|
12
|
-
* metadata for the repo picker / re-auth UX, never consulted by the
|
|
13
|
-
* editing/preview/build paths.
|
|
14
|
-
*/
|
|
15
|
-
export interface ProjectProvenance {
|
|
16
|
-
provider: "github";
|
|
17
|
-
owner: string;
|
|
18
|
-
repo: string;
|
|
19
|
-
/**
|
|
20
|
-
* Legacy GitHub-App installation id. New clones never write it (the OAuth
|
|
21
|
-
* App model has no installations — ADR 0006 D1 amendment 2026-06-10); kept
|
|
22
|
-
* optional so provenance files written by 0.4.x betas still parse.
|
|
23
|
-
*/
|
|
24
|
-
installationId?: string;
|
|
25
|
-
}
|
|
26
10
|
export interface CloneRepositoryOptions {
|
|
27
11
|
/** HTTPS clone URL. Tokens embedded in the URL are stripped (D7). */
|
|
28
12
|
url: string;
|
|
@@ -49,8 +33,6 @@ export interface CloneRepositoryOptions {
|
|
|
49
33
|
onProgress?: (event: CloneProgressEvent) => void;
|
|
50
34
|
/** When provided, credentials embedded in `url` are migrated into it (D7). */
|
|
51
35
|
tokenStore?: TokenStore;
|
|
52
|
-
/** Provider provenance to record beside the clone (ADR 0006 D4). */
|
|
53
|
-
provenance?: ProjectProvenance;
|
|
54
36
|
/**
|
|
55
37
|
* Injectable git HTTP transport for tests (isomorphic-git's `http` client
|
|
56
38
|
* shape). Defaults to isomorphic-git's node client.
|
|
@@ -63,9 +45,6 @@ export interface CloneRepositoryResult {
|
|
|
63
45
|
/** The checked-out branch. */
|
|
64
46
|
branch?: string;
|
|
65
47
|
}
|
|
66
|
-
export declare function provenancePath(projectDir: string): string;
|
|
67
|
-
/** Read recorded provider provenance for a project, if any. Never throws. */
|
|
68
|
-
export declare function readProjectProvenance(projectDir: string): Promise<ProjectProvenance | null>;
|
|
69
48
|
/**
|
|
70
49
|
* Reduce a (possibly renderer-/user-supplied) project folder name to a single
|
|
71
50
|
* safe path segment: path separators become dashes and leading dots are
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
1
|
+
import { onlineSiblingPath, isOnlineSibling } from "./sync-messages.ts";
|
|
2
|
+
export { onlineSiblingPath, isOnlineSibling };
|
|
3
|
+
import type { GitCache, KeptBothFile } from "./sync-types.ts";
|
|
4
|
+
export type { KeptBothFile };
|
|
5
|
+
/** Type guard exposing MergeConflictError's per-file payload. */
|
|
6
|
+
export declare function isMergeConflictError(e: unknown): e is {
|
|
7
|
+
data: {
|
|
8
|
+
filepaths: string[];
|
|
9
|
+
bothModified: string[];
|
|
10
|
+
deleteByUs: string[];
|
|
11
|
+
deleteByTheirs: string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A non-forced `git.checkout` refused to overwrite working-tree files whose
|
|
16
|
+
* content moved after we committed them. isomorphic-git detects this in its
|
|
17
|
+
* analysis pass and throws BEFORE touching the tree, so the refusal is
|
|
18
|
+
* atomic: nothing on disk has been written.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isCheckoutConflict(e: unknown): boolean;
|
|
3
21
|
export interface ConvergeResult {
|
|
4
22
|
/** The branch tip after the merge (the merge commit, or the ff/no-op tip). */
|
|
5
23
|
oid: string;
|
|
@@ -8,13 +26,13 @@ export interface ConvergeResult {
|
|
|
8
26
|
* the writer should blend these. Empty when everything merged cleanly.
|
|
9
27
|
*/
|
|
10
28
|
combinedFiles: string[];
|
|
11
|
-
/**
|
|
12
|
-
|
|
29
|
+
/** Files kept as a pair: ours at `path`, theirs at `onlinePath`. */
|
|
30
|
+
keptBothFiles: KeptBothFile[];
|
|
13
31
|
}
|
|
14
32
|
/** Snapshot message for the equalization commit (driver-unreachable cases). */
|
|
15
33
|
export declare const CONVERGE_PREPARE_MESSAGE = "Getting your changes ready to combine with the online version";
|
|
16
|
-
/** Snapshot message for the post-merge restore commit (
|
|
17
|
-
export declare const CONVERGE_RESTORE_MESSAGE = "Kept
|
|
34
|
+
/** Snapshot message for the post-merge restore commit (kept-both/edits). */
|
|
35
|
+
export declare const CONVERGE_RESTORE_MESSAGE = "Kept both versions of the files that can't be combined";
|
|
18
36
|
/** The merge commit message (same wording the old sync used). */
|
|
19
37
|
export declare const CONVERGE_MERGE_MESSAGE = "Combined your changes with the online version";
|
|
20
38
|
/**
|
|
@@ -44,5 +62,4 @@ export declare function convergeMerge(params: {
|
|
|
44
62
|
};
|
|
45
63
|
authorName?: string;
|
|
46
64
|
authorEmail?: string;
|
|
47
|
-
allowUnrelatedHistories?: boolean;
|
|
48
65
|
}): Promise<ConvergeResult>;
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
* SECURITY INVARIANT: token values never appear in error messages or logs.
|
|
16
16
|
*/
|
|
17
17
|
import type httpNode from "isomorphic-git/http/node";
|
|
18
|
-
import type { HostCallbacks, RemoteAuthProvider } from "./github-auth.ts";
|
|
19
18
|
import { type HostCredential } from "./token-store.ts";
|
|
20
19
|
/** What the Advanced Setup UI collects for "Connect a Git server". */
|
|
21
20
|
export interface GenericTokenConnectInput {
|
|
@@ -38,14 +37,6 @@ export interface GenericTokenConnectInput {
|
|
|
38
37
|
*/
|
|
39
38
|
repoUrl?: string;
|
|
40
39
|
}
|
|
41
|
-
/**
|
|
42
|
-
* The {@link RemoteAuthProvider} `connect` input for the generic provider:
|
|
43
|
-
* the UI-collected fields plus the standard host callbacks (`onUserCode` is
|
|
44
|
-
* part of the shared contract but unused — there is no device code in the
|
|
45
|
-
* token flow; pass a no-op, or use {@link connectGenericHost} directly).
|
|
46
|
-
*/
|
|
47
|
-
export interface GenericHostCallbacks extends HostCallbacks, GenericTokenConnectInput {
|
|
48
|
-
}
|
|
49
40
|
export interface GenericAuthOptions {
|
|
50
41
|
/** Injectable git HTTP transport for tests. */
|
|
51
42
|
httpClient?: typeof httpNode;
|
|
@@ -82,16 +73,3 @@ export declare function knownForgeTokenUrl(host: string): string | null;
|
|
|
82
73
|
* a full end-to-end verification of the token against a real repository.
|
|
83
74
|
*/
|
|
84
75
|
export declare function connectGenericHost(input: GenericTokenConnectInput, options?: GenericAuthOptions): Promise<HostCredential>;
|
|
85
|
-
/**
|
|
86
|
-
* {@link RemoteAuthProvider} wrapper for the generic token flow, so host apps
|
|
87
|
-
* can route any non-GitHub origin through the shared provider contract.
|
|
88
|
-
*/
|
|
89
|
-
export declare class GenericTokenAuthProvider implements RemoteAuthProvider {
|
|
90
|
-
private readonly options;
|
|
91
|
-
constructor(options?: GenericAuthOptions);
|
|
92
|
-
/** Handles any smart-HTTP(S) host that is not github.com (device flow). */
|
|
93
|
-
matches(origin: URL): boolean;
|
|
94
|
-
connect(callbacks: GenericHostCallbacks): Promise<HostCredential>;
|
|
95
|
-
/** Revalidate a stored credential: false only on a definitive rejection. */
|
|
96
|
-
validate(credential: HostCredential): Promise<boolean>;
|
|
97
|
-
}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
*
|
|
32
32
|
* One timer serves the whole request (re-armed via `refresh()`; no per-chunk
|
|
33
33
|
* allocation). On timeout the thrown error's message classifies as offline in
|
|
34
|
-
*
|
|
34
|
+
* transport.ts. Known limitation (documented, not fixable at this
|
|
35
35
|
* layer): isomorphic-git's client accepts no AbortSignal, so an abandoned
|
|
36
36
|
* timed-out transfer's socket is left to the OS/agent to reap.
|
|
37
37
|
*
|