gutterpress 0.9.0-alpha.2 → 0.10.0-alpha.4
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 +18 -4
- package/dist/api/index.d.ts +11 -5
- package/dist/api/index.js +20 -15
- package/dist/assets/preview/scripts/preview-bridge.d.ts +1 -0
- package/dist/assets/preview/scripts/preview-interface.d.ts +1 -0
- package/dist/{audit-nhn2pjz3.js → audit-k1vnfwvc.js} +10 -7
- package/dist/{build-san7fv2z.js → build-hqmwgvdw.js} +14 -8
- package/dist/checks/source/index.d.ts +1 -0
- package/dist/checks/source/layout-markers.d.ts +3 -0
- package/dist/checks/source/local-ref-parser.d.ts +28 -0
- package/dist/{cli-e5zhb0xs.js → cli-0r0tq16s.js} +17 -24
- package/dist/cli-46ycxe6r.js +18 -0
- package/dist/cli-c41yr7he.js +241 -0
- package/dist/cli-hp9r2pzt.js +2343 -0
- package/dist/{index-yzrh708h.js → cli-k1065rkg.js} +2 -5
- package/dist/{cli-k4bd06sd.js → cli-n25qycwz.js} +4986 -5889
- package/dist/{cli-najycadg.js → cli-ra0ed2xt.js} +69 -253
- package/dist/{cli-506tg37g.js → cli-revgt4pr.js} +2 -1
- package/dist/{cli-wchtvxvw.js → cli-v5mp7a6q.js} +14 -1
- package/dist/cli.js +20 -18
- package/dist/{doctor-hrk0kxxz.js → doctor-dxms7ehm.js} +4 -2
- package/dist/engine/compiler/build.d.ts +139 -0
- package/dist/engine/compiler/postprocess.d.ts +23 -0
- package/dist/engine/compiler/tier2.d.ts +78 -0
- package/dist/engine/shared/cdp.d.ts +104 -0
- package/dist/engine/shared/content-value.d.ts +64 -0
- package/dist/engine/shared/gcpm-extract.d.ts +119 -0
- package/dist/engine/shared/margin-box-support.d.ts +12 -0
- package/dist/engine/shared/pdf-inspect.d.ts +24 -0
- package/dist/engine/shared/synthesis.d.ts +155 -0
- package/dist/engine-wa7y9av9.js +41 -0
- package/dist/engine-z4p9sr4h.js +40 -0
- package/dist/gutterpress-agent-1ctgfz92.js +576 -0
- package/dist/gutterpress-viewer-cem7dmr5.js +2349 -0
- package/dist/{index-bynn850m.js → index-05y3dnxq.js} +3049 -4015
- package/dist/index-9tyq9kks.js +708 -0
- package/dist/{cli-yzrh708h.js → index-mdefp0y5.js} +1 -1
- package/dist/{index-wchtvxvw.js → index-v5mp7a6q.js} +14 -1
- package/dist/index-xxg4zfrg.js +1907 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +24 -17
- package/dist/lib/asset-inline.d.ts +36 -0
- package/dist/lib/browser-pool.d.ts +18 -0
- package/dist/lib/build-error.d.ts +1 -1
- package/dist/lib/build-preflight.d.ts +35 -2
- package/dist/lib/build-runner.d.ts +45 -17
- package/dist/lib/build-staging.d.ts +9 -55
- package/dist/lib/cli-args.d.ts +8 -0
- package/dist/lib/desktop.d.ts +2 -2
- package/dist/lib/embedded-assets.d.ts +1 -1
- package/dist/lib/engine.d.ts +33 -0
- package/dist/lib/ghostscript.d.ts +46 -1
- package/dist/lib/markdown/assemble.d.ts +18 -10
- package/dist/lib/markdown/gp-pin-scope.d.ts +1 -0
- package/dist/lib/markdown/gutterpress-css.d.ts +125 -0
- package/dist/lib/markdown/images.d.ts +26 -0
- package/dist/lib/markdown/index.d.ts +4 -2
- package/dist/lib/markdown/inline-source.d.ts +10 -0
- package/dist/lib/markdown/markers.d.ts +32 -0
- package/dist/lib/markdown/renderer.d.ts +8 -3
- package/dist/lib/markdown/source-range.d.ts +61 -0
- package/dist/lib/missing-asset-placeholder.d.ts +52 -0
- package/dist/lib/presets.d.ts +1 -1
- package/dist/lib/printsafe.d.ts +2 -3
- package/dist/lib/remote-auth/converge-merge.d.ts +48 -0
- package/dist/lib/remote-auth/image-clash.d.ts +17 -0
- package/dist/lib/remote-auth/recovery/classify.d.ts +55 -67
- package/dist/lib/remote-auth/recovery/inspect.d.ts +14 -13
- package/dist/lib/remote-auth/recovery/locks.d.ts +20 -0
- package/dist/lib/remote-auth/recovery/repair.d.ts +29 -0
- package/dist/lib/remote-auth/recovery/types.d.ts +8 -204
- package/dist/lib/remote-auth/sync-messages.d.ts +2 -3
- package/dist/lib/remote-auth/sync-types.d.ts +35 -63
- package/dist/lib/remote-auth/sync.d.ts +11 -18
- package/dist/lib/remote-auth/transport.d.ts +14 -7
- package/dist/lib/theme-import.d.ts +4 -5
- package/dist/{lint-96j9hrj4.js → lint-xjwm5ep8.js} +10 -7
- package/dist/{manifest.schema-z61rzw44.json → manifest.schema-zxgxnbg7.json} +21 -0
- package/dist/{new-8p38wavc.js → new-kwdwpf0j.js} +12 -8
- package/dist/{plugin-ees6nhkc.js → plugin-rg4tnn96.js} +10 -7
- package/dist/{preflight-1q6c2edh.js → preflight-3127y25z.js} +10 -7
- package/dist/preview/file-watcher.d.ts +13 -17
- package/dist/preview/lifecycle.d.ts +1 -1
- package/dist/{pagedjs-bridge-vn4hk9fx.js → preview-bridge-fz7vpk8m.js} +8 -0
- package/dist/preview-interface-435cczt5.js +1059 -0
- package/dist/{preview-y5a2zen1.js → preview-ncgfhqmw.js} +16 -9
- package/dist/preview-shell-c5mfa3q0.js +346 -0
- package/dist/{project-source-p0gn1wd5.js → project-source-ekcyp63q.js} +1 -1
- package/dist/{publish-rm9yb3wh.js → publish-pr0rwh6p.js} +10 -7
- package/dist/render.d.ts +3 -4
- package/dist/render.js +728 -64
- package/dist/{repair-zgq7q2g6.js → repair-8270smfw.js} +45 -79
- package/dist/schema/manifest.types.d.ts +38 -0
- package/dist/{source-provider-c1rjm2c0.js → source-provider-3tcj6qg2.js} +2 -2
- package/dist/source-provider-vanafrt9.js +40 -0
- package/dist/{theme-zz2ktzqs.css → theme-h5recz6c.css} +8 -7
- package/dist/{theme-570zmh2t.css → theme-j2bagrfx.css} +8 -7
- package/dist/{theme-nya4nqh6.css → theme-nn6d53zy.css} +8 -7
- package/dist/types.d.ts +7 -0
- package/dist/{validate-nr0xa6sa.js → validate-54e17rae.js} +10 -7
- package/package.json +6 -6
- package/dist/cli-yja077f6.js +0 -92
- package/dist/git-http-yrb4ag6z.js +0 -17
- package/dist/index-yja077f6.js +0 -92
- package/dist/lib/markdown/markdown-it-paged.d.ts +0 -30
- package/dist/lib/pagedjs-marker.d.ts +0 -42
- package/dist/lib/pagedjs.d.ts +0 -26
- package/dist/lib/pagination.d.ts +0 -149
- package/dist/lib/remote-auth/conflict-resolution.d.ts +0 -29
- package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +0 -103
- package/dist/lib/remote-auth/recovery/backup.d.ts +0 -113
- package/dist/lib/remote-auth/recovery/context.d.ts +0 -47
- package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -28
- package/dist/lib/remote-auth/recovery/failsafe.d.ts +0 -33
- package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +0 -28
- package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +0 -59
- package/dist/lib/remote-auth/recovery/policy.d.ts +0 -47
- package/dist/lib/remote-auth/recovery/recover-auth.d.ts +0 -42
- package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +0 -37
- package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +0 -40
- package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +0 -70
- package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +0 -23
- package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +0 -28
- package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +0 -39
- package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +0 -34
- package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +0 -37
- package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +0 -56
- package/dist/lib/remote-auth/recovery/recover-network.d.ts +0 -34
- package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +0 -27
- package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -68
- package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +0 -44
- package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +0 -35
- package/dist/lib/remote-auth/resolution-plan.d.ts +0 -64
- package/dist/paged.polyfill-n95pbxfn.js +0 -33288
- package/dist/pagedjs-interface-qxvzgwd7.js +0 -557
- package/dist/preview-shell-6dqexx1m.js +0 -581
- /package/dist/assets/{preview/scripts/pagedjs-bridge.d.ts → engine/gutterpress-agent.d.ts} +0 -0
- /package/dist/assets/{preview/scripts/pagedjs-interface.d.ts → engine/gutterpress-viewer.d.ts} +0 -0
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Error
|
|
3
|
-
* source of truth for git error classification.
|
|
2
|
+
* Error/health classification for sync + repair — the SINGLE source of truth.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* each decoder, not parallel copies "kept in sync by spec".
|
|
4
|
+
* 2026-08-14 simplification (owner directive): the 17-kind SyncErrorKind
|
|
5
|
+
* taxonomy and its 16 per-kind handlers are gone. Every structural problem a
|
|
6
|
+
* repo can have now has ONE answer — `repairRepo()` (repair.ts) — so
|
|
7
|
+
* classification collapses to three health verdicts:
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* - null — healthy, nothing to do
|
|
10
|
+
* - "stale_lock" — a leftover lock old enough to sweep
|
|
11
|
+
* - "needs_repair" — anything structural (missing/corrupt `.git`, broken
|
|
12
|
+
* ref store, detached HEAD, interrupted merge/rebase/
|
|
13
|
+
* cherry-pick left by an external tool)
|
|
14
14
|
*
|
|
15
|
+
* The transport decoders (auth/offline/insecure) and the merge/push guards
|
|
16
|
+
* used by sync.ts remain here unchanged — outcome mapping, not repair.
|
|
15
17
|
* This module is pure — no I/O, no side effects.
|
|
16
18
|
*/
|
|
17
|
-
import type { RepoHealth
|
|
19
|
+
import type { RepoHealth } from "./types.ts";
|
|
18
20
|
/**
|
|
19
|
-
* Minimum age before a leftover git lock counts as STALE
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* with retry_later ("a live process may still hold it").
|
|
21
|
+
* Minimum age before a leftover git lock counts as STALE. locks.ts imports
|
|
22
|
+
* this same constant as its sweep threshold, so preflight and sweep can never
|
|
23
|
+
* disagree: a lock young enough to pass preflight is exactly a lock the sweep
|
|
24
|
+
* would defer ("a live process may still hold it").
|
|
24
25
|
*/
|
|
25
26
|
export declare const STALE_LOCK_MIN_AGE_MS: number;
|
|
27
|
+
/** The collapsed repair taxonomy — see the module header. */
|
|
28
|
+
export type RepairNeed = "stale_lock" | "needs_repair";
|
|
26
29
|
/**
|
|
27
|
-
* Thrown by
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* Thrown by sync's structural preflight when the repo must be repaired before
|
|
31
|
+
* any sync work can safely run. The `code` string is the STABLE contract
|
|
32
|
+
* hosts may match on across the dynamic-import boundary (where `instanceof`
|
|
33
|
+
* is unreliable); `kind` says whether a lock sweep suffices or the full
|
|
34
|
+
* repair pipeline is needed.
|
|
31
35
|
*/
|
|
32
36
|
export declare class RepoNeedsRecoveryError extends Error {
|
|
33
|
-
readonly kind:
|
|
37
|
+
readonly kind: RepairNeed;
|
|
34
38
|
readonly code = "RepoNeedsRecovery";
|
|
35
|
-
constructor(kind:
|
|
39
|
+
constructor(kind: RepairNeed);
|
|
36
40
|
}
|
|
37
41
|
/** Type guard for {@link RepoNeedsRecoveryError} (matches on the stable code). */
|
|
38
42
|
export declare function isRepoNeedsRecoveryError(e: unknown): e is RepoNeedsRecoveryError;
|
|
@@ -40,9 +44,8 @@ export declare function isRepoNeedsRecoveryError(e: unknown): e is RepoNeedsReco
|
|
|
40
44
|
* Thrown by transport.ts's onAuth when a stored credential EXISTS but the
|
|
41
45
|
* remote URL fails isCredentialTransmissionSafe (non-loopback http). Loud and
|
|
42
46
|
* typed on purpose: the old behavior (silently withholding the credential)
|
|
43
|
-
* surfaced as a 401 → "auth" → "reconnect" loop
|
|
44
|
-
*
|
|
45
|
-
* (matchable across dynamic-import boundaries where `instanceof` fails).
|
|
47
|
+
* surfaced as a 401 → "auth" → "reconnect" loop. The `code` string is the
|
|
48
|
+
* STABLE contract (matchable across dynamic-import boundaries).
|
|
46
49
|
*/
|
|
47
50
|
export declare class InsecureTransportError extends Error {
|
|
48
51
|
readonly code = "InsecureTransport";
|
|
@@ -51,7 +54,7 @@ export declare class InsecureTransportError extends Error {
|
|
|
51
54
|
/** Type guard for {@link InsecureTransportError} (matches on the stable code). */
|
|
52
55
|
export declare function isInsecureTransportError(e: unknown): e is InsecureTransportError;
|
|
53
56
|
export declare function isPushRejected(e: unknown): boolean;
|
|
54
|
-
/** Type guard exposing MergeConflictError's per-file payload (
|
|
57
|
+
/** Type guard exposing MergeConflictError's per-file payload (converge-merge). */
|
|
55
58
|
export declare function isMergeConflictError(e: unknown): e is {
|
|
56
59
|
data: {
|
|
57
60
|
filepaths: string[];
|
|
@@ -60,50 +63,35 @@ export declare function isMergeConflictError(e: unknown): e is {
|
|
|
60
63
|
deleteByTheirs: string[];
|
|
61
64
|
};
|
|
62
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* Unrelated histories — the local project and the configured online project
|
|
68
|
+
* share no common starting point. Sync surfaces this as a plain setup error
|
|
69
|
+
* (a wrong online address must never be silently spliced into the book).
|
|
70
|
+
*/
|
|
71
|
+
export declare function isUnrelatedHistories(e: unknown): boolean;
|
|
63
72
|
export declare function classifyTransportFailure(e: unknown): "auth_required" | "network_unavailable" | "insecure_transport" | null;
|
|
64
73
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* (an in-progress rebase usually detaches HEAD — the abort repair must win over
|
|
73
|
-
* the rescue-branch repair), and specific interrupted-op repairs precede the
|
|
74
|
-
* generic stale-lock cleanup.
|
|
75
|
-
*
|
|
76
|
-
* `minLockAgeMs` gates the stale-lock classification: at preflight (the
|
|
77
|
-
* default, STALE_LOCK_MIN_AGE_MS) a younger lock is treated as healthy because
|
|
78
|
-
* a live process may still hold it — the same rule recover-stale-lock.ts
|
|
79
|
-
* applies before acting. Error-path callers pass 0: a lock that just made a
|
|
80
|
-
* sync THROW is worth routing regardless of age (the handler still re-checks
|
|
81
|
-
* and returns retry_later while it is fresh).
|
|
74
|
+
* True when a thrown error smells like LOCAL repo corruption (unreadable
|
|
75
|
+
* objects/refs/index) rather than a transport or logic failure — the signal
|
|
76
|
+
* for a host's mid-sync catch to run `repairRepo()`. NOTE on NotFoundError
|
|
77
|
+
* ambiguity: a transport 404 also surfaces as NotFoundError, but
|
|
78
|
+
* transport.ts's fetchRemoteTip rewrites those to an HttpError(401) BEFORE
|
|
79
|
+
* they can reach this heuristic, so a raw NotFoundError here is a LOCAL
|
|
80
|
+
* ref-resolution failure.
|
|
82
81
|
*/
|
|
83
|
-
export declare function
|
|
84
|
-
minLockAgeMs?: number;
|
|
85
|
-
}): SyncErrorKind | null;
|
|
82
|
+
export declare function isLikelyRepoCorruption(e: unknown): boolean;
|
|
86
83
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
84
|
+
* Classify a repo's structural condition from a RepoHealth snapshot.
|
|
85
|
+
* Returns null for a healthy repo, "stale_lock" when the only problem is a
|
|
86
|
+
* sweepable lock, and "needs_repair" for everything structural — the repair
|
|
87
|
+
* pipeline (repair.ts) handles every structural case in one ordered pass, so
|
|
88
|
+
* finer distinctions buy nothing.
|
|
90
89
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* 2. Clearly transient/transport errors (auth, network) — these are decoded
|
|
96
|
-
* from the THROWN ERROR and win over the remaining structural health
|
|
97
|
-
* flags (detached HEAD, stale lock). Rationale: you cannot repair repo
|
|
98
|
-
* STRUCTURE while you are offline or signed out, and the scary
|
|
99
|
-
* backup+rescue-branch+confirm repair is the wrong first response to a
|
|
100
|
-
* blip — the friendly "reconnect" / "try later" is correct. Once the user
|
|
101
|
-
* is back online, the next sync re-runs preflight (no thrown error) and
|
|
102
|
-
* the structural kind surfaces then (step 3).
|
|
103
|
-
* 3. Structural health flags (detached HEAD, stale lock) — applied when the
|
|
104
|
-
* failure is NOT a transient transport error (e.g. a preflight with no
|
|
105
|
-
* error, or a non-transport error).
|
|
106
|
-
* 4. Remaining error-code/message heuristics (conflicts, corrupt index,
|
|
107
|
-
* unrelated histories, missing objects/ref-store, wrong remote/branch).
|
|
90
|
+
* `minLockAgeMs` gates the stale-lock verdict: at preflight (the default) a
|
|
91
|
+
* younger lock is treated as healthy because a live process may still hold
|
|
92
|
+
* it. Error-path callers pass 0: a lock that just made a sync THROW is worth
|
|
93
|
+
* routing regardless of age (the sweep still defers while it is fresh).
|
|
108
94
|
*/
|
|
109
|
-
export declare function
|
|
95
|
+
export declare function classifyFromHealth(health: RepoHealth, opts?: {
|
|
96
|
+
minLockAgeMs?: number;
|
|
97
|
+
}): RepairNeed | null;
|
|
@@ -12,15 +12,16 @@
|
|
|
12
12
|
* Notes on two health facts:
|
|
13
13
|
* - hasGitDir is true whenever `.git/` EXISTS, even with a missing/corrupt
|
|
14
14
|
* HEAD (a damaged repo is still a repo — see the inline note at the check).
|
|
15
|
-
* - hasStaleLock uses the
|
|
16
|
-
* (findLockCandidates in
|
|
15
|
+
* - hasStaleLock uses the lock sweep's OWN scanner
|
|
16
|
+
* (findLockCandidates in locks.ts) — one implementation, so
|
|
17
17
|
* health and handler can never disagree about which locks exist.
|
|
18
18
|
*
|
|
19
19
|
* All probes are best-effort and throw-free (the caller must never see an
|
|
20
20
|
* exception from a preflight probe).
|
|
21
21
|
*/
|
|
22
22
|
import type { LogData } from "../operation-log.ts";
|
|
23
|
-
import type {
|
|
23
|
+
import type { RepairNeed } from "./classify.ts";
|
|
24
|
+
import type { RepoHealth } from "./types.ts";
|
|
24
25
|
/**
|
|
25
26
|
* Probe the local repository and return a RepoHealth snapshot.
|
|
26
27
|
* Never throws — on any error the relevant flag is set conservatively.
|
|
@@ -31,7 +32,10 @@ import type { RepoHealth, RecoveryContext, SyncErrorKind } from "./types.ts";
|
|
|
31
32
|
* pull step immediately performs the same walk anyway (sync-simplicity
|
|
32
33
|
* mandate: no redundant walks on the hot path).
|
|
33
34
|
*/
|
|
34
|
-
export declare function inspectRepo(ctx:
|
|
35
|
+
export declare function inspectRepo(ctx: {
|
|
36
|
+
repoDir: string;
|
|
37
|
+
source?: import("../../project-source.ts").ProjectSource;
|
|
38
|
+
}, opts?: {
|
|
35
39
|
checkLocalChanges?: boolean;
|
|
36
40
|
}): Promise<RepoHealth>;
|
|
37
41
|
/**
|
|
@@ -39,15 +43,12 @@ export declare function inspectRepo(ctx: Pick<RecoveryContext, "repoDir" | "sour
|
|
|
39
43
|
* its commit, and read its root tree. Throws when any step fails (missing or
|
|
40
44
|
* corrupt object/ref); resolves when the repo is fully readable.
|
|
41
45
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* that check and the repair command's diagnosis step share ONE implementation
|
|
45
|
-
* rather than two copies that could drift. `inspectRepo`'s health flags are
|
|
46
|
+
* Exported so the repair
|
|
47
|
+
* command's diagnosis step and any host check share ONE implementation. `inspectRepo`'s health flags are
|
|
46
48
|
* all filesystem-presence checks (no object is ever read), so they cannot
|
|
47
49
|
* detect object-store corruption on their own — callers that need to catch
|
|
48
|
-
*
|
|
49
|
-
* `
|
|
50
|
-
* through `classifyGitError`.
|
|
50
|
+
* unreadable objects/refs must run this probe and feed a caught error through
|
|
51
|
+
* `isLikelyRepoCorruption` (classify.ts) to decide whether to repairRepo().
|
|
51
52
|
*/
|
|
52
53
|
export declare function verifyRepoReadable(dir: string): Promise<void>;
|
|
53
54
|
/**
|
|
@@ -64,11 +65,11 @@ export declare function isUnbornRepo(repoDir: string): boolean;
|
|
|
64
65
|
* classifyFromHealth returned (a pure mapping — it cannot drift from the
|
|
65
66
|
* classifier's decision order, because it never re-implements it).
|
|
66
67
|
*/
|
|
67
|
-
export declare function preflightStructuralReason(kind:
|
|
68
|
+
export declare function preflightStructuralReason(kind: RepairNeed | null): string;
|
|
68
69
|
/**
|
|
69
70
|
* Build a flat, secret-free record of the preflight decision inputs for the
|
|
70
71
|
* operation log. Every health boolean is recorded (so support can see the FULL
|
|
71
72
|
* picture, not just the one-word kind), plus the opened dir vs repo root, the
|
|
72
73
|
* chosen kind, and the single reason that drove it.
|
|
73
74
|
*/
|
|
74
|
-
export declare function buildPreflightDiagnostics(openedDir: string, repoDir: string, health: RepoHealth, kind:
|
|
75
|
+
export declare function buildPreflightDiagnostics(openedDir: string, repoDir: string, health: RepoHealth, kind: RepairNeed | null): LogData;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface LockCandidate {
|
|
2
|
+
/** Absolute path to the lock file. */
|
|
3
|
+
path: string;
|
|
4
|
+
/** Age in ms relative to `now` (now - mtime). */
|
|
5
|
+
ageMs: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Gather every candidate lock file (top-level + refs/**) with its age.
|
|
9
|
+
* Locks that vanish between discovery and stat are simply dropped (a racing
|
|
10
|
+
* delete is fine to ignore). Never throws.
|
|
11
|
+
*/
|
|
12
|
+
export declare function findLockCandidates(gitDir: string, now: number): Promise<LockCandidate[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Delete every stale lock under `gitDir` — but ONLY when every candidate is
|
|
15
|
+
* stale (one fresh lock defers the whole sweep; a live process may hold it).
|
|
16
|
+
*
|
|
17
|
+
* Returns "clean" (nothing found), "swept" (all removed), or "fresh"
|
|
18
|
+
* (deferred — the caller should retry later).
|
|
19
|
+
*/
|
|
20
|
+
export declare function sweepStaleLocks(gitDir: string, now?: number, minAgeMs?: number): Promise<"clean" | "swept" | "fresh">;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HostCredential, TokenStore } from "../token-store.ts";
|
|
2
|
+
export interface RepairOptions {
|
|
3
|
+
projectDir: string;
|
|
4
|
+
/** Explicit credential; wins over the token store (nuclear re-clone only). */
|
|
5
|
+
credential?: HostCredential;
|
|
6
|
+
/** Host-keyed store used to resolve the credential for the remote's host. */
|
|
7
|
+
tokenStore?: TokenStore;
|
|
8
|
+
authorName?: string;
|
|
9
|
+
authorEmail?: string;
|
|
10
|
+
/** Operation log (same file sync writes to). */
|
|
11
|
+
logFile?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RepairResult {
|
|
14
|
+
status: "repaired" | "retry_later" | "failed";
|
|
15
|
+
/** One author-language sentence describing the outcome. */
|
|
16
|
+
message: string;
|
|
17
|
+
/** Author-language lines describing what was done (for the log/details). */
|
|
18
|
+
actions: string[];
|
|
19
|
+
/** Present when the re-clone ran: on-disk backup of the old `.git`. */
|
|
20
|
+
damagedGitBackupPath?: string;
|
|
21
|
+
/** Suggested delay before retrying (present on "retry_later"). */
|
|
22
|
+
retryAfterMs?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Repair a damaged repository. See the module header for the pipeline and
|
|
26
|
+
* invariants. Never throws for expected outcomes — everything is reported
|
|
27
|
+
* through {@link RepairResult}.
|
|
28
|
+
*/
|
|
29
|
+
export declare function repairRepo(options: RepairOptions): Promise<RepairResult>;
|
|
@@ -1,131 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared types
|
|
2
|
+
* Shared types for the repo health probe + repair pipeline.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* to users. Every user-visible string lives in manual-guidance.ts.
|
|
10
|
-
*/
|
|
11
|
-
import type httpNode from "isomorphic-git/http/node";
|
|
12
|
-
import type { ProjectSource } from "../../project-source.ts";
|
|
13
|
-
import type { ConflictFile } from "../sync.ts";
|
|
14
|
-
import type { HostCredential, TokenStore } from "../token-store.ts";
|
|
15
|
-
/**
|
|
16
|
-
* Every failure mode the recovery subsystem can handle. The classifier
|
|
17
|
-
* (classify.ts) maps a thrown error + repo health facts to one of these.
|
|
18
|
-
*/
|
|
19
|
-
export type SyncErrorKind = "non_fast_forward" | "merge_conflict" | "binary_conflict" | "auth_required" | "network_unavailable" | "insecure_transport" | "detached_head" | "stale_lock" | "corrupt_index" | "missing_git_dir" | "missing_or_corrupt_objects" | "unrelated_histories" | "wrong_remote_or_branch" | "interrupted_rebase" | "interrupted_cherry_pick" | "interrupted_merge" | "unknown";
|
|
20
|
-
/**
|
|
21
|
-
* Machine token for the primary CTA in the guidance dialog. The host switches
|
|
22
|
-
* on this to route the button to the right flow. The human-readable label the
|
|
23
|
-
* button shows lives in `ManualGuidance.recommendedAction` (never this token).
|
|
24
|
-
*/
|
|
25
|
-
export type RecoveryActionKey = "sync" | "reconnect" | "resolve_conflict" | "restore_repo" | "check_connection";
|
|
26
|
-
/**
|
|
27
|
-
* How much irreversible change a repair makes. The confirmation gate shows
|
|
28
|
-
* a plain-language summary appropriate to the risk level.
|
|
29
|
-
*/
|
|
30
|
-
export type RecoveryRisk = "none" | "low" | "medium" | "high";
|
|
31
|
-
/**
|
|
32
|
-
* Jargon-free guidance the host shows when a repair is blocked or fails.
|
|
33
|
-
* No git words, no tokens, no internal paths.
|
|
34
|
-
*/
|
|
35
|
-
export interface ManualGuidance {
|
|
36
|
-
/** One-sentence plain-language summary of what went wrong. */
|
|
37
|
-
userSummary: string;
|
|
38
|
-
/** The single most important action the user should take next. */
|
|
39
|
-
recommendedNextStep: string;
|
|
40
|
-
/** Action label for the primary button in the UI. */
|
|
41
|
-
recommendedAction: string;
|
|
42
|
-
/** Machine token the host switches on to route the primary button's action. */
|
|
43
|
-
recommendedActionKey: RecoveryActionKey;
|
|
44
|
-
/** Optional secondary steps (shown as a list). */
|
|
45
|
-
safeNextSteps?: string[];
|
|
46
|
-
/** Details for a support ticket — may contain technical info. */
|
|
47
|
-
supportDetails?: string;
|
|
48
|
-
/** Path to the backup zip, when one was created before the failure. */
|
|
49
|
-
backupZipPath?: string;
|
|
50
|
-
}
|
|
51
|
-
/** The backup zip created before a risky repair. */
|
|
52
|
-
export interface RecoveryBackup {
|
|
53
|
-
/** Absolute path to the zip file under os.tmpdir()/print-sync-recovery/. */
|
|
54
|
-
zipPath: string;
|
|
55
|
-
/** ISO timestamp the backup was created. */
|
|
56
|
-
createdAt: string;
|
|
57
|
-
/** Files included in the backup (relative paths). */
|
|
58
|
-
entries: string[];
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* What a confirmation gate shows the user before a risky repair.
|
|
62
|
-
* The UI must render this information — never silently start a risky repair.
|
|
63
|
-
*/
|
|
64
|
-
export interface RepairConfirmation {
|
|
65
|
-
repair: SyncErrorKind;
|
|
66
|
-
risk: RecoveryRisk;
|
|
67
|
-
/** Plain-language summary of what the repair will do. */
|
|
68
|
-
summary: string;
|
|
69
|
-
/** Path to the backup zip (already created and verified). */
|
|
70
|
-
backupZipPath: string;
|
|
71
|
-
willChangeLocalFiles: boolean;
|
|
72
|
-
willChangeGitMetadata: boolean;
|
|
73
|
-
willChangeRemote: boolean;
|
|
74
|
-
/** True when the backup alone is enough to undo the repair. */
|
|
75
|
-
canBeUndoneFromBackup: boolean;
|
|
76
|
-
}
|
|
77
|
-
/** Gate the host must satisfy: show the user what will happen and ask. */
|
|
78
|
-
export interface ConfirmationGate {
|
|
79
|
-
confirmRepair(req: RepairConfirmation): Promise<boolean>;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Outcome of a recovery attempt. Each status maps to a host-side action:
|
|
83
|
-
* - recovered → refresh the project; show a success toast.
|
|
84
|
-
* - retry_later → schedule a retry after retryAfterMs; show a soft message.
|
|
85
|
-
* - needs_user → show the conflict chooser / reconnect dialog.
|
|
86
|
-
* - blocked → show guidance and a "what do I do?" panel.
|
|
87
|
-
* - failed_no_changes_made → show guidance; nothing was changed locally or remotely.
|
|
88
|
-
* - failed_backup_available → show guidance + "open backup folder" button.
|
|
89
|
-
*/
|
|
90
|
-
export type RecoveryResult = {
|
|
91
|
-
status: "recovered";
|
|
92
|
-
message: string;
|
|
93
|
-
backupZipPath?: string;
|
|
94
|
-
} | {
|
|
95
|
-
status: "retry_later";
|
|
96
|
-
message: string;
|
|
97
|
-
retryAfterMs: number;
|
|
98
|
-
} | {
|
|
99
|
-
status: "needs_user";
|
|
100
|
-
message: string;
|
|
101
|
-
guidance: ManualGuidance;
|
|
102
|
-
files?: ConflictFile[];
|
|
103
|
-
backupZipPath?: string;
|
|
104
|
-
/**
|
|
105
|
-
* Conflict tip OIDs threaded through to the host so it can call
|
|
106
|
-
* resolveConflicts with the correct local/remote versions after the user
|
|
107
|
-
* chooses. Present on the per-file conflict-chooser paths only.
|
|
108
|
-
*/
|
|
109
|
-
localId?: string;
|
|
110
|
-
remoteId?: string;
|
|
111
|
-
} | {
|
|
112
|
-
status: "blocked";
|
|
113
|
-
message: string;
|
|
114
|
-
guidance: ManualGuidance;
|
|
115
|
-
backupZipPath?: string;
|
|
116
|
-
} | {
|
|
117
|
-
status: "failed_no_changes_made";
|
|
118
|
-
message: string;
|
|
119
|
-
guidance: ManualGuidance;
|
|
120
|
-
} | {
|
|
121
|
-
status: "failed_backup_available";
|
|
122
|
-
message: string;
|
|
123
|
-
backupZipPath: string;
|
|
124
|
-
guidance: ManualGuidance;
|
|
125
|
-
};
|
|
126
|
-
/**
|
|
127
|
-
* Preflight health snapshot of a local repo. Populated by inspectRepo()
|
|
128
|
-
* (inspect.ts) before any recovery decision is made.
|
|
4
|
+
* 2026-08-14 simplification (owner directive): the old handler-dispatch type
|
|
5
|
+
* surface (SyncErrorKind taxonomy, RecoveryContext, RecoveryResult,
|
|
6
|
+
* ManualGuidance, confirmation gates, fault injection) is gone — repair is
|
|
7
|
+
* one automatic pipeline (repair.ts) and needs none of it. What remains is
|
|
8
|
+
* the health snapshot inspect.ts produces.
|
|
129
9
|
*/
|
|
130
10
|
export interface RepoHealth {
|
|
131
11
|
/** True when .git/ exists and looks like a valid git dir. */
|
|
@@ -137,15 +17,13 @@ export interface RepoHealth {
|
|
|
137
17
|
/**
|
|
138
18
|
* True when HEAD (or the ref store) could not even be READ — distinct from
|
|
139
19
|
* a clean detached HEAD, where HEAD resolves fine but points at a commit
|
|
140
|
-
* instead of a branch.
|
|
141
|
-
* missing/corrupt, and routes to `missing_or_corrupt_objects` instead of
|
|
142
|
-
* `detached_head` (see classifyFromHealth).
|
|
20
|
+
* instead of a branch. Routes to the re-clone step of repairRepo.
|
|
143
21
|
*/
|
|
144
22
|
headUnreadable: boolean;
|
|
145
23
|
/**
|
|
146
24
|
* True when a leftover git lock file exists (a previous operation may have
|
|
147
25
|
* died). Detects any of index.lock, HEAD.lock, config.lock, packed-refs.lock,
|
|
148
|
-
* or a per-ref refs/**\/*.lock — the same set the
|
|
26
|
+
* or a per-ref refs/**\/*.lock — the same set the lock sweep scans.
|
|
149
27
|
*/
|
|
150
28
|
hasStaleLock: boolean;
|
|
151
29
|
/** How old the YOUNGEST detected lock file is in milliseconds, when present. */
|
|
@@ -159,77 +37,3 @@ export interface RepoHealth {
|
|
|
159
37
|
/** True when the working tree has uncommitted changes. */
|
|
160
38
|
hasLocalChanges: boolean;
|
|
161
39
|
}
|
|
162
|
-
/**
|
|
163
|
-
* Named fault injection points every risky repair MUST call via
|
|
164
|
-
* `ctx.faults?.before(point)`. Throwing from this hook simulates a failure
|
|
165
|
-
* at that exact point so failsafe invariants can be asserted.
|
|
166
|
-
*/
|
|
167
|
-
export type FaultPoint = "backup_create" | "backup_verify" | "after_backup_before_repair" | "fetch" | "merge" | "checkout_branch" | "abort_interrupted_operation" | "remove_operation_state" | "create_recovery_branch" | "commit_recovery_snapshot" | "remove_index_lock" | "remove_index" | "rebuild_index" | "clone_temp_repo" | "replace_git_dir" | "push" | "write_conflict_snapshot";
|
|
168
|
-
export interface FaultInjector {
|
|
169
|
-
before(point: FaultPoint): Promise<void>;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Everything a recover-<x>.ts handler needs. Extends SyncProjectOptions with
|
|
173
|
-
* the repo-level facts that the dispatcher resolves once and passes down.
|
|
174
|
-
*/
|
|
175
|
-
export interface RecoveryContext {
|
|
176
|
-
/** The project directory the user opened (may be a repo subfolder). */
|
|
177
|
-
projectDir: string;
|
|
178
|
-
/** Absolute path to the git repository root. */
|
|
179
|
-
repoDir: string;
|
|
180
|
-
/**
|
|
181
|
-
* The projectDir's classification, resolved ONCE by buildRecoveryContext and
|
|
182
|
-
* threaded to every consumer (inspectRepo, diagnoseProjectRemote) so the
|
|
183
|
-
* recovery path never re-walks parent dirs to re-classify the same folder.
|
|
184
|
-
* `null` records that classification failed; omitted means "not resolved" —
|
|
185
|
-
* consumers classify themselves in both cases.
|
|
186
|
-
*/
|
|
187
|
-
source?: ProjectSource | null;
|
|
188
|
-
/** Current branch name (may be empty on detached HEAD). */
|
|
189
|
-
branch: string;
|
|
190
|
-
/** Sanitized HTTPS remote URL (no embedded credentials). */
|
|
191
|
-
remoteUrl?: string;
|
|
192
|
-
/** Short identifier for the repo used in backup paths. */
|
|
193
|
-
repoSlug: string;
|
|
194
|
-
/** Resolved credential for the remote host. */
|
|
195
|
-
credential?: HostCredential;
|
|
196
|
-
/** Host-keyed credential store (for clearing stale tokens). */
|
|
197
|
-
tokenStore?: TokenStore;
|
|
198
|
-
/** Injectable HTTP transport (real or test mock). */
|
|
199
|
-
httpClient?: typeof httpNode;
|
|
200
|
-
/** The user's display name for snapshot commits. */
|
|
201
|
-
authorName?: string;
|
|
202
|
-
/** The user's email for snapshot commits. Paired with `authorName` — a
|
|
203
|
-
* recovery commit must carry the SAME identity a normal snapshot does. */
|
|
204
|
-
authorEmail?: string;
|
|
205
|
-
/** Gate the host must satisfy before a risky repair starts. */
|
|
206
|
-
confirmation: ConfirmationGate;
|
|
207
|
-
/** Fault injection hooks (tests only — omit in production). */
|
|
208
|
-
faults?: FaultInjector;
|
|
209
|
-
/** Clock override (tests only). Returns epoch ms. */
|
|
210
|
-
now?: () => number;
|
|
211
|
-
/**
|
|
212
|
-
* Optional path to a log file for debugging. When set, each recovery step
|
|
213
|
-
* (backup, fetch, merge, checkout, etc.) is appended as a timestamped line.
|
|
214
|
-
* Never logs secrets — only repo slug, branch, short OIDs, outcome status.
|
|
215
|
-
*/
|
|
216
|
-
logFile?: string;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* The signature every recover-<x>.ts module MUST export.
|
|
220
|
-
* The dispatcher (dispatch.ts) maps SyncErrorKind → RecoverFn.
|
|
221
|
-
*/
|
|
222
|
-
export type RecoverFn = (ctx: RecoveryContext, error?: unknown) => Promise<RecoveryResult>;
|
|
223
|
-
/**
|
|
224
|
-
* Optional per-kind precondition probe a recover-<x>.ts module MAY export as
|
|
225
|
-
* `stillApplies`. The dispatcher calls it — for serializeRepo:true kinds only,
|
|
226
|
-
* INSIDE withRepoLock, immediately before invoking the handler body — to catch
|
|
227
|
-
* the case where the condition that justified this kind was already resolved
|
|
228
|
-
* (externally, or by a previous attempt) between classification and dispatch.
|
|
229
|
-
* Returning false means there is nothing left to repair; the dispatcher
|
|
230
|
-
* returns a benign no-op RecoveryResult without running the handler at all.
|
|
231
|
-
* Must be read-only and throw-free from the caller's perspective (probes
|
|
232
|
-
* should catch their own errors and resolve `true` — "assume it still
|
|
233
|
-
* applies" — rather than reject, so a probe failure never blocks recovery).
|
|
234
|
-
*/
|
|
235
|
-
export type StillAppliesFn = (ctx: RecoveryContext) => Promise<boolean>;
|
|
@@ -11,8 +11,8 @@ export declare const MSG_SYNCED_MERGED = "Your changes are online, combined with
|
|
|
11
11
|
export declare const MSG_OFFLINE = "Your changes are saved on this computer. gutterpress couldn't reach the online repository \u2014 try syncing again when you're back online.";
|
|
12
12
|
export declare const MSG_AUTH = "The online repository didn't accept the saved connection. Reconnect and try again.";
|
|
13
13
|
export declare const MSG_INSECURE_TRANSPORT = "This project's online address isn't secure, so the saved connection wasn't sent \u2014 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.";
|
|
14
|
-
export declare const
|
|
15
|
-
export declare const
|
|
14
|
+
export declare const MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer \u2014 try Sync again in a moment.";
|
|
15
|
+
export declare const MSG_UNRELATED = "The online address points at a different project's files, so the two can't be combined. Check the project's online address.";
|
|
16
16
|
export declare const MSG_NO_REMOTE = "This project isn't connected to an online repository yet.";
|
|
17
17
|
export declare const MSG_SSH_REMOTE = "This project's online address uses SSH (git@\u2026), which gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
|
|
18
18
|
export declare const MSG_NO_BRANCH = "This project's version history isn't on a named branch, so it can't be synced right now.";
|
|
@@ -21,6 +21,5 @@ export declare const MSG_PULLED_MERGED = "The latest online changes were combine
|
|
|
21
21
|
export declare const MSG_PULL_UP_TO_DATE = "You already have the latest online changes.";
|
|
22
22
|
export declare const MSG_PUSH_UP_TO_DATE = "There's nothing new to send \u2014 everything is already online.";
|
|
23
23
|
export declare const MSG_PULL_FIRST = "The online copy has changes you don't have yet. Get the latest changes first, then send yours.";
|
|
24
|
-
export declare const MSG_EXPIRED_CHOICES = "Those combine choices have expired. Please run Sync again.";
|
|
25
24
|
/** Message recorded on the automatic pre-sync snapshot (D5 invariant). */
|
|
26
25
|
export declare const SYNC_SNAPSHOT_MESSAGE = "Snapshot before syncing";
|