gutterpress 0.10.0 → 0.10.1-beta.1
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-7a37g51p.js} +5 -5
- package/dist/{build-6r502chw.js → build-kdaet3pd.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-zfcryxg8.js → cli-ak1tagkr.js} +94 -57
- package/dist/{cli-eq5naw4m.js → cli-df7gcyns.js} +370 -1249
- package/dist/{cli-149edp6b.js → cli-dx6sxj2t.js} +1 -1
- package/dist/{cli-cqtggsng.js → cli-mcb484g3.js} +2 -47
- package/dist/{cli-k1065rkg.js → cli-w692g6dx.js} +75 -29
- package/dist/cli.js +14 -15
- package/dist/{doctor-akvxbtjb.js → doctor-vajd5h45.js} +2 -2
- package/dist/{engine-b159tbns.js → engine-0nt35svz.js} +2 -2
- package/dist/{engine-ft4cr3ep.js → engine-gkcqdye5.js} +1 -1
- package/dist/{gutterpress-viewer-te8g5grx.js → gutterpress-viewer-tma1qwz2.js} +57 -15
- package/dist/{index-ge7q9xj3.js → index-6g72h2n1.js} +771 -1482
- package/dist/{index-ycpvr0am.js → index-b2rqw41a.js} +1 -1
- package/dist/{index-mdefp0y5.js → index-dtrftayr.js} +75 -27
- package/dist/{index-wq3r5pj7.js → index-kvgq4q3q.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-p6a4bkvh.js} +5 -5
- package/dist/{new-hvq0x91q.js → new-4cdzfbsj.js} +5 -5
- package/dist/{plugin-pssmk0dx.js → plugin-a89tqspy.js} +5 -5
- package/dist/{preflight-4j00yd0g.js → preflight-r4y3f8f9.js} +5 -5
- package/dist/{preview-d4s6gk6p.js → preview-k8n2kew7.js} +5 -5
- package/dist/{publish-tkc26en7.js → publish-jnhjp2vd.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-prnybpkx.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
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import httpNode from "isomorphic-git/http/node";
|
|
2
2
|
import { type HostCredential, type TokenStore } from "./token-store.ts";
|
|
3
|
-
import type { OperationLogger } from "./operation-log.ts";
|
|
4
3
|
import type { GitCache, RemoteTransport } from "./sync-types.ts";
|
|
5
4
|
/**
|
|
6
5
|
* The git repo directory for a project dir. A project IS its git repo, so this
|
|
@@ -17,6 +16,19 @@ export declare function repoDirFor(projectDir: string): Promise<string>;
|
|
|
17
16
|
* http carries no network exposure, so it stays allowed.
|
|
18
17
|
*/
|
|
19
18
|
export declare function isCredentialTransmissionSafe(url: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Thrown by {@link onAuthFor} when a stored credential EXISTS but the remote
|
|
21
|
+
* URL fails {@link isCredentialTransmissionSafe} (non-loopback http). Loud and
|
|
22
|
+
* typed on purpose: the old behavior (silently withholding the credential)
|
|
23
|
+
* surfaced as a 401 → "auth" → "reconnect" loop. The `code` string is the
|
|
24
|
+
* STABLE contract (matchable across dynamic-import boundaries).
|
|
25
|
+
*/
|
|
26
|
+
export declare class InsecureTransportError extends Error {
|
|
27
|
+
readonly code = "InsecureTransport";
|
|
28
|
+
constructor();
|
|
29
|
+
}
|
|
30
|
+
/** Type guard for {@link InsecureTransportError} (matches on the stable code). */
|
|
31
|
+
export declare function isInsecureTransportError(e: unknown): e is InsecureTransportError;
|
|
20
32
|
export declare function onAuthFor(credential: HostCredential | undefined): {
|
|
21
33
|
onAuth?: undefined;
|
|
22
34
|
} | {
|
|
@@ -35,13 +47,19 @@ export declare function resolveTransport(dir: string, options: {
|
|
|
35
47
|
tokenStore?: TokenStore;
|
|
36
48
|
}): Promise<RemoteTransport>;
|
|
37
49
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
* Decode a thrown transport error into the outcome it maps to, or null when it
|
|
51
|
+
* is not a transport failure at all. The single source of truth for transport
|
|
52
|
+
* error decoding — sync.ts and clone.ts consume it rather than keeping
|
|
53
|
+
* parallel copies.
|
|
54
|
+
*/
|
|
55
|
+
export declare function classifyTransportFailure(e: unknown): "auth_required" | "network_unavailable" | "insecure_transport" | null;
|
|
56
|
+
/**
|
|
57
|
+
* The failure arms of {@link SyncOutcome}. Decoding delegates to
|
|
58
|
+
* {@link classifyTransportFailure}: auth_required → "auth",
|
|
59
|
+
* network_unavailable → "offline", insecure_transport → "error" with its
|
|
60
|
+
* dedicated message (NEVER "auth" — reconnecting can't fix an http:// address,
|
|
61
|
+
* and the auth recovery path deletes the stored credential), anything else →
|
|
62
|
+
* the generic "error" arm.
|
|
45
63
|
*/
|
|
46
64
|
export declare function failureOutcome(e: unknown, snapshotId?: string): {
|
|
47
65
|
status: "auth" | "offline" | "error";
|
|
@@ -51,7 +69,7 @@ export declare function failureOutcome(e: unknown, snapshotId?: string): {
|
|
|
51
69
|
/** Friendly setup-problem message for the expected gate errors, else null. */
|
|
52
70
|
export declare function setupErrorMessage(e: unknown): string | null;
|
|
53
71
|
/**
|
|
54
|
-
* Snapshot-first step
|
|
72
|
+
* Snapshot-first step used by syncProject
|
|
55
73
|
* (ADR 0006 D5): commit any unsaved work in the WHOLE repo BEFORE any network
|
|
56
74
|
* or merge step, so a forced post-merge checkout can never discard it. The
|
|
57
75
|
* working-tree check runs lazily at action time on the caller's function-scoped
|
|
@@ -65,44 +83,28 @@ export declare function snapshotBeforeAction(args: {
|
|
|
65
83
|
authorEmail?: string;
|
|
66
84
|
cache: GitCache;
|
|
67
85
|
}): Promise<string | undefined>;
|
|
68
|
-
/**
|
|
69
|
-
* Structural preflight — never touch the tree of a damaged repo. An interrupted
|
|
70
|
-
* merge/rebase/cherry-pick, detached HEAD, stale lock, or missing `.git` must be
|
|
71
|
-
* REPAIRED before any sync work: snapshot-first would otherwise commit whatever
|
|
72
|
-
* is on disk (e.g. the literal conflict markers a half-done native-git merge
|
|
73
|
-
* leaves in tracked files) and push it to every collaborator. Throwing the
|
|
74
|
-
* typed error routes the caller through the recover() path.
|
|
75
|
-
* `checkLocalChanges: false` — only the structural flags matter here.
|
|
76
|
-
*
|
|
77
|
-
* Runs INSIDE the caller's repo lock. Shared by pullChanges and pushChanges.
|
|
78
|
-
*/
|
|
79
|
-
export declare function assertNoStructuralDamage(projectDir: string, logger: OperationLogger): Promise<void>;
|
|
80
86
|
export declare function currentBranchOrThrow(dir: string): Promise<string>;
|
|
81
87
|
/**
|
|
82
|
-
* Run `fn` (a fetch that moves remote-tracking
|
|
83
|
-
* (deep-analysis R15): isomorphic-git updates refs/remotes/<remote
|
|
84
|
-
* the ref advertisement BEFORE collecting the packfile, so an abort
|
|
85
|
-
* mid-transfer (e.g. the defaultGitHttp idle timeout) leaves
|
|
86
|
-
* at
|
|
88
|
+
* Run `fn` (a fetch that moves ONE remote-tracking ref) with a rollback guard
|
|
89
|
+
* (deep-analysis R15): isomorphic-git updates `refs/remotes/<remote>/<branch>`
|
|
90
|
+
* from the ref advertisement BEFORE collecting the packfile, so an abort
|
|
91
|
+
* mid-transfer (e.g. the defaultGitHttp idle timeout) leaves the ref pointing
|
|
92
|
+
* at an oid with no local object. Such a dangling ref poisons the next fetch —
|
|
87
93
|
* zero `have`s → the server streams the ENTIRE repository (the OOM
|
|
88
|
-
* fetchRemoteTip's `ref` choice exists to prevent) — and resolving it reports
|
|
94
|
+
* `fetchRemoteTip`'s `ref` choice exists to prevent) — and resolving it reports
|
|
89
95
|
* missing-object "corruption" on a never-corrupt repo.
|
|
90
96
|
*
|
|
91
|
-
* `
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
+
* If `fn` throws and the ref moved to an oid whose object is MISSING locally,
|
|
98
|
+
* it is restored to its previous oid (or DELETED if it did not exist before).
|
|
99
|
+
* A ref whose object DID land is kept — the pack made it. On success no ref is
|
|
100
|
+
* touched.
|
|
101
|
+
*
|
|
102
|
+
* Every read here is best-effort and never masks `fn`'s error: a damaged ref
|
|
103
|
+
* store must not block the guarded fetch, because the recovery handlers run on
|
|
104
|
+
* exactly such repos and skipping `fn` would skip the repair itself. An
|
|
105
|
+
* unreadable pre-scan simply degrades to the delete-if-dangling arm.
|
|
97
106
|
*/
|
|
98
|
-
export declare function guardRefs<T>(dir: string, listRefs: () => Promise<string[]>, cache: GitCache, fn: () => Promise<T>): Promise<T>;
|
|
99
|
-
/** Single-ref form of {@link guardRefs} — guards one remote-tracking ref. */
|
|
100
107
|
export declare function guardTrackingRef<T>(dir: string, ref: string, cache: GitCache, fn: () => Promise<T>): Promise<T>;
|
|
101
|
-
/**
|
|
102
|
-
* Remote-wide form of {@link guardRefs} for a fetch that may move or create
|
|
103
|
-
* ANY refs/remotes/<remote>/* ref (e.g. `singleBranch: false`).
|
|
104
|
-
*/
|
|
105
|
-
export declare function guardRemoteRefs<T>(dir: string, remote: string, cache: GitCache, fn: () => Promise<T>): Promise<T>;
|
|
106
108
|
/**
|
|
107
109
|
* Fetch the tracked branch's online tip. Returns `null` when the online
|
|
108
110
|
* repository has no such branch yet (a freshly created empty repo).
|
|
@@ -38,6 +38,18 @@ export interface SnapshotOptions {
|
|
|
38
38
|
repoRoot?: string;
|
|
39
39
|
/** Optional log file for debugging snapshot operations. */
|
|
40
40
|
logFile?: string;
|
|
41
|
+
/**
|
|
42
|
+
* The CALLER's object cache, when the snapshot is one step of a longer
|
|
43
|
+
* locked operation (sync). Committing rewrites `.git/index`, and
|
|
44
|
+
* isomorphic-git's index cache invalidates on a stat comparison that can
|
|
45
|
+
* miss a same-second rewrite of the same size — so a caller that keeps
|
|
46
|
+
* using its own cache afterwards would keep reading the PRE-snapshot index.
|
|
47
|
+
* That is not cosmetic: `git.checkout` derives STAGE from it, and a stale
|
|
48
|
+
* STAGE makes an unmodified file look locally modified. Pass the cache and
|
|
49
|
+
* the snapshot's own `git.add`/`git.commit` refresh it in place. Omit it
|
|
50
|
+
* for standalone snapshots — they get a private cache, released on return.
|
|
51
|
+
*/
|
|
52
|
+
cache?: GitCache;
|
|
41
53
|
}
|
|
42
54
|
/**
|
|
43
55
|
* The git repo directory a project's operations run against. A project IS its
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MANIFEST_FILENAMES,
|
|
3
3
|
runLint
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-df7gcyns.js";
|
|
5
5
|
import {
|
|
6
6
|
log
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-mcb484g3.js";
|
|
8
8
|
import {
|
|
9
9
|
UsageError,
|
|
10
10
|
rejectExtraPositionals,
|
|
11
11
|
rejectUnknownFlags
|
|
12
|
-
} from "./cli-
|
|
13
|
-
import"./cli-
|
|
12
|
+
} from "./cli-dx6sxj2t.js";
|
|
13
|
+
import"./cli-ak1tagkr.js";
|
|
14
14
|
import"./cli-c41yr7he.js";
|
|
15
15
|
import {
|
|
16
16
|
EXIT_CODES
|
|
17
17
|
} from "./cli-46ycxe6r.js";
|
|
18
|
-
import"./cli-
|
|
18
|
+
import"./cli-w692g6dx.js";
|
|
19
19
|
import"./cli-v5mp7a6q.js";
|
|
20
20
|
import"./cli-37x76zdn.js";
|
|
21
21
|
|
|
@@ -5,23 +5,23 @@ import {
|
|
|
5
5
|
TARGETS,
|
|
6
6
|
TARGET_IDS,
|
|
7
7
|
scaffoldProject
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-df7gcyns.js";
|
|
9
9
|
import {
|
|
10
10
|
resolveGhostscript
|
|
11
|
-
} from "./cli-
|
|
11
|
+
} from "./cli-mcb484g3.js";
|
|
12
12
|
import {
|
|
13
13
|
UsageError,
|
|
14
14
|
rejectExtraPositionals,
|
|
15
15
|
rejectUnknownFlags
|
|
16
|
-
} from "./cli-
|
|
17
|
-
import"./cli-
|
|
16
|
+
} from "./cli-dx6sxj2t.js";
|
|
17
|
+
import"./cli-ak1tagkr.js";
|
|
18
18
|
import {
|
|
19
19
|
isToolAvailable
|
|
20
20
|
} from "./cli-c41yr7he.js";
|
|
21
21
|
import {
|
|
22
22
|
EXIT_CODES
|
|
23
23
|
} from "./cli-46ycxe6r.js";
|
|
24
|
-
import"./cli-
|
|
24
|
+
import"./cli-w692g6dx.js";
|
|
25
25
|
import"./cli-v5mp7a6q.js";
|
|
26
26
|
import"./cli-37x76zdn.js";
|
|
27
27
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
addNpmPlugin,
|
|
3
3
|
parseNpmPluginSpec
|
|
4
|
-
} from "./cli-
|
|
5
|
-
import"./cli-
|
|
4
|
+
} from "./cli-df7gcyns.js";
|
|
5
|
+
import"./cli-mcb484g3.js";
|
|
6
6
|
import {
|
|
7
7
|
UsageError,
|
|
8
8
|
rejectExtraPositionals,
|
|
9
9
|
rejectUnknownFlags
|
|
10
|
-
} from "./cli-
|
|
11
|
-
import"./cli-
|
|
10
|
+
} from "./cli-dx6sxj2t.js";
|
|
11
|
+
import"./cli-ak1tagkr.js";
|
|
12
12
|
import"./cli-c41yr7he.js";
|
|
13
13
|
import {
|
|
14
14
|
EXIT_CODES
|
|
15
15
|
} from "./cli-46ycxe6r.js";
|
|
16
|
-
import"./cli-
|
|
16
|
+
import"./cli-w692g6dx.js";
|
|
17
17
|
import"./cli-v5mp7a6q.js";
|
|
18
18
|
import"./cli-37x76zdn.js";
|
|
19
19
|
|
|
@@ -2,21 +2,21 @@ import {
|
|
|
2
2
|
executeValidation,
|
|
3
3
|
publishTargetFor,
|
|
4
4
|
reportMissingTools
|
|
5
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-df7gcyns.js";
|
|
6
6
|
import {
|
|
7
7
|
log
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-mcb484g3.js";
|
|
9
9
|
import {
|
|
10
10
|
UsageError,
|
|
11
11
|
rejectExtraPositionals,
|
|
12
12
|
rejectUnknownFlags
|
|
13
|
-
} from "./cli-
|
|
14
|
-
import"./cli-
|
|
13
|
+
} from "./cli-dx6sxj2t.js";
|
|
14
|
+
import"./cli-ak1tagkr.js";
|
|
15
15
|
import"./cli-c41yr7he.js";
|
|
16
16
|
import {
|
|
17
17
|
EXIT_CODES
|
|
18
18
|
} from "./cli-46ycxe6r.js";
|
|
19
|
-
import"./cli-
|
|
19
|
+
import"./cli-w692g6dx.js";
|
|
20
20
|
import"./cli-v5mp7a6q.js";
|
|
21
21
|
import"./cli-37x76zdn.js";
|
|
22
22
|
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
runBuild,
|
|
7
7
|
splitOutPath,
|
|
8
8
|
startPreviewServer
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-df7gcyns.js";
|
|
10
10
|
import {
|
|
11
11
|
log
|
|
12
|
-
} from "./cli-
|
|
12
|
+
} from "./cli-mcb484g3.js";
|
|
13
13
|
import {
|
|
14
14
|
UsageError,
|
|
15
15
|
parseEngine,
|
|
@@ -18,13 +18,13 @@ import {
|
|
|
18
18
|
rejectExtraPositionals,
|
|
19
19
|
rejectUnknownFlags,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
22
|
-
import"./cli-
|
|
21
|
+
} from "./cli-dx6sxj2t.js";
|
|
22
|
+
import"./cli-ak1tagkr.js";
|
|
23
23
|
import"./cli-c41yr7he.js";
|
|
24
24
|
import {
|
|
25
25
|
BuildError
|
|
26
26
|
} from "./cli-46ycxe6r.js";
|
|
27
|
-
import"./cli-
|
|
27
|
+
import"./cli-w692g6dx.js";
|
|
28
28
|
import"./cli-v5mp7a6q.js";
|
|
29
29
|
import"./cli-37x76zdn.js";
|
|
30
30
|
|
|
@@ -7,22 +7,22 @@ import {
|
|
|
7
7
|
publishCredentialKey,
|
|
8
8
|
publishProviderFor,
|
|
9
9
|
runPublish
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-df7gcyns.js";
|
|
11
11
|
import {
|
|
12
12
|
FileTokenStore,
|
|
13
13
|
log
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-mcb484g3.js";
|
|
15
15
|
import {
|
|
16
16
|
UsageError,
|
|
17
17
|
rejectExtraPositionals,
|
|
18
18
|
rejectUnknownFlags
|
|
19
|
-
} from "./cli-
|
|
20
|
-
import"./cli-
|
|
19
|
+
} from "./cli-dx6sxj2t.js";
|
|
20
|
+
import"./cli-ak1tagkr.js";
|
|
21
21
|
import"./cli-c41yr7he.js";
|
|
22
22
|
import {
|
|
23
23
|
EXIT_CODES
|
|
24
24
|
} from "./cli-46ycxe6r.js";
|
|
25
|
-
import"./cli-
|
|
25
|
+
import"./cli-w692g6dx.js";
|
|
26
26
|
import"./cli-v5mp7a6q.js";
|
|
27
27
|
import"./cli-37x76zdn.js";
|
|
28
28
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeAndReport
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-df7gcyns.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-mcb484g3.js";
|
|
7
7
|
import {
|
|
8
8
|
UsageError,
|
|
9
9
|
rejectExtraPositionals,
|
|
10
10
|
rejectUnknownFlags
|
|
11
|
-
} from "./cli-
|
|
12
|
-
import"./cli-
|
|
11
|
+
} from "./cli-dx6sxj2t.js";
|
|
12
|
+
import"./cli-ak1tagkr.js";
|
|
13
13
|
import"./cli-c41yr7he.js";
|
|
14
14
|
import {
|
|
15
15
|
EXIT_CODES
|
|
16
16
|
} from "./cli-46ycxe6r.js";
|
|
17
|
-
import"./cli-
|
|
17
|
+
import"./cli-w692g6dx.js";
|
|
18
18
|
import"./cli-v5mp7a6q.js";
|
|
19
19
|
import"./cli-37x76zdn.js";
|
|
20
20
|
|
package/package.json
CHANGED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* app-heartbeat.ts — detect a running Gutterpress app before a CLI repair
|
|
3
|
-
* mutates the repo.
|
|
4
|
-
*
|
|
5
|
-
* WHY: `gutterpress repair` and the desktop's own recovery path both call into
|
|
6
|
-
* the recovery subsystem, but each runs in its own OS process. The per-repo
|
|
7
|
-
* FIFO lock (source-provider.ts) only serializes operations WITHIN a process,
|
|
8
|
-
* so `gutterpress repair` run from a terminal while the desktop has the same
|
|
9
|
-
* project open can race a live sync/snapshot. Rather than build a
|
|
10
|
-
* cross-process lock manager, the desktop leaves a small liveness marker
|
|
11
|
-
* behind while a project is open, and `repair` checks it before mutating.
|
|
12
|
-
*
|
|
13
|
-
* The marker lives at `<repoDir>/.git/gutterpress-app-heartbeat` (NOT in
|
|
14
|
-
* userData) so `repair`, given only a repo directory, can find it without any
|
|
15
|
-
* knowledge of the desktop's install. It is deliberately NOT a lock:
|
|
16
|
-
* - It never blocks the desktop itself, or any other repair run with --force.
|
|
17
|
-
* - It carries no locking semantics — just "an app touched this repo
|
|
18
|
-
* recently, maybe check before you assume it's idle".
|
|
19
|
-
* - It is written best-effort (a failed write must never surprise the
|
|
20
|
-
* author with a crash) and read best-effort (a missing/corrupt file
|
|
21
|
-
* means "no app appears to have this open", not an error).
|
|
22
|
-
*
|
|
23
|
-
* Two invariants that keep this file inert to the rest of the recovery
|
|
24
|
-
* subsystem:
|
|
25
|
-
* - Filename does not match any pattern `findLockCandidates` scans
|
|
26
|
-
* (recover-stale-lock.ts: fixed top-level `*.lock` names + `refs/**`) —
|
|
27
|
-
* so stale-lock recovery can never see or remove it.
|
|
28
|
-
* - It lives under `.git`, which `hasPendingChanges`/`listWorkdirChanges`
|
|
29
|
-
* (source-provider.ts) always ignores (`git.isIgnored` treats a `.git`
|
|
30
|
-
* path segment as ignored unconditionally) — so it can never look like an
|
|
31
|
-
* uncommitted author change.
|
|
32
|
-
*/
|
|
33
|
-
/**
|
|
34
|
-
* Fallback freshness window used only when a heartbeat carries no `ttlMs`
|
|
35
|
-
* (an older writer, or one with no cadence to report). Prefer a
|
|
36
|
-
* cadence-derived TTL — see {@link heartbeatTtlMs} — since the actual
|
|
37
|
-
* refresh cadence ranges 1 min–24 h (AUTO_SYNC_MIN/MAX_MINUTES,
|
|
38
|
-
* host-policy.ts) and a fixed 2-minute window reads a live app as "closed"
|
|
39
|
-
* for most of any longer cadence.
|
|
40
|
-
*/
|
|
41
|
-
export declare const APP_HEARTBEAT_FRESH_MS: number;
|
|
42
|
-
export interface AppHeartbeat {
|
|
43
|
-
/** PID of the process that wrote the heartbeat (diagnostic only — never
|
|
44
|
-
* used to `kill`/signal anything). */
|
|
45
|
-
pid: number;
|
|
46
|
-
/** Epoch ms the heartbeat was last (re)written. */
|
|
47
|
-
timestamp: number;
|
|
48
|
-
/**
|
|
49
|
-
* Freshness window (ms) the writer says applies to THIS heartbeat, derived
|
|
50
|
-
* from its own refresh cadence (see {@link heartbeatTtlMs}). When present,
|
|
51
|
-
* `isAppHeartbeatFresh` honors it instead of its `maxAgeMs` fallback param —
|
|
52
|
-
* the writer knows how often it actually refreshes; the reader doesn't.
|
|
53
|
-
*/
|
|
54
|
-
ttlMs?: number;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* The freshness window a heartbeat writer should stamp for a given refresh
|
|
58
|
-
* cadence (the actual interval this writer refreshes the marker on — e.g.
|
|
59
|
-
* `autoSyncDelayMs(settings.versionHistory)`). `null` (feature disabled, no
|
|
60
|
-
* periodic refresh at all) falls back to {@link APP_HEARTBEAT_FRESH_MS}.
|
|
61
|
-
*
|
|
62
|
-
* Standard heartbeat rule: TTL should comfortably exceed the refresh period
|
|
63
|
-
* so one missed/delayed tick doesn't read as "closed". Exported so both the
|
|
64
|
-
* desktop (the writer, which knows its own cadence) and tests can compute it
|
|
65
|
-
* consistently.
|
|
66
|
-
*/
|
|
67
|
-
export declare function heartbeatTtlMs(periodicMs: number | null): number;
|
|
68
|
-
/** Absolute path to the heartbeat marker for a repo. */
|
|
69
|
-
export declare function appHeartbeatPath(repoDir: string): string;
|
|
70
|
-
/**
|
|
71
|
-
* Write/refresh the heartbeat marker. Best-effort: a failed write (missing
|
|
72
|
-
* `.git` dir, read-only filesystem, …) is swallowed — it must never surface
|
|
73
|
-
* as an error to the author, since it is only ever a background liveness
|
|
74
|
-
* signal, not a required operation.
|
|
75
|
-
*
|
|
76
|
-
* `ttlMs`, when supplied, should be {@link heartbeatTtlMs} applied to this
|
|
77
|
-
* writer's OWN actual refresh cadence — it is stamped into the marker so a
|
|
78
|
-
* reader with no knowledge of that cadence still judges freshness correctly.
|
|
79
|
-
* Omit it (or pass `undefined`) when the caller has no cadence to report; the
|
|
80
|
-
* reader then falls back to its own `maxAgeMs` default.
|
|
81
|
-
*/
|
|
82
|
-
export declare function writeAppHeartbeat(repoDir: string, now?: number, pid?: number, ttlMs?: number): Promise<void>;
|
|
83
|
-
/**
|
|
84
|
-
* Remove the heartbeat marker (project close / app quit). Best-effort: an
|
|
85
|
-
* already-missing or unreadable file is not an error.
|
|
86
|
-
*/
|
|
87
|
-
export declare function removeAppHeartbeat(repoDir: string): Promise<void>;
|
|
88
|
-
/**
|
|
89
|
-
* Read + parse the heartbeat marker. Returns `null` when absent, unreadable,
|
|
90
|
-
* or corrupt (never throws — a damaged heartbeat is just "no signal").
|
|
91
|
-
*/
|
|
92
|
-
export declare function readAppHeartbeat(repoDir: string): Promise<AppHeartbeat | null>;
|
|
93
|
-
/**
|
|
94
|
-
* True when a FRESH heartbeat exists for this repo — i.e. the gutterpress app
|
|
95
|
-
* appears to have this project open right now. Absent/stale/corrupt all read
|
|
96
|
-
* as "not open" (fail open: `repair` should not block on ambiguous signal).
|
|
97
|
-
*
|
|
98
|
-
* Freshness window: the heartbeat's own stamped `ttlMs` (derived from the
|
|
99
|
-
* writer's actual refresh cadence — see {@link heartbeatTtlMs}) wins when
|
|
100
|
-
* present; `maxAgeMs` is only a fallback for a heartbeat with none.
|
|
101
|
-
*/
|
|
102
|
-
export declare function isAppHeartbeatFresh(repoDir: string, now?: number, maxAgeMs?: number): Promise<boolean>;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/** Read one version's exact bytes by BLOB oid. */
|
|
2
|
-
export declare function readImageVersion(options: {
|
|
3
|
-
projectDir: string;
|
|
4
|
-
oid: string;
|
|
5
|
-
}): Promise<Uint8Array>;
|
|
6
|
-
/**
|
|
7
|
-
* Keep a specific version of a clashing image: write the blob's exact bytes
|
|
8
|
-
* to the file and snapshot. Serialized on the per-repo lock like every other
|
|
9
|
-
* mutating operation.
|
|
10
|
-
*/
|
|
11
|
-
export declare function keepImageVersion(options: {
|
|
12
|
-
projectDir: string;
|
|
13
|
-
path: string;
|
|
14
|
-
oid: string;
|
|
15
|
-
authorName?: string;
|
|
16
|
-
authorEmail?: string;
|
|
17
|
-
}): Promise<void>;
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error/health classification for sync + repair — the SINGLE source of truth.
|
|
3
|
-
*
|
|
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:
|
|
8
|
-
*
|
|
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
|
-
*
|
|
15
|
-
* The transport decoders (auth/offline/insecure) and the merge/push guards
|
|
16
|
-
* used by sync.ts remain here unchanged — outcome mapping, not repair.
|
|
17
|
-
* This module is pure — no I/O, no side effects.
|
|
18
|
-
*/
|
|
19
|
-
import type { RepoHealth } from "./types.ts";
|
|
20
|
-
/**
|
|
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").
|
|
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";
|
|
29
|
-
/**
|
|
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.
|
|
35
|
-
*/
|
|
36
|
-
export declare class RepoNeedsRecoveryError extends Error {
|
|
37
|
-
readonly kind: RepairNeed;
|
|
38
|
-
readonly code = "RepoNeedsRecovery";
|
|
39
|
-
constructor(kind: RepairNeed);
|
|
40
|
-
}
|
|
41
|
-
/** Type guard for {@link RepoNeedsRecoveryError} (matches on the stable code). */
|
|
42
|
-
export declare function isRepoNeedsRecoveryError(e: unknown): e is RepoNeedsRecoveryError;
|
|
43
|
-
/**
|
|
44
|
-
* Thrown by transport.ts's onAuth when a stored credential EXISTS but the
|
|
45
|
-
* remote URL fails isCredentialTransmissionSafe (non-loopback http). Loud and
|
|
46
|
-
* typed on purpose: the old behavior (silently withholding the credential)
|
|
47
|
-
* surfaced as a 401 → "auth" → "reconnect" loop. The `code` string is the
|
|
48
|
-
* STABLE contract (matchable across dynamic-import boundaries).
|
|
49
|
-
*/
|
|
50
|
-
export declare class InsecureTransportError extends Error {
|
|
51
|
-
readonly code = "InsecureTransport";
|
|
52
|
-
constructor();
|
|
53
|
-
}
|
|
54
|
-
/** Type guard for {@link InsecureTransportError} (matches on the stable code). */
|
|
55
|
-
export declare function isInsecureTransportError(e: unknown): e is InsecureTransportError;
|
|
56
|
-
export declare function isPushRejected(e: unknown): boolean;
|
|
57
|
-
/** Type guard exposing MergeConflictError's per-file payload (converge-merge). */
|
|
58
|
-
export declare function isMergeConflictError(e: unknown): e is {
|
|
59
|
-
data: {
|
|
60
|
-
filepaths: string[];
|
|
61
|
-
bothModified: string[];
|
|
62
|
-
deleteByUs: string[];
|
|
63
|
-
deleteByTheirs: string[];
|
|
64
|
-
};
|
|
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;
|
|
72
|
-
export declare function classifyTransportFailure(e: unknown): "auth_required" | "network_unavailable" | "insecure_transport" | null;
|
|
73
|
-
/**
|
|
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.
|
|
81
|
-
*/
|
|
82
|
-
export declare function isLikelyRepoCorruption(e: unknown): boolean;
|
|
83
|
-
/**
|
|
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.
|
|
89
|
-
*
|
|
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).
|
|
94
|
-
*/
|
|
95
|
-
export declare function classifyFromHealth(health: RepoHealth, opts?: {
|
|
96
|
-
minLockAgeMs?: number;
|
|
97
|
-
}): RepairNeed | null;
|