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,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type/interface declarations for snapshot-first sync + conflict resolution
|
|
3
|
+
* (#15, ADR 0006 D5). Extracted from sync.ts so the orchestrator, transport and
|
|
4
|
+
* conflict-resolution modules share ONE definition of each result/option shape.
|
|
5
|
+
* Pure type surface — no runtime code.
|
|
6
|
+
*/
|
|
7
|
+
import type httpNode from "isomorphic-git/http/node";
|
|
8
|
+
import type { HostCredential, TokenStore } from "./token-store.ts";
|
|
9
|
+
/**
|
|
10
|
+
* isomorphic-git object cache, scoped to ONE operation (one function call)
|
|
11
|
+
* and released with it. NEVER share these across operations or hold them in
|
|
12
|
+
* module state: reading any object from a packfile makes isomorphic-git load
|
|
13
|
+
* the ENTIRE pack into the cache (measured ~1.3–3.8 GB RSS on a 2 GB repo),
|
|
14
|
+
* so a long-lived shared cache pins that memory for the life of the process —
|
|
15
|
+
* the root cause of the 0.5.0 "sync uses 2 GB" report.
|
|
16
|
+
*/
|
|
17
|
+
export type GitCache = Record<string, unknown>;
|
|
18
|
+
/** How one conflicted file differs between the two copies. */
|
|
19
|
+
export type ConflictKind =
|
|
20
|
+
/** Edited in both copies. */
|
|
21
|
+
"both-edited"
|
|
22
|
+
/** The author deleted it; the online copy edited it. */
|
|
23
|
+
| "you-deleted"
|
|
24
|
+
/** The online copy deleted it; the author edited it. */
|
|
25
|
+
| "online-deleted";
|
|
26
|
+
/** One file that changed in both the local and the online copy. */
|
|
27
|
+
export interface ConflictFile {
|
|
28
|
+
path: string;
|
|
29
|
+
kind: ConflictKind;
|
|
30
|
+
}
|
|
31
|
+
/** Author's per-file decision for a conflicted file (ADR 0006 D5). */
|
|
32
|
+
export interface ConflictResolution {
|
|
33
|
+
path: string;
|
|
34
|
+
choice: "mine" | "theirs" | "both";
|
|
35
|
+
}
|
|
36
|
+
/** Outcome of a sync (or conflict-resolution) attempt. */
|
|
37
|
+
export type SyncOutcome = {
|
|
38
|
+
status: "synced";
|
|
39
|
+
message: string;
|
|
40
|
+
/** Snapshot taken of unsaved work before syncing, if any. */
|
|
41
|
+
snapshotId?: string;
|
|
42
|
+
/** True when online changes were merged into the local copy. */
|
|
43
|
+
mergedRemoteChanges: boolean;
|
|
44
|
+
/** True when pulling online changes changed the local working tree. */
|
|
45
|
+
filesChanged?: boolean;
|
|
46
|
+
} | {
|
|
47
|
+
status: "up-to-date";
|
|
48
|
+
message: string;
|
|
49
|
+
snapshotId?: string;
|
|
50
|
+
filesChanged?: boolean;
|
|
51
|
+
} | {
|
|
52
|
+
status: "conflict";
|
|
53
|
+
message: string;
|
|
54
|
+
files: ConflictFile[];
|
|
55
|
+
/** Local branch tip the conflict was computed against. */
|
|
56
|
+
localId: string;
|
|
57
|
+
/** Online tip the conflict was computed against. */
|
|
58
|
+
remoteId: string;
|
|
59
|
+
snapshotId?: string;
|
|
60
|
+
} | {
|
|
61
|
+
status: "auth";
|
|
62
|
+
message: string;
|
|
63
|
+
snapshotId?: string;
|
|
64
|
+
filesChanged?: boolean;
|
|
65
|
+
} | {
|
|
66
|
+
status: "offline";
|
|
67
|
+
message: string;
|
|
68
|
+
snapshotId?: string;
|
|
69
|
+
filesChanged?: boolean;
|
|
70
|
+
} | {
|
|
71
|
+
status: "error";
|
|
72
|
+
message: string;
|
|
73
|
+
/**
|
|
74
|
+
* Stable machine-readable signal for the small set of known "the project
|
|
75
|
+
* isn't set up right" failures (no remote / SSH remote / no named
|
|
76
|
+
* branch) — set by conflict-resolution.ts's setupErrorMessage() check.
|
|
77
|
+
* Lets a host UI route to its connect/setup surface without
|
|
78
|
+
* string-matching `message` (which stays free to reword).
|
|
79
|
+
*/
|
|
80
|
+
code?: "needs-connection-setup";
|
|
81
|
+
snapshotId?: string;
|
|
82
|
+
filesChanged?: boolean;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Outcome of a pull-only attempt ({@link pullChanges}): fetch + fast-forward/
|
|
86
|
+
* merge of the online changes, NEVER a push. Conflict semantics are identical
|
|
87
|
+
* to {@link syncProject} (abortOnConflict — the working tree is never left
|
|
88
|
+
* with conflict markers).
|
|
89
|
+
*/
|
|
90
|
+
export type PullOutcome = {
|
|
91
|
+
status: "pulled";
|
|
92
|
+
message: string;
|
|
93
|
+
/** Snapshot taken of unsaved work before pulling, if any. */
|
|
94
|
+
snapshotId?: string;
|
|
95
|
+
/**
|
|
96
|
+
* True when the pull created a combine (merge) commit — local commits
|
|
97
|
+
* existed alongside the online ones. False for a plain fast-forward.
|
|
98
|
+
*/
|
|
99
|
+
merged: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* True when the working tree CONTENT changed (the tip's tree differs
|
|
102
|
+
* from before) — the host should refresh its preview.
|
|
103
|
+
*/
|
|
104
|
+
filesChanged: boolean;
|
|
105
|
+
} | {
|
|
106
|
+
status: "up-to-date";
|
|
107
|
+
message: string;
|
|
108
|
+
snapshotId?: string;
|
|
109
|
+
} | {
|
|
110
|
+
status: "conflict";
|
|
111
|
+
message: string;
|
|
112
|
+
files: ConflictFile[];
|
|
113
|
+
localId: string;
|
|
114
|
+
remoteId: string;
|
|
115
|
+
snapshotId?: string;
|
|
116
|
+
} | {
|
|
117
|
+
status: "auth";
|
|
118
|
+
message: string;
|
|
119
|
+
snapshotId?: string;
|
|
120
|
+
} | {
|
|
121
|
+
status: "offline";
|
|
122
|
+
message: string;
|
|
123
|
+
snapshotId?: string;
|
|
124
|
+
} | {
|
|
125
|
+
status: "error";
|
|
126
|
+
message: string;
|
|
127
|
+
snapshotId?: string;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Outcome of a push-only attempt ({@link pushChanges}): snapshot-if-needed,
|
|
131
|
+
* then push — NEVER a merge. When the online copy has commits this computer
|
|
132
|
+
* doesn't have, the result is the typed `"pull-first"` status (the host shows
|
|
133
|
+
* a plain-language "get the latest changes first" message) — pushChanges
|
|
134
|
+
* never auto-merges.
|
|
135
|
+
*/
|
|
136
|
+
export type PushOutcome = {
|
|
137
|
+
status: "pushed";
|
|
138
|
+
message: string;
|
|
139
|
+
snapshotId?: string;
|
|
140
|
+
} | {
|
|
141
|
+
status: "up-to-date";
|
|
142
|
+
message: string;
|
|
143
|
+
snapshotId?: string;
|
|
144
|
+
} | {
|
|
145
|
+
status: "pull-first";
|
|
146
|
+
message: string;
|
|
147
|
+
snapshotId?: string;
|
|
148
|
+
} | {
|
|
149
|
+
status: "auth";
|
|
150
|
+
message: string;
|
|
151
|
+
snapshotId?: string;
|
|
152
|
+
} | {
|
|
153
|
+
status: "offline";
|
|
154
|
+
message: string;
|
|
155
|
+
snapshotId?: string;
|
|
156
|
+
} | {
|
|
157
|
+
status: "error";
|
|
158
|
+
message: string;
|
|
159
|
+
snapshotId?: string;
|
|
160
|
+
};
|
|
161
|
+
export interface SyncProjectOptions {
|
|
162
|
+
projectDir: string;
|
|
163
|
+
/** Explicit credential; wins over the token store. */
|
|
164
|
+
credential?: HostCredential;
|
|
165
|
+
/** Host-keyed store used to resolve the credential for the remote's host. */
|
|
166
|
+
tokenStore?: TokenStore;
|
|
167
|
+
/** Snapshot message for unsaved work (defaults to a friendly one). */
|
|
168
|
+
message?: string;
|
|
169
|
+
authorName?: string;
|
|
170
|
+
authorEmail?: string;
|
|
171
|
+
/** Injectable git HTTP transport for tests. */
|
|
172
|
+
httpClient?: typeof httpNode;
|
|
173
|
+
/**
|
|
174
|
+
* Bounded retry policy for {@link syncProject}'s pull→push race loop. The
|
|
175
|
+
* loop is ALWAYS bounded (never infinite) and the snapshot-first guarantee
|
|
176
|
+
* holds on every path. Defaults to {@link DEFAULT_SYNC_RETRY}. `sleep` is
|
|
177
|
+
* injectable so tests can drive backoff deterministically.
|
|
178
|
+
*/
|
|
179
|
+
retry?: SyncRetryOptions;
|
|
180
|
+
/**
|
|
181
|
+
* Optional path to a log file for debugging sync/recovery operations.
|
|
182
|
+
* When set, each step (snapshot, fetch, merge, push, conflict) is appended
|
|
183
|
+
* as a timestamped line. Never logs secrets.
|
|
184
|
+
*/
|
|
185
|
+
logFile?: string;
|
|
186
|
+
}
|
|
187
|
+
/** Bounded retry policy for the sync race loop (BUG 6). */
|
|
188
|
+
export interface SyncRetryOptions {
|
|
189
|
+
/** Max pull→push passes before giving up. Clamped to ≥ 1. Default 3. */
|
|
190
|
+
attempts?: number;
|
|
191
|
+
/** Delay between passes, in ms. Clamped to ≥ 0. Default 150. */
|
|
192
|
+
backoffMs?: number;
|
|
193
|
+
/** Injectable delay (tests only); defaults to a real timer. */
|
|
194
|
+
sleep?: (ms: number) => Promise<void>;
|
|
195
|
+
}
|
|
196
|
+
export interface ResolveConflictsOptions {
|
|
197
|
+
projectDir: string;
|
|
198
|
+
resolutions: ConflictResolution[];
|
|
199
|
+
/** The `localId` from the conflict outcome. */
|
|
200
|
+
localId: string;
|
|
201
|
+
/** The `remoteId` from the conflict outcome. */
|
|
202
|
+
remoteId: string;
|
|
203
|
+
credential?: HostCredential;
|
|
204
|
+
tokenStore?: TokenStore;
|
|
205
|
+
authorName?: string;
|
|
206
|
+
authorEmail?: string;
|
|
207
|
+
httpClient?: typeof httpNode;
|
|
208
|
+
/**
|
|
209
|
+
* When true, the merge is allowed to combine two commits that share no
|
|
210
|
+
* common ancestor (unrelated histories). Set by the unrelated-histories
|
|
211
|
+
* recovery path; regular merge conflicts leave this false (the local and
|
|
212
|
+
* remote share a common base, so `allowUnrelatedHistories` is unnecessary).
|
|
213
|
+
*/
|
|
214
|
+
allowUnrelatedHistories?: boolean;
|
|
215
|
+
/** Optional log file for debugging conflict resolution steps. */
|
|
216
|
+
logFile?: string;
|
|
217
|
+
}
|
|
218
|
+
/** Resolved project remote + credential used by every transport call. */
|
|
219
|
+
export interface RemoteTransport {
|
|
220
|
+
remote: string;
|
|
221
|
+
/** Sanitized HTTPS URL (no embedded credentials). */
|
|
222
|
+
url: string;
|
|
223
|
+
host: string;
|
|
224
|
+
credential?: HostCredential;
|
|
225
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { conflictFilesFrom, onAuthFor } from "./transport.ts";
|
|
2
|
+
import type { PullOutcome, PushOutcome, SyncOutcome, SyncProjectOptions, SyncRetryOptions } from "./sync-types.ts";
|
|
3
|
+
export { onAuthFor, conflictFilesFrom };
|
|
4
|
+
export { onlineCopyPath, resolveConflicts } from "./conflict-resolution.ts";
|
|
5
|
+
export { SYNC_SNAPSHOT_MESSAGE } from "./sync-messages.ts";
|
|
6
|
+
export type { ConflictFile, ConflictKind, ConflictResolution, PullOutcome, PushOutcome, ResolveConflictsOptions, SyncOutcome, SyncProjectOptions, SyncRetryOptions, } from "./sync-types.ts";
|
|
7
|
+
/**
|
|
8
|
+
* Default sync retry budget (BUG 6): 3 bounded passes with a short backoff.
|
|
9
|
+
* A fast-moving remote can race the push once or twice without the user seeing
|
|
10
|
+
* the false "Someone else synced at the same moment" message; a remote that
|
|
11
|
+
* genuinely races EVERY pass still terminates with that friendly message and
|
|
12
|
+
* the work safely snapshotted.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_SYNC_RETRY: Required<Omit<SyncRetryOptions, "sleep">>;
|
|
15
|
+
/**
|
|
16
|
+
* Snapshot-first sync (ADR 0006 D5) — the composition of {@link pullChanges}
|
|
17
|
+
* then {@link pushChanges}. If someone pushes between our pull and our push,
|
|
18
|
+
* the push reports pull-first and the pair re-runs (their commits merge in on
|
|
19
|
+
* the next pass), with a short backoff between passes. The loop is ALWAYS
|
|
20
|
+
* bounded by `retry.attempts` (BUG 6 — a fast-moving remote no longer triggers
|
|
21
|
+
* a FALSE race message after only two attempts); a remote that genuinely races
|
|
22
|
+
* every pass surfaces a friendly "try again" rather than looping forever. The
|
|
23
|
+
* snapshot-first guarantee holds on every path (the work is saved locally
|
|
24
|
+
* before any network step). Never throws for expected outcomes — everything is
|
|
25
|
+
* reported through the {@link SyncOutcome} union.
|
|
26
|
+
*/
|
|
27
|
+
export declare function syncProject(options: SyncProjectOptions): Promise<SyncOutcome>;
|
|
28
|
+
/**
|
|
29
|
+
* Pull-only operation (the History tab's "Pull"): snapshot-if-needed →
|
|
30
|
+
* fetch → fast-forward or clean merge of the online changes — NEVER a push.
|
|
31
|
+
*
|
|
32
|
+
* Same conflict semantics as {@link syncProject}: `abortOnConflict` keeps the
|
|
33
|
+
* working tree completely untouched and the conflict comes back as
|
|
34
|
+
* `{ status: "conflict", files }` for the per-file choices dialog. Serialized
|
|
35
|
+
* on the per-repo lock; one function-scoped object cache, released on return.
|
|
36
|
+
*/
|
|
37
|
+
export declare function pullChanges(options: SyncProjectOptions): Promise<PullOutcome>;
|
|
38
|
+
/**
|
|
39
|
+
* Push-only operation (the History tab's "Push"): snapshot-if-needed → push.
|
|
40
|
+
* If the online copy has commits this computer doesn't have (non-fast-forward),
|
|
41
|
+
* it does NOT auto-merge — the typed `"pull-first"` result tells the host to
|
|
42
|
+
* show a plain-language "get the latest changes first" message. Serialized on
|
|
43
|
+
* the per-repo lock; one function-scoped object cache, released on return.
|
|
44
|
+
*/
|
|
45
|
+
export declare function pushChanges(options: SyncProjectOptions): Promise<PushOutcome>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import httpNode from "isomorphic-git/http/node";
|
|
2
|
+
import { type HostCredential } from "./token-store.ts";
|
|
3
|
+
/** Why a remote-access probe failed, in machine-readable form. */
|
|
4
|
+
export type RemoteAccessFailureReason = "auth" | "not-found" | "unreachable" | "ssh-unsupported" | "insecure-transport" | "tls" | "unknown";
|
|
5
|
+
export type RemoteAccessResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
defaultBranch?: string;
|
|
8
|
+
refCount: number;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
reason: RemoteAccessFailureReason;
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
export interface TestRemoteAccessOptions {
|
|
15
|
+
/** The remote URL to probe (HTTPS; SSH is classified, never contacted). */
|
|
16
|
+
url: string;
|
|
17
|
+
/** Credential for the remote's host, if one is stored. */
|
|
18
|
+
credential?: HostCredential;
|
|
19
|
+
/** Injectable git HTTP transport for tests. Defaults to the node client. */
|
|
20
|
+
httpClient?: typeof httpNode;
|
|
21
|
+
/** Probe timeout. The probe resolves `unreachable` when it elapses. */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* True for any remote URL gutterpress's HTTPS-only transport cannot use:
|
|
26
|
+
* `ssh://…` and the scp-like `git@host:owner/repo.git` shorthand.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSshRemoteUrl(url: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Author-friendly messages per failure reason (no URLs, no tokens — and no
|
|
31
|
+
* literal scheme tokens like "http://": the desktop's Advanced Setup dialog
|
|
32
|
+
* redacts anything matching /https?:\/\/\S+/, which would garble the copy).
|
|
33
|
+
* Exported for the wording guard in test-access.test.ts.
|
|
34
|
+
*/
|
|
35
|
+
export declare const FAILURE_MESSAGES: Record<RemoteAccessFailureReason, string>;
|
|
36
|
+
/** Map a raw transport error to a classified failure. Never echoes the URL. */
|
|
37
|
+
export declare function classifyRemoteAccessError(e: unknown): RemoteAccessResult;
|
|
38
|
+
/**
|
|
39
|
+
* Probe a remote with a single refs listing (the `git ls-remote` equivalent).
|
|
40
|
+
*
|
|
41
|
+
* - Never throws — every outcome is a classified {@link RemoteAccessResult}.
|
|
42
|
+
* - SSH URLs return `ssh-unsupported` with zero network traffic.
|
|
43
|
+
* - Credentials embedded in the URL are stripped and used for auth when no
|
|
44
|
+
* explicit credential is supplied (ADR 0006 D7) — they never leak onward.
|
|
45
|
+
* - `defaultBranch` comes from the server's `HEAD` symref when advertised.
|
|
46
|
+
*/
|
|
47
|
+
export declare function testRemoteAccess(options: TestRemoteAccessOptions): Promise<RemoteAccessResult>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type httpNode from "isomorphic-git/http/node";
|
|
2
|
+
type HttpClient = typeof httpNode;
|
|
3
|
+
export declare function pkt(data: string | Buffer): Buffer;
|
|
4
|
+
export declare const FLUSH: Buffer<ArrayBuffer>;
|
|
5
|
+
export declare function parsePktLines(body: Buffer): string[];
|
|
6
|
+
/**
|
|
7
|
+
* isomorphic-git location options: a worktree repo (`dir`) or a bare repo
|
|
8
|
+
* (`gitdir`). Bare support exists so the server can serve a real `--bare`
|
|
9
|
+
* clone (used by the large-repo sync repro scripts).
|
|
10
|
+
*/
|
|
11
|
+
type RepoLoc = {
|
|
12
|
+
dir: string;
|
|
13
|
+
} | {
|
|
14
|
+
gitdir: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function createFixtureRepo(dir: string): Promise<{
|
|
17
|
+
head: string;
|
|
18
|
+
first: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Collect commit+tree+blob oids reachable from `commit`, to `depth` commits.
|
|
22
|
+
*
|
|
23
|
+
* `stopCommits` (the client's `have` lines) bounds the walk like a real
|
|
24
|
+
* upload-pack: commits the client already has are neither read nor included.
|
|
25
|
+
* `skipTrees` (the root trees of those have-commits) prunes tree recursion so
|
|
26
|
+
* a fetch of message-only commits on a multi-GB repo packs ONLY the new
|
|
27
|
+
* commit objects instead of re-walking the whole project tree.
|
|
28
|
+
*/
|
|
29
|
+
export declare function collectOids(repo: RepoLoc, commit: string, depth: number, opts?: {
|
|
30
|
+
stopCommits?: Set<string>;
|
|
31
|
+
skipTrees?: Set<string>;
|
|
32
|
+
}): Promise<string[]>;
|
|
33
|
+
export interface GitServer {
|
|
34
|
+
url: string;
|
|
35
|
+
/** Authorization headers the server saw, in order. */
|
|
36
|
+
authHeaders: Array<string | undefined>;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface GitServerOptions {
|
|
40
|
+
requireAuth?: {
|
|
41
|
+
username: string;
|
|
42
|
+
password: string;
|
|
43
|
+
};
|
|
44
|
+
/** Answer every request with 404 (a host with no repo at that path). */
|
|
45
|
+
notFound?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare function startGitServer(repoDir: string, opts?: GitServerOptions): Promise<GitServer>;
|
|
48
|
+
export declare function tempDir(prefix: string): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* An http client that delivers the git-upload-pack response but ERRORS the
|
|
51
|
+
* body instead of completing it — the shape a defaultGitHttp idle-timeout trip
|
|
52
|
+
* takes mid-pack. isomorphic-git 1.38.4 updates refs/remotes/<remote>/* from
|
|
53
|
+
* the ref advertisement BEFORE the packfile is collected/persisted, so this
|
|
54
|
+
* reproduces "refs moved, objects never landed" deterministically (the R15
|
|
55
|
+
* dangling-tracking-ref failure). Shared by the transport and recovery tests.
|
|
56
|
+
*/
|
|
57
|
+
export declare function packDroppingClient(inner: HttpClient): HttpClient;
|
|
58
|
+
/**
|
|
59
|
+
* Binary-safe pkt-line command parser for a receive-pack request body:
|
|
60
|
+
* returns the `<old> <new> <ref>` commands and the byte offset where the raw
|
|
61
|
+
* packfile starts (right after the flush-pkt).
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseReceivePackRequest(body: Buffer): {
|
|
64
|
+
commands: Array<{
|
|
65
|
+
oldOid: string;
|
|
66
|
+
newOid: string;
|
|
67
|
+
ref: string;
|
|
68
|
+
}>;
|
|
69
|
+
packfile: Buffer;
|
|
70
|
+
};
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** An opaque stored credential for one remote host. */
|
|
2
|
+
export interface HostCredential {
|
|
3
|
+
/** Remote host the credential authenticates against, e.g. "github.com". */
|
|
4
|
+
host: string;
|
|
5
|
+
/** How the credential was acquired (drives re-auth UX, not transport). */
|
|
6
|
+
kind: "github-oauth" | "token";
|
|
7
|
+
/** The secret token value. NEVER log this. */
|
|
8
|
+
token: string;
|
|
9
|
+
/** Login/username associated with the token, when known. */
|
|
10
|
+
username?: string;
|
|
11
|
+
/** Optional human label ("GitHub — @octocat"). */
|
|
12
|
+
label?: string;
|
|
13
|
+
/** Epoch ms the credential was stored. */
|
|
14
|
+
createdAt: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Host-keyed credential vault contract (ADR 0006 D3 layer 2). Implementations
|
|
18
|
+
* are provided by the HOST APP (Electron safeStorage, CLI 0600 file); the lib
|
|
19
|
+
* only consumes this interface.
|
|
20
|
+
*/
|
|
21
|
+
export interface TokenStore {
|
|
22
|
+
get(host: string): Promise<HostCredential | null>;
|
|
23
|
+
set(host: string, credential: HostCredential): Promise<void>;
|
|
24
|
+
delete(host: string): Promise<void>;
|
|
25
|
+
/** All stored credentials (used by "connected accounts" UIs). */
|
|
26
|
+
list(): Promise<HostCredential[]>;
|
|
27
|
+
}
|
|
28
|
+
/** A credential with the token value masked — safe for logs/diagnostics. */
|
|
29
|
+
export declare function redactCredential(cred: HostCredential): Omit<HostCredential, "token"> & {
|
|
30
|
+
token: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* THE canonical credential-store key for a remote host — the ONE derivation
|
|
34
|
+
* every writer (GitHub device flow, generic connect, embedded-URL migration)
|
|
35
|
+
* and every reader (diagnose, transport) must share. Historically each site
|
|
36
|
+
* derived its own key (the device flow hardcoded `github.com`, the URL
|
|
37
|
+
* migration dropped `:port`, generic connect kept `www.`), so a credential
|
|
38
|
+
* stored by one flow could be invisible to the lookup of another — the
|
|
39
|
+
* "connected successfully but never syncable" defect class.
|
|
40
|
+
*
|
|
41
|
+
* Accepts a bare hostname ("Git.Example.com"), a host:port pair
|
|
42
|
+
* ("git.example.com:3000"), any URL on the host, or an scp-like SSH address
|
|
43
|
+
* ("git@host:owner/repo.git"). Returns `hostname[:port]` lower-cased with any
|
|
44
|
+
* leading `www.` stripped (so `www.github.com` remotes find the `github.com`
|
|
45
|
+
* device-flow credential). The port is kept ONLY when explicit and
|
|
46
|
+
* non-default — `new URL` already drops :443/:80 for https/http. Returns ""
|
|
47
|
+
* when nothing usable remains.
|
|
48
|
+
*/
|
|
49
|
+
export declare function credentialHostKey(hostOrUrl: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the Gutterpress user config directory (where the CLI token store
|
|
52
|
+
* lives). There is no pre-existing lib config-dir mechanism to follow (the CLI
|
|
53
|
+
* config cascade is per-project manifest based), so this establishes the
|
|
54
|
+
* standard one: `$GUTTERPRESS_CONFIG_DIR` override → `%APPDATA%/gutterpress` on
|
|
55
|
+
* Windows → `$XDG_CONFIG_HOME/gutterpress` → `~/.config/gutterpress`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function defaultConfigDir(): string;
|
|
58
|
+
/**
|
|
59
|
+
* The CLI's {@link TokenStore}: a JSON file with `0600` permissions under the
|
|
60
|
+
* user config dir. Plaintext-at-rest by design (the `gh`/`npm` model) — the
|
|
61
|
+
* desktop uses an OS-keychain-backed store instead.
|
|
62
|
+
*/
|
|
63
|
+
export declare class FileTokenStore implements TokenStore {
|
|
64
|
+
readonly filePath: string;
|
|
65
|
+
private queue;
|
|
66
|
+
constructor(filePath?: string);
|
|
67
|
+
private read;
|
|
68
|
+
private write;
|
|
69
|
+
private enqueue;
|
|
70
|
+
get(host: string): Promise<HostCredential | null>;
|
|
71
|
+
set(host: string, credential: HostCredential): Promise<void>;
|
|
72
|
+
delete(host: string): Promise<void>;
|
|
73
|
+
list(): Promise<HostCredential[]>;
|
|
74
|
+
}
|
|
75
|
+
/** Result of {@link extractUrlCredential}. */
|
|
76
|
+
export interface UrlCredentialExtraction {
|
|
77
|
+
/** The URL with any userinfo (user:token@) stripped. */
|
|
78
|
+
cleanUrl: string;
|
|
79
|
+
/** Credential recovered from the URL's userinfo, if any. */
|
|
80
|
+
credential?: HostCredential;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Detect a token embedded in a remote URL (`https://user:tok@host/…` — common
|
|
84
|
+
* in the wild), returning the sanitized URL plus the recovered credential so
|
|
85
|
+
* callers can migrate it into the {@link TokenStore}. The token value must
|
|
86
|
+
* never be echoed into logs or diagnostics — only the clean URL is safe to
|
|
87
|
+
* display. Non-HTTP(S) or unparseable URLs pass through unchanged.
|
|
88
|
+
*/
|
|
89
|
+
export declare function extractUrlCredential(url: string): UrlCredentialExtraction;
|
|
90
|
+
/**
|
|
91
|
+
* Migrate any credential embedded in `url` into `store` (only when the store
|
|
92
|
+
* has no existing credential for that host — a stored credential is fresher
|
|
93
|
+
* than one fossilized in a clone URL) and return the sanitized URL.
|
|
94
|
+
*/
|
|
95
|
+
export declare function migrateUrlCredential(url: string, store: TokenStore): Promise<string>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import httpNode from "isomorphic-git/http/node";
|
|
2
|
+
import { type HostCredential, type TokenStore } from "./token-store.ts";
|
|
3
|
+
import type { ConflictFile, GitCache, RemoteTransport } from "./sync-types.ts";
|
|
4
|
+
/**
|
|
5
|
+
* The git repo directory for a project dir. A project IS its git repo, so this
|
|
6
|
+
* walks up to the enclosing repo root (opening a subfolder syncs the whole
|
|
7
|
+
* repo — plain git, no per-book scoping). Anything unclassifiable is itself.
|
|
8
|
+
*/
|
|
9
|
+
export declare function repoDirFor(projectDir: string): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* True when a stored credential may be transmitted to `url`: over https to
|
|
12
|
+
* anywhere, or over http ONLY to loopback (a local git daemon or the in-memory
|
|
13
|
+
* test server). Deep-analysis SECURITY fix: the protocol gates elsewhere accept
|
|
14
|
+
* http:// too, so without this a repo-scoped account token was sent as cleartext
|
|
15
|
+
* Basic auth to a remote http host — harvestable by anyone on the path. Loopback
|
|
16
|
+
* http carries no network exposure, so it stays allowed.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isCredentialTransmissionSafe(url: string): boolean;
|
|
19
|
+
export declare function onAuthFor(credential: HostCredential | undefined): {
|
|
20
|
+
onAuth?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
onAuth: (url: string) => {
|
|
23
|
+
username: string;
|
|
24
|
+
password: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the project's tracked remote + credential. Throws friendly errors
|
|
29
|
+
* for the no-remote / SSH cases (the UI should have gated on diagnose, but
|
|
30
|
+
* the lib must stay safe to call directly).
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveTransport(dir: string, options: {
|
|
33
|
+
credential?: HostCredential;
|
|
34
|
+
tokenStore?: TokenStore;
|
|
35
|
+
}): Promise<RemoteTransport>;
|
|
36
|
+
/**
|
|
37
|
+
* The failure arms shared verbatim by {@link SyncOutcome}, {@link PullOutcome}
|
|
38
|
+
* and {@link PushOutcome} — so one classifier serves all three operations.
|
|
39
|
+
* Decoding delegates to the shared recovery classifier
|
|
40
|
+
* (classifyTransportFailure): auth_required → "auth", network_unavailable →
|
|
41
|
+
* "offline", insecure_transport → "error" with its dedicated message (NEVER
|
|
42
|
+
* "auth" — reconnecting can't fix an http:// address, and the auth recovery
|
|
43
|
+
* path deletes the stored credential), anything else → the generic "error" arm.
|
|
44
|
+
*/
|
|
45
|
+
export declare function failureOutcome(e: unknown, snapshotId?: string): {
|
|
46
|
+
status: "auth" | "offline" | "error";
|
|
47
|
+
message: string;
|
|
48
|
+
snapshotId?: string;
|
|
49
|
+
};
|
|
50
|
+
export declare function conflictFilesFrom(data: {
|
|
51
|
+
filepaths: string[];
|
|
52
|
+
bothModified: string[];
|
|
53
|
+
deleteByUs: string[];
|
|
54
|
+
deleteByTheirs: string[];
|
|
55
|
+
}): ConflictFile[];
|
|
56
|
+
/** Friendly setup-problem message for the expected gate errors, else null. */
|
|
57
|
+
export declare function setupErrorMessage(e: unknown): string | null;
|
|
58
|
+
/**
|
|
59
|
+
* Snapshot-first step shared by syncProject / pullChanges / pushChanges
|
|
60
|
+
* (ADR 0006 D5): commit any unsaved work in the WHOLE repo BEFORE any network
|
|
61
|
+
* or merge step, so a forced post-merge checkout can never discard it. The
|
|
62
|
+
* working-tree check runs lazily at action time on the caller's function-scoped
|
|
63
|
+
* object cache (released with the operation).
|
|
64
|
+
*/
|
|
65
|
+
export declare function snapshotBeforeAction(args: {
|
|
66
|
+
projectDir: string;
|
|
67
|
+
dir: string;
|
|
68
|
+
message?: string;
|
|
69
|
+
authorName?: string;
|
|
70
|
+
authorEmail?: string;
|
|
71
|
+
cache: GitCache;
|
|
72
|
+
}): Promise<string | undefined>;
|
|
73
|
+
export declare function currentBranchOrThrow(dir: string): Promise<string>;
|
|
74
|
+
/**
|
|
75
|
+
* Run `fn` (a fetch that moves remote-tracking refs) with a rollback guard
|
|
76
|
+
* (deep-analysis R15): isomorphic-git updates refs/remotes/<remote>/* from
|
|
77
|
+
* the ref advertisement BEFORE collecting the packfile, so an abort
|
|
78
|
+
* mid-transfer (e.g. the defaultGitHttp idle timeout) leaves refs pointing
|
|
79
|
+
* at oids with no local object. Such a dangling ref poisons the next fetch —
|
|
80
|
+
* zero `have`s → the server streams the ENTIRE repository (the OOM
|
|
81
|
+
* fetchRemoteTip's `ref` choice exists to prevent) — and resolving it reports
|
|
82
|
+
* missing-object "corruption" on a never-corrupt repo.
|
|
83
|
+
*
|
|
84
|
+
* `listRefs` names the refs at risk; it runs again after a throw so refs
|
|
85
|
+
* CREATED by `fn` are covered too. If `fn` throws, every ref that moved to an
|
|
86
|
+
* oid whose object is MISSING locally is restored to its previous oid (or
|
|
87
|
+
* deleted if it didn't exist); refs whose objects DID land are kept — the
|
|
88
|
+
* pack made it. On success no ref is touched. Restoration is best-effort,
|
|
89
|
+
* per ref, and never masks `fn`'s error.
|
|
90
|
+
*/
|
|
91
|
+
export declare function guardRefs<T>(dir: string, listRefs: () => Promise<string[]>, cache: GitCache, fn: () => Promise<T>): Promise<T>;
|
|
92
|
+
/** Single-ref form of {@link guardRefs} — guards one remote-tracking ref. */
|
|
93
|
+
export declare function guardTrackingRef<T>(dir: string, ref: string, cache: GitCache, fn: () => Promise<T>): Promise<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Remote-wide form of {@link guardRefs} for a fetch that may move or create
|
|
96
|
+
* ANY refs/remotes/<remote>/* ref (e.g. `singleBranch: false`).
|
|
97
|
+
*/
|
|
98
|
+
export declare function guardRemoteRefs<T>(dir: string, remote: string, cache: GitCache, fn: () => Promise<T>): Promise<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Fetch the tracked branch's online tip. Returns `null` when the online
|
|
101
|
+
* repository has no such branch yet (a freshly created empty repo).
|
|
102
|
+
*/
|
|
103
|
+
export declare function fetchRemoteTip(dir: string, branch: string, transport: RemoteTransport, http: typeof httpNode, cache: GitCache): Promise<string | null>;
|
|
104
|
+
/** First 8 chars of an oid for diagnostic log lines (null-safe). */
|
|
105
|
+
export declare function short(oid: string | null | undefined): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared slug helper — turn a human name / URL into a safe, lowercase,
|
|
3
|
+
* hyphen-separated id usable as a folder or filename stem.
|
|
4
|
+
*
|
|
5
|
+
* One implementation, two historical call sites (DRY): the theme manager
|
|
6
|
+
* (`themes/<id>/`) and project scaffolding (project folder / output filename).
|
|
7
|
+
* They differed only in the empty-input fallback, so that is the single
|
|
8
|
+
* parameter — everything else (NFKD normalise, diacritic strip, collapse
|
|
9
|
+
* non-alphanumerics to single hyphens, trim edge hyphens) is identical.
|
|
10
|
+
*
|
|
11
|
+
* Bundle-safe (CLAUDE.md §1/§3): pure string work, no deps.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Slugify `name`. When the input contains no usable characters the result is
|
|
15
|
+
* `fallback` (defaults to `""` — callers that need a placeholder pass one, e.g.
|
|
16
|
+
* theme ids pass `"theme"`).
|
|
17
|
+
*/
|
|
18
|
+
export declare function slugify(name: string, fallback?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Prettify a slug / filename stem into a display name: `-`/`_` runs become
|
|
21
|
+
* spaces and the first character is upper-cased ("stat-block" → "Stat block",
|
|
22
|
+
* "my_cool-theme" → "My cool theme"). The rough inverse of {@link slugify} used
|
|
23
|
+
* to label themes, snippets and custom templates that carry only a slug id.
|
|
24
|
+
*
|
|
25
|
+
* One implementation, several historical call sites (theme manager, snippets,
|
|
26
|
+
* project templates) — bundle-safe pure string work, no deps.
|
|
27
|
+
*/
|
|
28
|
+
export declare function prettify(slug: string): string;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Folder (relative to the project root) snippets live in. */
|
|
2
|
+
export declare const SNIPPETS_DIR = "snippets";
|
|
3
|
+
/** One snippet's metadata for the picker (no body — read lazily). */
|
|
4
|
+
export interface SnippetEntry {
|
|
5
|
+
/** Display name (derived from the `.md` filename stem, prettified). */
|
|
6
|
+
name: string;
|
|
7
|
+
/** The on-disk filename, e.g. `callout.md`. Stable id for read/delete. */
|
|
8
|
+
fileName: string;
|
|
9
|
+
/** Distinct `{{variable}}` names parsed from the body, in first-seen order. */
|
|
10
|
+
variables: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse the distinct `{{variable}}` placeholder names from a template, in the
|
|
14
|
+
* order they first appear. Whitespace inside the braces is ignored. Pure.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractVariables(template: string): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Replace every `{{name}}` placeholder with `values[name]`. A name with no
|
|
19
|
+
* provided value becomes the empty string (the caller prompts for values, so an
|
|
20
|
+
* unanswered field simply collapses). Non-placeholder braces are left intact.
|
|
21
|
+
* Pure.
|
|
22
|
+
*/
|
|
23
|
+
export declare function substituteVariables(template: string, values: Record<string, string>): string;
|
|
24
|
+
/**
|
|
25
|
+
* List the project's snippets (newest filesystem order is not guaranteed; sort
|
|
26
|
+
* for the picker). Returns `[]` when the `snippets/` folder doesn't exist.
|
|
27
|
+
*/
|
|
28
|
+
export declare function listSnippets(projectDir: string): Promise<SnippetEntry[]>;
|
|
29
|
+
/** Read one snippet's raw body. Refuses path traversal. */
|
|
30
|
+
export declare function readSnippet(projectDir: string, fileName: string): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Save a snippet body under `snippets/<slug(name)>.md`, creating the folder when
|
|
33
|
+
* absent. Returns the stored entry (with its filename + parsed variables). The
|
|
34
|
+
* returned `name` echoes the author-supplied name, while `fileName` is the
|
|
35
|
+
* slugified storage name.
|
|
36
|
+
*/
|
|
37
|
+
export declare function saveSnippet(projectDir: string, name: string, body: string): Promise<SnippetEntry>;
|
|
38
|
+
/** Delete a snippet by filename. Refuses path traversal. */
|
|
39
|
+
export declare function deleteSnippet(projectDir: string, fileName: string): Promise<void>;
|