gutterpress 0.0.0
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 +422 -0
- package/dist/CGATS21_CRPC1-g0e3k7kr.icc +0 -0
- package/dist/api/index.d.ts +76 -0
- package/dist/api/index.js +272 -0
- package/dist/assets/preview/scripts/pagedjs-bridge.d.ts +1 -0
- package/dist/assets/preview/scripts/pagedjs-interface.d.ts +1 -0
- package/dist/assets/preview/scripts/preview-shell.d.ts +1 -0
- package/dist/audit-1jrwwacz.js +66 -0
- package/dist/build-5s92kgpc.js +77 -0
- package/dist/chapter-01-6fn9pvd0.md +31 -0
- package/dist/chapter-01-7jwqg1ct.md +20 -0
- package/dist/chapter-01-jj8ftn4c.md +29 -0
- package/dist/chapter-01-rkpyvjna.md +26 -0
- package/dist/checks/asset/approved-fonts.d.ts +3 -0
- package/dist/checks/asset/extensions.d.ts +41 -0
- package/dist/checks/asset/font-license.d.ts +3 -0
- package/dist/checks/asset/image-alpha.d.ts +3 -0
- package/dist/checks/asset/image-color-space.d.ts +3 -0
- package/dist/checks/asset/image-file-size.d.ts +3 -0
- package/dist/checks/asset/image-resolution.d.ts +3 -0
- package/dist/checks/asset/image-tac.d.ts +3 -0
- package/dist/checks/asset/index.d.ts +7 -0
- package/dist/checks/formatter.d.ts +3 -0
- package/dist/checks/heuristic/index.d.ts +4 -0
- package/dist/checks/heuristic/layer-count.d.ts +3 -0
- package/dist/checks/heuristic/placement-variance.d.ts +3 -0
- package/dist/checks/heuristic/section-density.d.ts +3 -0
- package/dist/checks/heuristic/text-density.d.ts +3 -0
- package/dist/checks/pdf/bleed.d.ts +3 -0
- package/dist/checks/pdf/bookmarks.d.ts +3 -0
- package/dist/checks/pdf/color-spaces.d.ts +3 -0
- package/dist/checks/pdf/cross-refs.d.ts +3 -0
- package/dist/checks/pdf/embedded-fonts.d.ts +3 -0
- package/dist/checks/pdf/image-resolution.d.ts +3 -0
- package/dist/checks/pdf/index.d.ts +15 -0
- package/dist/checks/pdf/ink-coverage.d.ts +3 -0
- package/dist/checks/pdf/page-labels.d.ts +3 -0
- package/dist/checks/pdf/page-size.d.ts +3 -0
- package/dist/checks/pdf/pdfx-markers.d.ts +3 -0
- package/dist/checks/pdf/pdfx-metadata.d.ts +3 -0
- package/dist/checks/pdf/pdfx-structure.d.ts +6 -0
- package/dist/checks/pdf/qpdf-structure.d.ts +3 -0
- package/dist/checks/pdf/rasterized-pages.d.ts +3 -0
- package/dist/checks/pdf/toc-links.d.ts +3 -0
- package/dist/checks/pdf/transparency.d.ts +3 -0
- package/dist/checks/policy.d.ts +61 -0
- package/dist/checks/register-builtins.d.ts +17 -0
- package/dist/checks/registry.d.ts +40 -0
- package/dist/checks/runner.d.ts +25 -0
- package/dist/checks/source/accessibility-alt-text.d.ts +3 -0
- package/dist/checks/source/accessibility-heading-order.d.ts +3 -0
- package/dist/checks/source/config-file.d.ts +8 -0
- package/dist/checks/source/htmlhint.d.ts +3 -0
- package/dist/checks/source/index.d.ts +6 -0
- package/dist/checks/source/local-refs.d.ts +3 -0
- package/dist/checks/source/markdownlint.d.ts +3 -0
- package/dist/checks/source/stylelint.d.ts +3 -0
- package/dist/checks/tool-check.d.ts +23 -0
- package/dist/checks/types.d.ts +52 -0
- package/dist/cli-1tyj4bk3.js +124 -0
- package/dist/cli-37x76zdn.js +4 -0
- package/dist/cli-506tg37g.js +26 -0
- package/dist/cli-5bxx0c4y.js +464 -0
- package/dist/cli-7ykx7pp0.js +12378 -0
- package/dist/cli-h7phg3ka.js +746 -0
- package/dist/cli-hvqjs342.js +284 -0
- package/dist/cli-yja077f6.js +92 -0
- package/dist/cli.js +157 -0
- package/dist/constants.d.ts +17 -0
- package/dist/doctor-25zjt8dz.js +57 -0
- package/dist/favicon-wkbm9cjn.ico +0 -0
- package/dist/git-http-yrb4ag6z.js +17 -0
- package/dist/index-1tyj4bk3.js +124 -0
- package/dist/index-37x76zdn.js +4 -0
- package/dist/index-3h3fef7r.js +15218 -0
- package/dist/index-5bxx0c4y.js +464 -0
- package/dist/index-yja077f6.js +92 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +310 -0
- package/dist/lib/app-heartbeat.d.ts +102 -0
- package/dist/lib/asset-inline.d.ts +73 -0
- package/dist/lib/browser-pool.d.ts +17 -0
- package/dist/lib/build-error.d.ts +52 -0
- package/dist/lib/build-fingerprint.d.ts +15 -0
- package/dist/lib/build-preflight.d.ts +43 -0
- package/dist/lib/build-runner.d.ts +161 -0
- package/dist/lib/build-staging.d.ts +64 -0
- package/dist/lib/chromium.d.ts +20 -0
- package/dist/lib/cli-args.d.ts +45 -0
- package/dist/lib/desktop.d.ts +14 -0
- package/dist/lib/diagnostics.d.ts +64 -0
- package/dist/lib/embedded-assets.d.ts +29 -0
- package/dist/lib/exec.d.ts +84 -0
- package/dist/lib/fetch-timeout.d.ts +41 -0
- package/dist/lib/ghostscript.d.ts +48 -0
- package/dist/lib/host-policy.d.ts +75 -0
- package/dist/lib/image-inspect.d.ts +32 -0
- package/dist/lib/install-hints.d.ts +21 -0
- package/dist/lib/lint-runner.d.ts +10 -0
- package/dist/lib/manifest-config.d.ts +31 -0
- package/dist/lib/manifest-doc.d.ts +25 -0
- package/dist/lib/manifest.d.ts +41 -0
- package/dist/lib/markdown/assemble.d.ts +78 -0
- package/dist/lib/markdown/chapter-id.d.ts +24 -0
- package/dist/lib/markdown/images.d.ts +34 -0
- package/dist/lib/markdown/index.d.ts +78 -0
- package/dist/lib/markdown/markdown-it-paged.d.ts +30 -0
- package/dist/lib/markdown/plugins.d.ts +69 -0
- package/dist/lib/markdown/renderer.d.ts +111 -0
- package/dist/lib/npm-plugin-installer.d.ts +33 -0
- package/dist/lib/open-path.d.ts +5 -0
- package/dist/lib/output-paths.d.ts +23 -0
- package/dist/lib/pagedjs-marker.d.ts +42 -0
- package/dist/lib/pagedjs.d.ts +26 -0
- package/dist/lib/pagination.d.ts +169 -0
- package/dist/lib/pdf-inspect.d.ts +123 -0
- package/dist/lib/pdf-parse.d.ts +57 -0
- package/dist/lib/plugin-manager.d.ts +108 -0
- package/dist/lib/plugin-vendor.d.ts +107 -0
- package/dist/lib/presets.d.ts +45 -0
- package/dist/lib/printsafe.d.ts +17 -0
- package/dist/lib/project-scaffold.d.ts +116 -0
- package/dist/lib/project-source.d.ts +140 -0
- package/dist/lib/project-templates.d.ts +49 -0
- package/dist/lib/publish/butler.d.ts +10 -0
- package/dist/lib/publish/command-runner.d.ts +18 -0
- package/dist/lib/publish/connect.d.ts +23 -0
- package/dist/lib/publish/manifest-publish.d.ts +9 -0
- package/dist/lib/publish/providers/azure-swa.d.ts +3 -0
- package/dist/lib/publish/providers/drivethrurpg.d.ts +10 -0
- package/dist/lib/publish/providers/guided.d.ts +3 -0
- package/dist/lib/publish/providers/itch.d.ts +5 -0
- package/dist/lib/publish/providers/kdp.d.ts +9 -0
- package/dist/lib/publish/providers/shopify.d.ts +5 -0
- package/dist/lib/publish/registry.d.ts +5 -0
- package/dist/lib/publish/run-publish.d.ts +30 -0
- package/dist/lib/publish/selections.d.ts +25 -0
- package/dist/lib/publish/types.d.ts +258 -0
- package/dist/lib/remote-auth/clone.d.ts +87 -0
- package/dist/lib/remote-auth/conflict-resolution.d.ts +29 -0
- package/dist/lib/remote-auth/diagnose.d.ts +77 -0
- package/dist/lib/remote-auth/generic-auth.d.ts +97 -0
- package/dist/lib/remote-auth/git-http.d.ts +85 -0
- package/dist/lib/remote-auth/github-auth.d.ts +69 -0
- package/dist/lib/remote-auth/github-repos.d.ts +64 -0
- package/dist/lib/remote-auth/operation-log.d.ts +66 -0
- package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +103 -0
- package/dist/lib/remote-auth/recovery/backup.d.ts +113 -0
- package/dist/lib/remote-auth/recovery/classify.d.ts +109 -0
- package/dist/lib/remote-auth/recovery/context.d.ts +47 -0
- package/dist/lib/remote-auth/recovery/dispatch.d.ts +33 -0
- package/dist/lib/remote-auth/recovery/failsafe.d.ts +33 -0
- package/dist/lib/remote-auth/recovery/inspect.d.ts +74 -0
- package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +28 -0
- package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +58 -0
- package/dist/lib/remote-auth/recovery/policy.d.ts +47 -0
- package/dist/lib/remote-auth/recovery/recover-auth.d.ts +42 -0
- package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +37 -0
- package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +40 -0
- package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +70 -0
- package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +23 -0
- package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +28 -0
- package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +39 -0
- package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +34 -0
- package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +37 -0
- package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +56 -0
- package/dist/lib/remote-auth/recovery/recover-network.d.ts +34 -0
- package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +27 -0
- package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +76 -0
- package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +44 -0
- package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +35 -0
- package/dist/lib/remote-auth/recovery/types.d.ts +235 -0
- package/dist/lib/remote-auth/resolution-plan.d.ts +64 -0
- package/dist/lib/remote-auth/sync-messages.d.ts +26 -0
- package/dist/lib/remote-auth/sync-types.d.ts +225 -0
- package/dist/lib/remote-auth/sync.d.ts +45 -0
- package/dist/lib/remote-auth/test-access.d.ts +47 -0
- package/dist/lib/remote-auth/test-support/git-http-server.d.ts +71 -0
- package/dist/lib/remote-auth/token-store.d.ts +95 -0
- package/dist/lib/remote-auth/transport.d.ts +105 -0
- package/dist/lib/slug.d.ts +28 -0
- package/dist/lib/snippets.d.ts +39 -0
- package/dist/lib/source-provider.d.ts +244 -0
- package/dist/lib/static-serve.d.ts +54 -0
- package/dist/lib/style-resolver.d.ts +30 -0
- package/dist/lib/theme-import.d.ts +70 -0
- package/dist/lib/theme-manager.d.ts +128 -0
- package/dist/lib/tool-probe.d.ts +37 -0
- package/dist/lib/validation-exec.d.ts +47 -0
- package/dist/lib/validation-profile.d.ts +18 -0
- package/dist/lib/version.d.ts +9 -0
- package/dist/lint-f63nxjfj.js +65 -0
- package/dist/manifest-n1gh3qw5.yaml +9 -0
- package/dist/manifest-ryyg71nf.yaml +9 -0
- package/dist/manifest.schema-rn1dpdpn.json +564 -0
- package/dist/new-7b08a42h.js +105 -0
- package/dist/paged.polyfill-n95pbxfn.js +33288 -0
- package/dist/pagedjs-bridge-vn4hk9fx.js +111 -0
- package/dist/pagedjs-interface-80g3xa8p.js +498 -0
- package/dist/platform.d.ts +100 -0
- package/dist/plugin-wf2cbmwc.js +129 -0
- package/dist/preflight-tg4mpz1h.js +222 -0
- package/dist/preview/file-watcher.d.ts +174 -0
- package/dist/preview/http-server.d.ts +45 -0
- package/dist/preview/lifecycle.d.ts +67 -0
- package/dist/preview/server-context.d.ts +36 -0
- package/dist/preview-h2bwbb2x.js +102 -0
- package/dist/preview-shell-rghtfmc8.js +155 -0
- package/dist/project-source-0tg0r6vm.js +17 -0
- package/dist/publish-ztaet53r.js +233 -0
- package/dist/render.d.ts +20 -0
- package/dist/render.js +682 -0
- package/dist/repair-jkp4n3j8.js +171 -0
- package/dist/schema/manifest.types.d.ts +218 -0
- package/dist/server.d.ts +34 -0
- package/dist/source-provider-hp96b5jf.js +46 -0
- package/dist/test-helpers/testkit.d.ts +91 -0
- package/dist/theme-0yzvthe8.css +65 -0
- package/dist/theme-570zmh2t.css +58 -0
- package/dist/theme-6a2fngc8.json +6 -0
- package/dist/theme-cd6efzjw.json +6 -0
- package/dist/theme-nya4nqh6.css +70 -0
- package/dist/theme-tqfypc1w.json +6 -0
- package/dist/theme-xt0hjb1b.json +6 -0
- package/dist/theme-zz2ktzqs.css +60 -0
- package/dist/types.d.ts +31 -0
- package/dist/utils/file-utils.d.ts +53 -0
- package/dist/utils/logger.d.ts +59 -0
- package/dist/validate-k2drs19j.js +99 -0
- package/package.json +102 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight repo health probe for the sync-recovery subsystem.
|
|
3
|
+
*
|
|
4
|
+
* inspectRepo() reads local filesystem state ONLY — no network, no git objects,
|
|
5
|
+
* never throws. It returns a RepoHealth snapshot that the classifier and policy
|
|
6
|
+
* lookup use to decide which recovery path to take.
|
|
7
|
+
*
|
|
8
|
+
* Reuses:
|
|
9
|
+
* - gitDirFor (source-provider.ts) for the .git dir path
|
|
10
|
+
* - hasPendingChanges (source-provider.ts) for local-changes detection
|
|
11
|
+
*
|
|
12
|
+
* Notes on two health facts:
|
|
13
|
+
* - hasGitDir is true whenever `.git/` EXISTS, even with a missing/corrupt
|
|
14
|
+
* HEAD (a damaged repo is still a repo — see the inline note at the check).
|
|
15
|
+
* - hasStaleLock uses the stale-lock handler's OWN lock scanner
|
|
16
|
+
* (findLockCandidates in recover-stale-lock.ts) — one implementation, so
|
|
17
|
+
* health and handler can never disagree about which locks exist.
|
|
18
|
+
*
|
|
19
|
+
* All probes are best-effort and throw-free (the caller must never see an
|
|
20
|
+
* exception from a preflight probe).
|
|
21
|
+
*/
|
|
22
|
+
import type { LogData } from "../operation-log.ts";
|
|
23
|
+
import type { RepoHealth, RecoveryContext, SyncErrorKind } from "./types.ts";
|
|
24
|
+
/**
|
|
25
|
+
* Probe the local repository and return a RepoHealth snapshot.
|
|
26
|
+
* Never throws — on any error the relevant flag is set conservatively.
|
|
27
|
+
*
|
|
28
|
+
* `checkLocalChanges: false` skips the hasPendingChanges working-tree walk
|
|
29
|
+
* (the one non-trivial probe) and reports hasLocalChanges=false. Use it when
|
|
30
|
+
* only the structural flags matter — e.g. syncProject's preflight, whose
|
|
31
|
+
* pull step immediately performs the same walk anyway (sync-simplicity
|
|
32
|
+
* mandate: no redundant walks on the hot path).
|
|
33
|
+
*/
|
|
34
|
+
export declare function inspectRepo(ctx: Pick<RecoveryContext, "repoDir" | "source">, opts?: {
|
|
35
|
+
checkLocalChanges?: boolean;
|
|
36
|
+
}): Promise<RepoHealth>;
|
|
37
|
+
/**
|
|
38
|
+
* Confirm the repo's object store is actually readable: resolve HEAD, read
|
|
39
|
+
* its commit, and read its root tree. Throws when any step fails (missing or
|
|
40
|
+
* corrupt object/ref); resolves when the repo is fully readable.
|
|
41
|
+
*
|
|
42
|
+
* This is the SAME verification recover-missing-objects.ts performs to check
|
|
43
|
+
* whether a fetch actually repaired the object store — it is exported here so
|
|
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
|
+
* all filesystem-presence checks (no object is ever read), so they cannot
|
|
47
|
+
* detect object-store corruption on their own — callers that need to catch
|
|
48
|
+
* `corrupt_index` / `missing_or_corrupt_objects` / `unrelated_histories` /
|
|
49
|
+
* `wrong_remote_or_branch` must run this probe and feed a caught error
|
|
50
|
+
* through `classifyGitError`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function verifyRepoReadable(dir: string): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* True when HEAD names a branch that simply has no commits yet — a fresh
|
|
55
|
+
* `git init` before the first snapshot. In that state `verifyRepoReadable`
|
|
56
|
+
* throws the SAME NotFoundError as a damaged ref store, but the repo is
|
|
57
|
+
* healthy, not corrupt. The distinguishing signal is the object store: a
|
|
58
|
+
* fresh repo has NO objects at all, while ref damage on a real repo leaves
|
|
59
|
+
* loose objects and/or packfiles behind.
|
|
60
|
+
*/
|
|
61
|
+
export declare function isUnbornRepo(repoDir: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* The SINGLE health signal that drove classification. Derived from the KIND
|
|
64
|
+
* classifyFromHealth returned (a pure mapping — it cannot drift from the
|
|
65
|
+
* classifier's decision order, because it never re-implements it).
|
|
66
|
+
*/
|
|
67
|
+
export declare function preflightStructuralReason(kind: SyncErrorKind | null): string;
|
|
68
|
+
/**
|
|
69
|
+
* Build a flat, secret-free record of the preflight decision inputs for the
|
|
70
|
+
* operation log. Every health boolean is recorded (so support can see the FULL
|
|
71
|
+
* picture, not just the one-word kind), plus the opened dir vs repo root, the
|
|
72
|
+
* chosen kind, and the single reason that drove it.
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildPreflightDiagnostics(openedDir: string, repoDir: string, health: RepoHealth, kind: SyncErrorKind | null): LogData;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-language guidance builders for the sync-recovery subsystem.
|
|
3
|
+
*
|
|
4
|
+
* AUTHOR-FACING COPY RULES (non-negotiable):
|
|
5
|
+
* - No git words ("branch", "commit", "rebase", "ref", "HEAD", etc.)
|
|
6
|
+
* - No tokens or credentials
|
|
7
|
+
* - No internal file paths (no .git/, no /tmp paths in userSummary)
|
|
8
|
+
* - No technical error codes in user-facing fields
|
|
9
|
+
* - supportDetails MAY contain technical info for support tickets
|
|
10
|
+
*
|
|
11
|
+
* Each SyncErrorKind gets its own copy block. The host maps the
|
|
12
|
+
* ManualGuidance to its display surface.
|
|
13
|
+
*/
|
|
14
|
+
import type { ManualGuidance, RecoveryContext, SyncErrorKind } from "./types.ts";
|
|
15
|
+
/**
|
|
16
|
+
* Build a ManualGuidance for a given error kind. The guidance is shown when
|
|
17
|
+
* a repair is blocked, failed, or needs user action. Never throws.
|
|
18
|
+
*
|
|
19
|
+
* SAFETY-COPY HONESTY: the "a safety copy was saved" reassurance is generated
|
|
20
|
+
* in ONE place, from the actual `backupZipPath` — never hardcoded per kind.
|
|
21
|
+
* The backup gate creates (and verifies) the zip BEFORE confirmation, so
|
|
22
|
+
* whenever this guidance is shown for a backup-requiring kind, a present
|
|
23
|
+
* backupZipPath means the copy really exists, and an absent one means backup
|
|
24
|
+
* creation FAILED and nothing was changed (failsafe.ts returns
|
|
25
|
+
* failed_no_changes_made without any writes). Promising a safety copy in that
|
|
26
|
+
* second case would be false — the one moment the promise matters most.
|
|
27
|
+
*/
|
|
28
|
+
export declare function makeManualGuidance(ctx: Pick<RecoveryContext, "repoSlug" | "remoteUrl">, kind: SyncErrorKind, error?: unknown, backupZipPath?: string): ManualGuidance;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* outcome-mapping.ts — shared SyncOutcome/PullOutcome → RecoveryResult mapping.
|
|
3
|
+
*
|
|
4
|
+
* WHY this exists: five thin recovery handlers (recover-non-fast-forward,
|
|
5
|
+
* recover-auth, recover-network, recover-merge-conflict, recover-binary-conflict)
|
|
6
|
+
* all build the SAME syncProject/pullChanges options object from the context and
|
|
7
|
+
* then translate the typed outcome union into a RecoveryResult. The option
|
|
8
|
+
* object was copied verbatim five times, and the outcome→result maps had drifted
|
|
9
|
+
* apart (e.g. an `offline` outcome mapped to `retry_later` in one handler and to
|
|
10
|
+
* `needs_user` in another). This module owns the ONE default map; each handler
|
|
11
|
+
* passes only the per-status `overrides` where its behavior INTENTIONALLY
|
|
12
|
+
* differs, so the disagreements are explicit rather than accidental copies.
|
|
13
|
+
*
|
|
14
|
+
* DEFAULT OUTCOME MAP (what a handler gets with no overrides):
|
|
15
|
+
* synced → recovered { message: outcome.message }
|
|
16
|
+
* pulled → recovered { message: outcome.message }
|
|
17
|
+
* up-to-date → recovered { message: outcome.message }
|
|
18
|
+
* conflict → needs_user { message, guidance: merge_conflict, files }
|
|
19
|
+
* auth → needs_user { message, guidance: auth_required }
|
|
20
|
+
* offline → retry_later { message, retryAfterMs: RETRY_AFTER_MS }
|
|
21
|
+
* error / → failed_no_changes_made { message, guidance: unknown }
|
|
22
|
+
* (unknown)
|
|
23
|
+
*
|
|
24
|
+
* An override wins over the default for its status. An UNKNOWN/future status
|
|
25
|
+
* routes to the `error` builder (override first, then default) — mirroring the
|
|
26
|
+
* `default:` arm every handler used to carry.
|
|
27
|
+
*/
|
|
28
|
+
import type { PullOutcome, SyncOutcome } from "../sync.ts";
|
|
29
|
+
import type { RecoveryContext, RecoveryResult } from "./types.ts";
|
|
30
|
+
/** Fixed delay before the next automated retry (30 seconds). */
|
|
31
|
+
export declare const RETRY_AFTER_MS = 30000;
|
|
32
|
+
/** The typed outcome unions the recovery handlers translate. */
|
|
33
|
+
export type AnyOutcome = SyncOutcome | PullOutcome;
|
|
34
|
+
/** Every status either outcome union can carry. */
|
|
35
|
+
export type OutcomeStatus = AnyOutcome["status"];
|
|
36
|
+
/** Builds a RecoveryResult from the context and the (already-classified) outcome. */
|
|
37
|
+
export type OutcomeBuilder = (ctx: RecoveryContext, outcome: AnyOutcome) => RecoveryResult;
|
|
38
|
+
/** Per-status overrides a handler supplies where it intentionally differs. */
|
|
39
|
+
export type OutcomeOverrides = Partial<Record<OutcomeStatus, OutcomeBuilder>>;
|
|
40
|
+
/**
|
|
41
|
+
* The shared options object every handler feeds to syncProject / pullChanges.
|
|
42
|
+
* Extracted so the five copies can never drift.
|
|
43
|
+
*/
|
|
44
|
+
export declare function syncOptionsFrom(ctx: RecoveryContext): {
|
|
45
|
+
projectDir: string;
|
|
46
|
+
credential: import("../token-store.ts").HostCredential | undefined;
|
|
47
|
+
tokenStore: import("../token-store.ts").TokenStore | undefined;
|
|
48
|
+
authorName: string | undefined;
|
|
49
|
+
authorEmail: string | undefined;
|
|
50
|
+
httpClient: typeof import("isomorphic-git/http/node").default | undefined;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Translate a SyncOutcome/PullOutcome into a RecoveryResult using the default
|
|
54
|
+
* map, letting per-status `overrides` win. Any status not present in the map
|
|
55
|
+
* (a future/unknown status) falls through to the `error` builder — override
|
|
56
|
+
* first, then default — matching each handler's original `default:` arm.
|
|
57
|
+
*/
|
|
58
|
+
export declare function mapOutcomeToResult(ctx: RecoveryContext, outcome: AnyOutcome, overrides?: OutcomeOverrides): RecoveryResult;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery policy matrix — pure data, no I/O.
|
|
3
|
+
*
|
|
4
|
+
* Each SyncErrorKind maps to a policy that controls:
|
|
5
|
+
* - risk: how much irreversible change the repair makes
|
|
6
|
+
* - createBackup: whether the repair MUST create a /tmp zip before running
|
|
7
|
+
* - requireConfirmation: whether the user must approve the repair
|
|
8
|
+
* - mayChangeLocalFiles: whether the repair can modify user-visible files
|
|
9
|
+
* - mayChangeGitMetadata: whether the repair can modify .git/ metadata
|
|
10
|
+
* - mayChangeRemote: whether the repair can push to the remote
|
|
11
|
+
* - automate: whether the repair can run without user interaction at all
|
|
12
|
+
*
|
|
13
|
+
* Safety invariants (from the spec):
|
|
14
|
+
* - detached_head, corrupt_index, missing_git_dir, missing_or_corrupt_objects,
|
|
15
|
+
* unrelated_histories → always backup + confirm
|
|
16
|
+
* - auth_required, network_unavailable, non_fast_forward → no backup needed,
|
|
17
|
+
* no confirmation required (they are thin delegates to sync.ts)
|
|
18
|
+
* - wrong_remote_or_branch → no repair attempted (pure block with guidance),
|
|
19
|
+
* so no backup is needed either
|
|
20
|
+
*/
|
|
21
|
+
import type { RecoveryRisk, SyncErrorKind } from "./types.ts";
|
|
22
|
+
export interface RecoveryPolicy {
|
|
23
|
+
risk: RecoveryRisk;
|
|
24
|
+
createBackup: boolean;
|
|
25
|
+
requireConfirmation: boolean;
|
|
26
|
+
mayChangeLocalFiles: boolean;
|
|
27
|
+
mayChangeGitMetadata: boolean;
|
|
28
|
+
mayChangeRemote: boolean;
|
|
29
|
+
/** True when the repair can run transparently without prompting the user. */
|
|
30
|
+
automate: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* True when the dispatcher must run the handler inside withRepoLock (the
|
|
33
|
+
* per-repo FIFO queue): every handler that mutates `.git` via RAW git.* /
|
|
34
|
+
* node:fs calls. MUST be false for the thin sync.ts delegates — they call
|
|
35
|
+
* lock-wrapped functions (syncProject/pullChanges) internally, and the FIFO
|
|
36
|
+
* queue is non-reentrant (locking them at the dispatcher would deadlock).
|
|
37
|
+
*/
|
|
38
|
+
serializeRepo: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare const recoveryPolicy: Record<SyncErrorKind, RecoveryPolicy>;
|
|
41
|
+
/** Look up the policy for a kind (always succeeds — every kind has an entry). */
|
|
42
|
+
export declare function policyFor(kind: SyncErrorKind): RecoveryPolicy;
|
|
43
|
+
/**
|
|
44
|
+
* Refinement: detached HEAD with local changes is riskier — escalate to high
|
|
45
|
+
* risk and always require confirmation regardless of the base policy.
|
|
46
|
+
*/
|
|
47
|
+
export declare function detachedHeadWithLocalChangesPolicy(hasLocalChanges: boolean): RecoveryPolicy;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-auth.ts — Auth failure: clear the bad credential, ask the author
|
|
3
|
+
* to reconnect.
|
|
4
|
+
*
|
|
5
|
+
* WHY this module exists: when the remote rejects the saved connection
|
|
6
|
+
* (401 / 403), the right behavior is:
|
|
7
|
+
*
|
|
8
|
+
* 1. Attempt sync so the snapshot-first invariant (ADR 0006 D5) still runs
|
|
9
|
+
* if possible — any unsaved local work gets committed before the network
|
|
10
|
+
* call, so nothing is lost regardless of what the server says.
|
|
11
|
+
* 2. If the result is `auth`, delete the stale credential from the token
|
|
12
|
+
* store so the next reconnect flow starts clean.
|
|
13
|
+
* 3. Return `needs_user` with plain-language reconnect guidance.
|
|
14
|
+
* recommendedAction is always "Reconnect" — no git words, no tokens.
|
|
15
|
+
* 4. Never create a backup zip (policy.createBackup = false for auth_required
|
|
16
|
+
* — there is nothing locally at risk; the author's work is already
|
|
17
|
+
* committed by the snapshot-first step in sync.ts).
|
|
18
|
+
* 5. Never ask for confirmation (no repair changes anything).
|
|
19
|
+
* 6. Never force-push (syncProject / pushChanges never does this either).
|
|
20
|
+
*
|
|
21
|
+
* This is a THIN WRAPPER around syncProject from sync.ts. The snapshot step,
|
|
22
|
+
* the network call, and the "auth" classification all live there. The only
|
|
23
|
+
* delta this module adds is:
|
|
24
|
+
* - Calling ctx.tokenStore.delete(host) when the outcome is `auth`.
|
|
25
|
+
* - Translating SyncOutcome { status: "auth" } → RecoveryResult { status:
|
|
26
|
+
* "needs_user" } with the auth_required ManualGuidance.
|
|
27
|
+
*/
|
|
28
|
+
import type { RecoveryContext, RecoveryResult } from "./types.ts";
|
|
29
|
+
/**
|
|
30
|
+
* Handle an `auth_required` classification.
|
|
31
|
+
*
|
|
32
|
+
* - Delegates the sync attempt (including the snapshot-first step) to
|
|
33
|
+
* `syncProject` from sync.ts.
|
|
34
|
+
* - On `auth` outcome: clears the bad credential from ctx.tokenStore (best-
|
|
35
|
+
* effort — missing store is silently ignored) and returns `needs_user` with
|
|
36
|
+
* author-friendly reconnect guidance.
|
|
37
|
+
* - On any successful outcome (the good-credential path): returns `recovered`
|
|
38
|
+
* so the host can refresh. The credential is NOT cleared on success.
|
|
39
|
+
* - On other failure outcomes (offline, conflict, error): passes through an
|
|
40
|
+
* appropriate RecoveryResult without touching the credential store.
|
|
41
|
+
*/
|
|
42
|
+
export declare function recover(ctx: RecoveryContext, _error?: unknown): Promise<RecoveryResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-binary-conflict.ts — never auto-merge binary files.
|
|
3
|
+
*
|
|
4
|
+
* WHY this exists: binary files (images, PDFs, audio, etc.) cannot be
|
|
5
|
+
* line-merged. When both the local copy and the online copy changed the same
|
|
6
|
+
* binary file, the only honest answer is: show the user the list of clashing
|
|
7
|
+
* files and let them choose which version to keep. The "Keep both" option
|
|
8
|
+
* works for binary files too — it keeps the author's version alongside an
|
|
9
|
+
* "(online copy)" rename.
|
|
10
|
+
*
|
|
11
|
+
* This handler is intentionally identical in shape to recover-merge-conflict.ts.
|
|
12
|
+
* The distinction between text and binary conflicts is made at a higher level
|
|
13
|
+
* (classify.ts / the merge engine) — by the time recover() is called we already
|
|
14
|
+
* know the conflict involves binary content, so we surface it the same way:
|
|
15
|
+
* a 'needs_user' result with the conflicted file list.
|
|
16
|
+
*
|
|
17
|
+
* Reuses:
|
|
18
|
+
* pullChanges — snapshot → fetch → merge (abortOnConflict). On a binary
|
|
19
|
+
* file isomorphic-git still throws MergeConflictError with the
|
|
20
|
+
* conflicting filepath in data.filepaths.
|
|
21
|
+
* makeManualGuidance — jargon-free copy for binary_conflict.
|
|
22
|
+
*
|
|
23
|
+
* Policy (policy.ts): createBackup=false, requireConfirmation=false.
|
|
24
|
+
* The working tree is left completely untouched by pullChanges
|
|
25
|
+
* (abortOnConflict), so no backup is needed to surface the conflict to the
|
|
26
|
+
* user. The confirmation gate is the per-file UI chooser, not withBackupGate.
|
|
27
|
+
*
|
|
28
|
+
* Safety invariants (see test file):
|
|
29
|
+
* - NO force-push: pullChanges never pushes; this handler never calls push.
|
|
30
|
+
* - Remote HEAD is NEVER advanced: we stop at conflict, surface needs_user.
|
|
31
|
+
* - Both blob versions are preserved in local history (pullChanges snapshots
|
|
32
|
+
* the working tree before merge, then aborts — no side is silently dropped).
|
|
33
|
+
* - write_conflict_snapshot fault hook is called and, if it throws, the error
|
|
34
|
+
* is caught and a safe fallback is returned (no unhandled throw to caller).
|
|
35
|
+
*/
|
|
36
|
+
import type { RecoverFn } from "./types.ts";
|
|
37
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-corrupt-index.ts — repair a corrupt or unreadable .git/index.
|
|
3
|
+
*
|
|
4
|
+
* WHY this is needed:
|
|
5
|
+
* The git index (staging area) can become corrupt if gutterpress or the OS
|
|
6
|
+
* crashes mid-operation. When the index is unreadable, virtually every
|
|
7
|
+
* git operation fails — status checks, sync, everything. The fix is simple:
|
|
8
|
+
* delete the bad index and let git rebuild it from the last saved snapshot.
|
|
9
|
+
* User-visible content files are NEVER touched — uncommitted edits survive.
|
|
10
|
+
*
|
|
11
|
+
* Safety invariants:
|
|
12
|
+
* - Backup created and verified BEFORE anything is deleted.
|
|
13
|
+
* - User must confirm before the repair runs.
|
|
14
|
+
* - If backup creation fails → failed_no_changes_made, no further writes.
|
|
15
|
+
* - If removal or rebuild throws after backup → failed_backup_available.
|
|
16
|
+
* - User DENY → blocked (index left exactly as-is).
|
|
17
|
+
* - Never force-pushes; the repair is entirely local (no remote contact).
|
|
18
|
+
* - Uncommitted edits in the working tree are NEVER overwritten or discarded.
|
|
19
|
+
*
|
|
20
|
+
* Repair steps (all inside withBackupGate's risky callback):
|
|
21
|
+
* 1. ctx.faults?.before("remove_index") — fault injection point
|
|
22
|
+
* 2. Delete .git/index
|
|
23
|
+
* 3. ctx.faults?.before("rebuild_index") — fault injection point
|
|
24
|
+
* 4. Walk HEAD's tree, call git.resetIndex({ filepath }) for each tracked file.
|
|
25
|
+
* resetIndex ONLY updates the index metadata (mode/oid/stat) — it does NOT
|
|
26
|
+
* write to the working-tree file, so uncommitted edits remain byte-for-byte.
|
|
27
|
+
* 5. Return status=recovered
|
|
28
|
+
*/
|
|
29
|
+
import type { RecoverFn, StillAppliesFn } from "./types.ts";
|
|
30
|
+
/**
|
|
31
|
+
* Precondition probe (see types.ts `StillAppliesFn`): re-probe whether the
|
|
32
|
+
* index is still unreadable right before the dispatcher hands off to
|
|
33
|
+
* `recover` below. `git.listFiles` with no `ref` reads the current staging
|
|
34
|
+
* area directly from `.git/index` — the same file this handler deletes and
|
|
35
|
+
* rebuilds — so a successful read here means the corruption is already gone
|
|
36
|
+
* (e.g. a previous attempt already rebuilt it, or the author replaced the
|
|
37
|
+
* file externally) and there is nothing left to repair.
|
|
38
|
+
*/
|
|
39
|
+
export declare const stillApplies: StillAppliesFn;
|
|
40
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-detached-head.ts — recover from a detached HEAD state.
|
|
3
|
+
*
|
|
4
|
+
* WHY: When an author's version history ends up in "detached" mode, syncing
|
|
5
|
+
* fails completely. This happens when the working copy landed on a specific
|
|
6
|
+
* saved point rather than the live named copy of the work.
|
|
7
|
+
* The repair re-attaches the copy to the named working version.
|
|
8
|
+
*
|
|
9
|
+
* All cases use a unified path through withBackupGate so fault injection
|
|
10
|
+
* points are consistent and predictable:
|
|
11
|
+
*
|
|
12
|
+
* Case A — clean working tree, current point IS reachable from the
|
|
13
|
+
* configured version line:
|
|
14
|
+
* Backup → create local rescue copy at current HEAD →
|
|
15
|
+
* checkout named version → 'recovered'.
|
|
16
|
+
*
|
|
17
|
+
* Case B — orphan saved point (NOT reachable from the named version line):
|
|
18
|
+
* Same path as Case A; the rescue copy preserves the orphan.
|
|
19
|
+
*
|
|
20
|
+
* Case C — uncommitted edits in the working tree:
|
|
21
|
+
* Backup → stage + commit edits to rescue copy →
|
|
22
|
+
* checkout named version → 'recovered'.
|
|
23
|
+
* DENY → blocked, everything unchanged.
|
|
24
|
+
*
|
|
25
|
+
* Branch resolution (which named version to re-attach to): the detached commit
|
|
26
|
+
* usually belongs to the author's real working branch, which is NOT always
|
|
27
|
+
* "main" (it may be "master", "trunk", …). The handler DISCOVERS that branch
|
|
28
|
+
* (resolveTargetBranch) instead of assuming "main": an explicit ctx.branch
|
|
29
|
+
* wins; otherwise it prefers a sole local branch, then the unique local branch
|
|
30
|
+
* the detached commit belongs to, then the remote's default
|
|
31
|
+
* (refs/remotes/origin/HEAD), and only finally "main". Discovery never throws.
|
|
32
|
+
*
|
|
33
|
+
* The final checkout is FORCED. By that point the prior state is preserved both
|
|
34
|
+
* on the rescue branch and in the verified /tmp backup, so forcing cannot lose
|
|
35
|
+
* work — and it avoids a spurious CheckoutConflictError from an untracked /
|
|
36
|
+
* ignored / stat-dirty file colliding with the target version.
|
|
37
|
+
*
|
|
38
|
+
* Fault injection points (called via ctx.faults?.before()):
|
|
39
|
+
* backup_create — before the zip is written
|
|
40
|
+
* backup_verify — after the zip is written, before verifying
|
|
41
|
+
* commit_recovery_snapshot — before staging + committing local edits (Case C)
|
|
42
|
+
* create_recovery_branch — before writing the rescue branch ref
|
|
43
|
+
* checkout_branch — before the final checkout
|
|
44
|
+
*
|
|
45
|
+
* Safety invariants:
|
|
46
|
+
* - NEVER force-push (push is never called; the rescue copy is local-only).
|
|
47
|
+
* - /tmp zip backup created and verified BEFORE any branch/checkout op.
|
|
48
|
+
* - backup_create fault → failed_no_changes_made, no writes after.
|
|
49
|
+
* - mid-repair fault → failed_backup_available, backup readable, remote unchanged.
|
|
50
|
+
* - confirmation DENIED → blocked, local + remote unchanged.
|
|
51
|
+
*
|
|
52
|
+
* Author-facing copy: no git words, no tokens, no internal paths in userSummary.
|
|
53
|
+
*/
|
|
54
|
+
import type { RecoveryContext, RecoveryResult, StillAppliesFn } from "./types.ts";
|
|
55
|
+
/**
|
|
56
|
+
* Precondition probe (see types.ts `StillAppliesFn`): re-probe whether HEAD is
|
|
57
|
+
* still detached right before the dispatcher hands off to `recover` below.
|
|
58
|
+
* HEAD may have been re-attached externally (e.g. the author ran a checkout
|
|
59
|
+
* in a terminal) between classification and dispatch. A thrown
|
|
60
|
+
* `currentBranch` read (HEAD/ref store unreadable) is a DIFFERENT, worse
|
|
61
|
+
* condition than detachment — conservatively report "still applies" so that
|
|
62
|
+
* case surfaces through the normal repair path rather than being swallowed
|
|
63
|
+
* here as a false no-op.
|
|
64
|
+
*/
|
|
65
|
+
export declare const stillApplies: StillAppliesFn;
|
|
66
|
+
/**
|
|
67
|
+
* Recover from a detached HEAD state.
|
|
68
|
+
* Implements the RecoverFn contract (types.ts).
|
|
69
|
+
*/
|
|
70
|
+
export declare function recover(ctx: RecoveryContext, _error?: unknown): Promise<RecoveryResult>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-interrupted-cherry-pick.ts — abort a cherry-pick left unfinished.
|
|
3
|
+
*
|
|
4
|
+
* WHY: A conflicted cherry-pick stops BEFORE committing, so — unlike a rebase —
|
|
5
|
+
* it does NOT advance HEAD. There is no branch ref to rewind: only the
|
|
6
|
+
* half-applied index/worktree conflict state and the `.git/CHERRY_PICK_HEAD`
|
|
7
|
+
* marker (plus MERGE_MSG / sequencer) to clear. For a non-technical author the
|
|
8
|
+
* safe move is to UNDO the unfinished update and return to the last working
|
|
9
|
+
* state — which is a force-checkout of the current branch + marker cleanup.
|
|
10
|
+
*
|
|
11
|
+
* The shared skeleton in abort-interrupted-operation.ts owns the backup gate,
|
|
12
|
+
* TOCTOU guard, hadLocalChanges capture, fault ordering, checkout, cleanup and
|
|
13
|
+
* re-verify; this module only supplies the cherry-pick-specific config (default
|
|
14
|
+
* branch resolution: ctx.branch → git.currentBranch → "HEAD", HEAD stays
|
|
15
|
+
* attached during a cherry-pick).
|
|
16
|
+
*/
|
|
17
|
+
import type { RecoverFn, StillAppliesFn } from "./types.ts";
|
|
18
|
+
/**
|
|
19
|
+
* Precondition probe (see types.ts `StillAppliesFn`) — the dispatcher's
|
|
20
|
+
* replacement for the abort skeleton's old hand-rolled TOCTOU guard.
|
|
21
|
+
*/
|
|
22
|
+
export declare const stillApplies: StillAppliesFn;
|
|
23
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-interrupted-merge.ts — abort a merge left unfinished.
|
|
3
|
+
*
|
|
4
|
+
* WHY: gutterpress itself never leaves a merge half-done (isomorphic-git's merge
|
|
5
|
+
* either completes or aborts in memory and never writes MERGE_HEAD), so this
|
|
6
|
+
* state comes from OUTSIDE the app: the author ran `git merge`/`git pull` in a
|
|
7
|
+
* terminal, hit conflicts, and walked away. The repo then has
|
|
8
|
+
* `.git/MERGE_HEAD` plus conflict markers in tracked files, and every sync
|
|
9
|
+
* would otherwise snapshot those markers into history. For a non-technical
|
|
10
|
+
* author the safe move is to UNDO the unfinished merge and return to the last
|
|
11
|
+
* working state.
|
|
12
|
+
*
|
|
13
|
+
* A conflicted merge stops BEFORE committing, so — like a cherry-pick and
|
|
14
|
+
* unlike a rebase — HEAD stays attached and no branch ref moved. The abort is a
|
|
15
|
+
* force-checkout of the current branch (resets index + worktree to HEAD) plus
|
|
16
|
+
* removal of the merge marker files. The shared skeleton in
|
|
17
|
+
* abort-interrupted-operation.ts owns the backup gate, TOCTOU guard,
|
|
18
|
+
* hadLocalChanges capture, fault ordering, checkout, cleanup and re-verify; this
|
|
19
|
+
* module only supplies the merge-specific config (default branch resolution:
|
|
20
|
+
* ctx.branch → git.currentBranch → "HEAD").
|
|
21
|
+
*/
|
|
22
|
+
import type { RecoverFn, StillAppliesFn } from "./types.ts";
|
|
23
|
+
/**
|
|
24
|
+
* Precondition probe (see types.ts `StillAppliesFn`) — the dispatcher's
|
|
25
|
+
* replacement for the abort skeleton's old hand-rolled TOCTOU guard.
|
|
26
|
+
*/
|
|
27
|
+
export declare const stillApplies: StillAppliesFn;
|
|
28
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-interrupted-rebase.ts — abort a rebase that was left unfinished.
|
|
3
|
+
*
|
|
4
|
+
* WHY: If gutterpress or the OS dies partway through combining versions, the repo
|
|
5
|
+
* can be left mid-rebase: `.git/rebase-merge/` (merge/interactive backend) or
|
|
6
|
+
* `.git/rebase-apply/` (am backend) exists and HEAD is usually detached at a
|
|
7
|
+
* replay commit. Every sync then fails. There is nothing to "finish" for a
|
|
8
|
+
* non-technical author — the only safe move is to UNDO the unfinished update and
|
|
9
|
+
* return the project to its last working state. That is exactly what an abort is.
|
|
10
|
+
*
|
|
11
|
+
* Unlike a merge/cherry-pick (which stop before committing and only need the
|
|
12
|
+
* current branch reset), a rebase advances HEAD, so the abort must rewind the
|
|
13
|
+
* real branch ref to the pre-rebase commit. The shared skeleton in
|
|
14
|
+
* abort-interrupted-operation.ts owns the backup gate, TOCTOU guard,
|
|
15
|
+
* hadLocalChanges capture, fault ordering, checkout, cleanup and re-verify; this
|
|
16
|
+
* module supplies the rebase-specific `resolveTarget` that:
|
|
17
|
+
* 1. Reads the pre-rebase commit from `<stateDir>/orig-head` (fallback:
|
|
18
|
+
* `.git/ORIG_HEAD`). If it cannot be resolved → THROW (the backup gate
|
|
19
|
+
* converts it to failed_backup_available; the backup is safe).
|
|
20
|
+
* 2. Resolves the branch from `<stateDir>/head-name` — but ONLY when it is a
|
|
21
|
+
* genuine `refs/heads/<x>` path. A sentinel like "detached HEAD" (the
|
|
22
|
+
* rebase started from a detached HEAD) is NOT a branch. Falls back to
|
|
23
|
+
* ctx.branch only when head-name was ABSENT.
|
|
24
|
+
* 3a. Named branch → rewind the branch ref to the pre-rebase commit and
|
|
25
|
+
* force-checkout it (re-attaches HEAD, resets tree + index to the tip).
|
|
26
|
+
* 3b. No branch (detached-HEAD rebase) → force-checkout the pre-rebase commit
|
|
27
|
+
* directly, leaving HEAD detached at it; no branch ref is touched.
|
|
28
|
+
*
|
|
29
|
+
* resolveTarget reads state with DIRECT fs checks (not inspectRepo) — the one
|
|
30
|
+
* narrow fact it needs is which backend dir still exists, not a broad preflight
|
|
31
|
+
* probe that would re-enter the per-repo lock.
|
|
32
|
+
*/
|
|
33
|
+
import type { RecoverFn, StillAppliesFn } from "./types.ts";
|
|
34
|
+
/**
|
|
35
|
+
* Precondition probe (see types.ts `StillAppliesFn`) — the dispatcher's
|
|
36
|
+
* replacement for the abort skeleton's old hand-rolled TOCTOU guard.
|
|
37
|
+
*/
|
|
38
|
+
export declare const stillApplies: StillAppliesFn;
|
|
39
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery handler for merge_conflict — thin wrapper over pullChanges.
|
|
3
|
+
*
|
|
4
|
+
* WHY a thin wrapper: merge_conflict is NOT a broken repo. The working tree is
|
|
5
|
+
* always left clean by pullChanges (abortOnConflict keeps the tree at the
|
|
6
|
+
* pre-merge snapshot). This handler's only job is to translate a
|
|
7
|
+
* `{ status: "conflict" }` PullOutcome into a `{ status: "needs_user" }`
|
|
8
|
+
* RecoveryResult so the host can show the per-file version chooser.
|
|
9
|
+
*
|
|
10
|
+
* No merge logic is re-implemented here. All the snapshot-first invariant,
|
|
11
|
+
* conflict detection, and two-parent merge machinery lives in sync.ts.
|
|
12
|
+
*
|
|
13
|
+
* SAFETY PROPERTIES (tested in recover-merge-conflict.test.ts):
|
|
14
|
+
* 1. Result is 'needs_user' with ConflictFile[] attached.
|
|
15
|
+
* 2. Working file NEVER contains '<<<<<<<' or '>>>>>>>' markers.
|
|
16
|
+
* 3. Remote HEAD + tree UNCHANGED after recover() returns.
|
|
17
|
+
* 4. User-visible local files preserved (snapshot kept both copies).
|
|
18
|
+
* 5. No force-push is attempted (policy mayChangeRemote=false).
|
|
19
|
+
* 6. Confirmation gate is NEVER called (requireConfirmation=false).
|
|
20
|
+
* 7. No backup zip created (createBackup=false — risk is "none").
|
|
21
|
+
*
|
|
22
|
+
* Policy: merge_conflict has risk="none", createBackup=false,
|
|
23
|
+
* requireConfirmation=false. The repair is fully safe and reversible via the
|
|
24
|
+
* snapshot pullChanges takes before attempting the merge.
|
|
25
|
+
*/
|
|
26
|
+
import type { RecoverFn } from "./types.ts";
|
|
27
|
+
/**
|
|
28
|
+
* Translate a sync.ts PullOutcome into a RecoveryResult.
|
|
29
|
+
*
|
|
30
|
+
* - conflict → needs_user (the intended outcome: surface the per-file chooser)
|
|
31
|
+
* - pulled / up-to-date → recovered (the pull succeeded; no conflict after all)
|
|
32
|
+
* - auth / offline / error → needs_user with guidance (pass the guidance through)
|
|
33
|
+
*/
|
|
34
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-missing-git-dir.ts — reclone-and-reattach when .git/ is missing.
|
|
3
|
+
*
|
|
4
|
+
* WHY this is needed:
|
|
5
|
+
* A project folder can lose its .git/ directory if the user copied the
|
|
6
|
+
* project from a zip archive that stripped hidden directories, or if .git/
|
|
7
|
+
* was accidentally deleted. Without .git/, no sync operation can run.
|
|
8
|
+
*
|
|
9
|
+
* What this repair does:
|
|
10
|
+
* 1. Creates a /tmp backup of the current folder (user files preserved).
|
|
11
|
+
* 2. Asks the user to confirm the repair.
|
|
12
|
+
* 3. Clones the remote into a TEMP directory (NEVER into the project dir).
|
|
13
|
+
* 4. Copies only the .git/ metadata from the temp clone into the project dir.
|
|
14
|
+
* 5. Cleans up the temp clone directory.
|
|
15
|
+
* 6. Returns status=recovered with user files intact and .git/ restored.
|
|
16
|
+
*
|
|
17
|
+
* Safety invariants:
|
|
18
|
+
* - The project folder is NEVER deleted or replaced.
|
|
19
|
+
* - The clone target is always a separate temp dir, never projectDir.
|
|
20
|
+
* - User content files (outside .git/) are NEVER overwritten.
|
|
21
|
+
* - No force-push is ever issued.
|
|
22
|
+
* - Backup created and verified BEFORE any repair runs.
|
|
23
|
+
* - Backup failure → failed_no_changes_made, no further writes.
|
|
24
|
+
* - User DENY → blocked, no changes.
|
|
25
|
+
* - Mid-repair failure → failed_backup_available, backup readable, remote unchanged.
|
|
26
|
+
* - No remoteUrl → blocked with guidance (reclone is impossible).
|
|
27
|
+
*/
|
|
28
|
+
import type { RecoverFn, StillAppliesFn } from "./types.ts";
|
|
29
|
+
/**
|
|
30
|
+
* Precondition probe (see types.ts `StillAppliesFn`): `.git/` may have
|
|
31
|
+
* reappeared between classification and dispatch (e.g. the author restored
|
|
32
|
+
* the folder or ran an init in a terminal). The dispatcher calls this INSIDE
|
|
33
|
+
* withRepoLock, immediately before invoking `recover` below, replacing what
|
|
34
|
+
* used to be a hand-rolled upfront check duplicated here.
|
|
35
|
+
*/
|
|
36
|
+
export declare const stillApplies: StillAppliesFn;
|
|
37
|
+
export declare const recover: RecoverFn;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recover-missing-objects.ts — safe fetch attempt for missing/corrupt loose objects.
|
|
3
|
+
*
|
|
4
|
+
* WHY this exists:
|
|
5
|
+
* When isomorphic-git raises a "ReadObjectFail" or similar "missing/corrupt
|
|
6
|
+
* object" error, the repo's on-disk object store has gaps. The safest repair
|
|
7
|
+
* is a fetch from the remote: git.fetch() downloads only the objects the
|
|
8
|
+
* remote has that the local repo is missing. If the remote can fill the gap,
|
|
9
|
+
* the repo becomes healthy again. If there is no remote, or the fetch cannot
|
|
10
|
+
* cover the damage, we stop with guidance and a backup.
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT — why we must verify after fetching:
|
|
13
|
+
* git.fetch() downloads MISSING objects into the pack store. However, it
|
|
14
|
+
* NEVER overwrites an already-existing (corrupt) loose object file on disk.
|
|
15
|
+
* Git's loose-object format uses a content-hash filename; if the file at
|
|
16
|
+
* .git/objects/xx/yyyy... already exists (even if corrupted), git skips it.
|
|
17
|
+
* So a corrupt loose object that the remote also has will remain corrupt on
|
|
18
|
+
* disk after a successful fetch() call. We MUST verify that the object store
|
|
19
|
+
* is healthy after the fetch — if any read still fails, the repair did not
|
|
20
|
+
* work and we stop with needs_user guidance instead of a false 'recovered'.
|
|
21
|
+
*
|
|
22
|
+
* Safety invariants (every test asserts these):
|
|
23
|
+
* I1. We NEVER push — not even a no-op push. Remote history is read-only here.
|
|
24
|
+
* I2. No force-push (we never call git.push at all).
|
|
25
|
+
* I3. Remote HEAD and tree are unchanged after this handler runs.
|
|
26
|
+
* I4. User-visible content files are preserved at all times.
|
|
27
|
+
* I5. A backup zip is created and verified BEFORE any repair attempt.
|
|
28
|
+
* I6. If backup creation fails → failed_no_changes_made; no further writes.
|
|
29
|
+
* I7. If the fetch throws AFTER backup → failed_backup_available; zip readable.
|
|
30
|
+
* I8. If the user denies the confirmation dialog → blocked, no-op.
|
|
31
|
+
* I9. All non-recovered results include ManualGuidance with jargon-free copy.
|
|
32
|
+
*
|
|
33
|
+
* Repair flow (inside withBackupGate's risky callback):
|
|
34
|
+
* 1. If no remote URL is configured → stop with needs_user guidance.
|
|
35
|
+
* 2. ctx.faults?.before("fetch") — fault injection point for tests.
|
|
36
|
+
* 3. git.fetch() from the remote (no push, read-only, tags=false).
|
|
37
|
+
* 4. Verify the repair: resolve HEAD + read commit + read tree. If any read
|
|
38
|
+
* fails, the corrupt object is still on disk → needs_user guidance.
|
|
39
|
+
* 5. If verification succeeds → recovered with the backup path.
|
|
40
|
+
* 6. If fetch throws → withBackupGate catches it → failed_backup_available.
|
|
41
|
+
*
|
|
42
|
+
* No merge logic, no push logic. All structural guard-rails live in
|
|
43
|
+
* withBackupGate (backup → confirm → risky → failsafe on throw).
|
|
44
|
+
*/
|
|
45
|
+
import type { RecoverFn, StillAppliesFn } from "./types.ts";
|
|
46
|
+
/**
|
|
47
|
+
* Precondition probe (see types.ts `StillAppliesFn`): re-probe object-store
|
|
48
|
+
* readability right before the dispatcher hands off to `recover` below,
|
|
49
|
+
* reusing the SAME verifyRepoReadable check the handler itself uses to
|
|
50
|
+
* confirm a fetch actually repaired the store (inspect.ts) — one
|
|
51
|
+
* implementation, not a second copy that could drift. A successful read means
|
|
52
|
+
* the damage is already gone (e.g. a previous fetch attempt already fixed
|
|
53
|
+
* it), so there is nothing left to repair.
|
|
54
|
+
*/
|
|
55
|
+
export declare const stillApplies: StillAppliesFn;
|
|
56
|
+
export declare const recover: RecoverFn;
|