gutterpress 0.9.0-alpha.2 → 0.10.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -4
- package/dist/api/index.d.ts +11 -5
- package/dist/api/index.js +20 -15
- package/dist/assets/preview/scripts/preview-bridge.d.ts +1 -0
- package/dist/assets/preview/scripts/preview-interface.d.ts +1 -0
- package/dist/{audit-nhn2pjz3.js → audit-xxegv0t0.js} +10 -7
- package/dist/{build-san7fv2z.js → build-w0vb6mgq.js} +14 -8
- package/dist/checks/source/index.d.ts +1 -0
- package/dist/checks/source/layout-markers.d.ts +3 -0
- package/dist/checks/source/local-ref-parser.d.ts +28 -0
- package/dist/cli-46ycxe6r.js +18 -0
- package/dist/{cli-k4bd06sd.js → cli-5czby0dd.js} +4986 -5889
- package/dist/cli-c41yr7he.js +241 -0
- package/dist/{cli-e5zhb0xs.js → cli-f4rcbt2t.js} +17 -24
- package/dist/{cli-najycadg.js → cli-gk3wsdpb.js} +69 -253
- package/dist/{index-yzrh708h.js → cli-k1065rkg.js} +2 -5
- package/dist/{cli-506tg37g.js → cli-revgt4pr.js} +2 -1
- package/dist/{cli-wchtvxvw.js → cli-v5mp7a6q.js} +14 -1
- package/dist/cli-yp3p0sf2.js +2343 -0
- package/dist/cli.js +20 -18
- package/dist/{doctor-hrk0kxxz.js → doctor-ctgn1dnt.js} +4 -2
- package/dist/engine/compiler/build.d.ts +139 -0
- package/dist/engine/compiler/postprocess.d.ts +23 -0
- package/dist/engine/compiler/tier2.d.ts +78 -0
- package/dist/engine/shared/cdp.d.ts +104 -0
- package/dist/engine/shared/content-value.d.ts +64 -0
- package/dist/engine/shared/gcpm-extract.d.ts +119 -0
- package/dist/engine/shared/margin-box-support.d.ts +12 -0
- package/dist/engine/shared/pdf-inspect.d.ts +24 -0
- package/dist/engine/shared/synthesis.d.ts +155 -0
- package/dist/engine-eng4xh2q.js +41 -0
- package/dist/engine-q5skhgms.js +40 -0
- package/dist/gutterpress-agent-1ctgfz92.js +576 -0
- package/dist/gutterpress-viewer-cem7dmr5.js +2349 -0
- package/dist/index-a4kr77td.js +1907 -0
- package/dist/{cli-yzrh708h.js → index-mdefp0y5.js} +1 -1
- package/dist/index-mvehxh0e.js +708 -0
- package/dist/{index-bynn850m.js → index-s416qpdv.js} +3049 -4015
- package/dist/{index-wchtvxvw.js → index-v5mp7a6q.js} +14 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +24 -17
- package/dist/lib/asset-inline.d.ts +36 -0
- package/dist/lib/browser-pool.d.ts +18 -0
- package/dist/lib/build-error.d.ts +1 -1
- package/dist/lib/build-preflight.d.ts +35 -2
- package/dist/lib/build-runner.d.ts +45 -17
- package/dist/lib/build-staging.d.ts +9 -55
- package/dist/lib/cli-args.d.ts +8 -0
- package/dist/lib/desktop.d.ts +2 -2
- package/dist/lib/embedded-assets.d.ts +1 -1
- package/dist/lib/engine.d.ts +33 -0
- package/dist/lib/ghostscript.d.ts +46 -1
- package/dist/lib/markdown/assemble.d.ts +18 -10
- package/dist/lib/markdown/gp-pin-scope.d.ts +1 -0
- package/dist/lib/markdown/gutterpress-css.d.ts +125 -0
- package/dist/lib/markdown/images.d.ts +26 -0
- package/dist/lib/markdown/index.d.ts +4 -2
- package/dist/lib/markdown/inline-source.d.ts +10 -0
- package/dist/lib/markdown/markers.d.ts +32 -0
- package/dist/lib/markdown/renderer.d.ts +8 -3
- package/dist/lib/markdown/source-range.d.ts +61 -0
- package/dist/lib/missing-asset-placeholder.d.ts +52 -0
- package/dist/lib/presets.d.ts +1 -1
- package/dist/lib/printsafe.d.ts +2 -3
- package/dist/lib/remote-auth/converge-merge.d.ts +48 -0
- package/dist/lib/remote-auth/image-clash.d.ts +17 -0
- package/dist/lib/remote-auth/recovery/classify.d.ts +55 -67
- package/dist/lib/remote-auth/recovery/inspect.d.ts +14 -13
- package/dist/lib/remote-auth/recovery/locks.d.ts +20 -0
- package/dist/lib/remote-auth/recovery/repair.d.ts +29 -0
- package/dist/lib/remote-auth/recovery/types.d.ts +8 -204
- package/dist/lib/remote-auth/sync-messages.d.ts +2 -3
- package/dist/lib/remote-auth/sync-types.d.ts +35 -63
- package/dist/lib/remote-auth/sync.d.ts +11 -18
- package/dist/lib/remote-auth/transport.d.ts +14 -7
- package/dist/lib/theme-import.d.ts +4 -5
- package/dist/{lint-96j9hrj4.js → lint-gsh6qwrq.js} +10 -7
- package/dist/{manifest.schema-z61rzw44.json → manifest.schema-zxgxnbg7.json} +21 -0
- package/dist/{new-8p38wavc.js → new-nkhycqy4.js} +12 -8
- package/dist/{plugin-ees6nhkc.js → plugin-129wcs93.js} +10 -7
- package/dist/{preflight-1q6c2edh.js → preflight-41hcejg9.js} +10 -7
- package/dist/preview/file-watcher.d.ts +13 -17
- package/dist/preview/lifecycle.d.ts +1 -1
- package/dist/{pagedjs-bridge-vn4hk9fx.js → preview-bridge-fz7vpk8m.js} +8 -0
- package/dist/preview-interface-fnqb2y4v.js +1015 -0
- package/dist/{preview-y5a2zen1.js → preview-pftsvr9z.js} +16 -9
- package/dist/preview-shell-c5mfa3q0.js +346 -0
- package/dist/{project-source-p0gn1wd5.js → project-source-ekcyp63q.js} +1 -1
- package/dist/{publish-rm9yb3wh.js → publish-dnjvb4jg.js} +10 -7
- package/dist/render.d.ts +3 -4
- package/dist/render.js +728 -64
- package/dist/{repair-zgq7q2g6.js → repair-a3enjttn.js} +45 -79
- package/dist/schema/manifest.types.d.ts +38 -0
- package/dist/{source-provider-c1rjm2c0.js → source-provider-3tcj6qg2.js} +2 -2
- package/dist/source-provider-vanafrt9.js +40 -0
- package/dist/{theme-zz2ktzqs.css → theme-h5recz6c.css} +8 -7
- package/dist/{theme-570zmh2t.css → theme-j2bagrfx.css} +8 -7
- package/dist/{theme-nya4nqh6.css → theme-nn6d53zy.css} +8 -7
- package/dist/types.d.ts +7 -0
- package/dist/{validate-nr0xa6sa.js → validate-ph1xffqy.js} +10 -7
- package/package.json +6 -6
- package/dist/cli-yja077f6.js +0 -92
- package/dist/git-http-yrb4ag6z.js +0 -17
- package/dist/index-yja077f6.js +0 -92
- package/dist/lib/markdown/markdown-it-paged.d.ts +0 -30
- package/dist/lib/pagedjs-marker.d.ts +0 -42
- package/dist/lib/pagedjs.d.ts +0 -26
- package/dist/lib/pagination.d.ts +0 -149
- package/dist/lib/remote-auth/conflict-resolution.d.ts +0 -29
- package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +0 -103
- package/dist/lib/remote-auth/recovery/backup.d.ts +0 -113
- package/dist/lib/remote-auth/recovery/context.d.ts +0 -47
- package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -28
- package/dist/lib/remote-auth/recovery/failsafe.d.ts +0 -33
- package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +0 -28
- package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +0 -59
- package/dist/lib/remote-auth/recovery/policy.d.ts +0 -47
- package/dist/lib/remote-auth/recovery/recover-auth.d.ts +0 -42
- package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +0 -37
- package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +0 -40
- package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +0 -70
- package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +0 -23
- package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +0 -28
- package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +0 -39
- package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +0 -34
- package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +0 -37
- package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +0 -56
- package/dist/lib/remote-auth/recovery/recover-network.d.ts +0 -34
- package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +0 -27
- package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -68
- package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +0 -44
- package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +0 -35
- package/dist/lib/remote-auth/resolution-plan.d.ts +0 -64
- package/dist/paged.polyfill-n95pbxfn.js +0 -33288
- package/dist/pagedjs-interface-qxvzgwd7.js +0 -557
- package/dist/preview-shell-6dqexx1m.js +0 -581
- /package/dist/assets/{preview/scripts/pagedjs-bridge.d.ts → engine/gutterpress-agent.d.ts} +0 -0
- /package/dist/assets/{preview/scripts/pagedjs-interface.d.ts → engine/gutterpress-viewer.d.ts} +0 -0
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export default function plugin(md: any, pluginOptions?: {}): void;
|
|
2
|
-
/**
|
|
3
|
-
* Minimal Paged.js-friendly CSS for the classes this plugin emits.
|
|
4
|
-
* Consumers should inject this into <head> after their user stylesheets so
|
|
5
|
-
* the layout contract (page/section/column breaks) wins at equal specificity.
|
|
6
|
-
*
|
|
7
|
-
* Also ships five author-facing image/block utility classes (CLAUDE.md §0 —
|
|
8
|
-
* a behavior broadly useful to non-technical authors belongs in core, not a
|
|
9
|
-
* project layer; see UX finding M17). markdown-it-attrs is bundled by
|
|
10
|
-
* default (renderer.ts), so `{.full-bleed}` already attaches
|
|
11
|
-
* the class to the rendered `<img>` — these rules are what make that class
|
|
12
|
-
* actually do something print-safe:
|
|
13
|
-
*
|
|
14
|
-
* .center — centers a block element (margin-inline: auto).
|
|
15
|
-
* .float-left — floats left with clearance margins.
|
|
16
|
-
* .float-right — floats right with clearance margins.
|
|
17
|
-
* .full-width — fills the page's content width (100%).
|
|
18
|
-
* .full-bleed — forces its own page (break-before) and cancels the
|
|
19
|
-
* page's LEFT/RIGHT margins via Paged.js's real
|
|
20
|
-
* `--pagedjs-margin-left`/`--pagedjs-margin-right` custom
|
|
21
|
-
* properties (set per-page by the polyfill from the active
|
|
22
|
-
* `@page` rule — see pagedjs/src/polisher/base.js), so
|
|
23
|
-
* content spans the page edge-to-edge horizontally. This
|
|
24
|
-
* does NOT cancel the top/bottom margins, extend past the
|
|
25
|
-
* trim into printer bleed overage, apply a named `@page`
|
|
26
|
-
* template, or remove headers/footers — none of that is
|
|
27
|
-
* implemented; the custom-property fallback of 0 means it
|
|
28
|
-
* degrades to plain full-width outside a Paged.js render.
|
|
29
|
-
*/
|
|
30
|
-
export const PAGED_CSS: "\n.md-page-break { break-before: page; }\n.page { break-before: page; }\n.spread { break-before: page; }\n.section { break-inside: avoid; }\n.section.col-split { break-inside: auto; }\n.md-column-break { break-after: column; height: 0; font-size: 0; line-height: 0; visibility: hidden; }\n\n.center { display: block; margin-left: auto; margin-right: auto; max-width: 100%; }\n.float-left { float: left; margin: 0 1em 1em 0; max-width: 50%; }\n.float-right { float: right; margin: 0 0 1em 1em; max-width: 50%; }\n.full-width { display: block; width: 100%; max-width: 100%; }\n.full-bleed {\n display: block;\n break-before: page;\n max-width: none;\n width: calc(100% + var(--pagedjs-margin-left, 0px) + var(--pagedjs-margin-right, 0px));\n margin-left: calc(-1 * var(--pagedjs-margin-left, 0px));\n margin-right: calc(-1 * var(--pagedjs-margin-right, 0px));\n}\n";
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single source of truth for the Paged.js polyfill `<script>` slot.
|
|
3
|
-
*
|
|
4
|
-
* PURE / node-free by design: this module imports nothing (no `node:*`, no `fs`),
|
|
5
|
-
* so it can be shared by BOTH the pure, browser-usable HTML assembler
|
|
6
|
-
* (`markdown/assemble.ts`) and the node-side build/preview rewriters
|
|
7
|
-
* (`build-runner.ts`, `pagedjs.ts`, `preview/file-watcher.ts`).
|
|
8
|
-
*
|
|
9
|
-
* The contract between the assembler and every rewriter is the stable
|
|
10
|
-
* `data-pagedjs-polyfill` MARKER ATTRIBUTE — never a URL, filename, or version
|
|
11
|
-
* substring. Core emits the marker; the rewriters find/strip/replace it by
|
|
12
|
-
* attribute. A pagedjs version bump or attribute reorder therefore cannot
|
|
13
|
-
* silently break the strip/replace passes, and the un-rewritten `book.html`
|
|
14
|
-
* carries NO network dependency (there is no live CDN URL to leak).
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Pinned Paged.js version. Bump HERE only — the value is emitted purely as
|
|
18
|
-
* documentation on the marker tag; matching never depends on it.
|
|
19
|
-
*/
|
|
20
|
-
export declare const PAGEDJS_VERSION = "0.4.3";
|
|
21
|
-
/** Stable marker attribute identifying the Paged.js polyfill script slot. */
|
|
22
|
-
export declare const PAGEDJS_POLYFILL_MARKER = "data-pagedjs-polyfill";
|
|
23
|
-
/**
|
|
24
|
-
* The polyfill `<script>` slot Gutterpress core emits into `book.html`. Carries the
|
|
25
|
-
* stable marker (with the intended version as its value, for greppability) and
|
|
26
|
-
* NO `src` — an un-rewritten `book.html` has zero network dependency. Every
|
|
27
|
-
* consumer replaces this slot with a locally-vendored polyfill before the book
|
|
28
|
-
* is ever loaded (build staging, the no-Chromium runtime fallback, live preview).
|
|
29
|
-
*/
|
|
30
|
-
export declare function pagedjsPolyfillTag(version?: string): string;
|
|
31
|
-
/**
|
|
32
|
-
* Match the polyfill `<script>` slot. Matches EITHER the stable marker attribute
|
|
33
|
-
* (core output) OR a `paged.polyfill` `src` (legacy CDN / vendored copy that a
|
|
34
|
-
* staging/patch pass has already swapped in) — so the same matcher works at every
|
|
35
|
-
* stage of the pipeline. Deliberately matches `paged.polyfill` (the version-
|
|
36
|
-
* stable FILENAME), never a bare `pagedjs` substring, so the navigation toolbar
|
|
37
|
-
* scripts (`pagedjs-interface.js` / `pagedjs-bridge.js`) are left untouched.
|
|
38
|
-
*
|
|
39
|
-
* Attribute-order tolerant and version-agnostic. Returns a FRESH RegExp per call
|
|
40
|
-
* so the `g` flag's `lastIndex` is never shared between callers.
|
|
41
|
-
*/
|
|
42
|
-
export declare function pagedjsPolyfillTagRegex(): RegExp;
|
package/dist/lib/pagedjs.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Inline script that polyfills Paged.js's missing break-inside: avoid support.
|
|
3
|
-
*
|
|
4
|
-
* Paged.js has an avoidBreakInside() method but never calls it. This handler
|
|
5
|
-
* intercepts onBreakToken and, when the break lands inside an element with
|
|
6
|
-
* data-break-inside="avoid", moves the break to before that element.
|
|
7
|
-
*
|
|
8
|
-
* Uses a data attribute (not CSS) because source nodes are disconnected from
|
|
9
|
-
* the DOM when onBreakToken fires, so getComputedStyle returns empty values.
|
|
10
|
-
*
|
|
11
|
-
* Must be registered via PagedConfig.before (runs before Paged.js renders).
|
|
12
|
-
*/
|
|
13
|
-
export declare const BREAK_INSIDE_HANDLER: string;
|
|
14
|
-
/**
|
|
15
|
-
* Inject the Paged.js polyfill + render-complete marker into an HTML STRING.
|
|
16
|
-
*
|
|
17
|
-
* Pure so the build can paginate an in-memory copy: the shipped `book.html`
|
|
18
|
-
* must never carry the polyfill `<script src>` (it is a build-time engine, not
|
|
19
|
-
* part of the artifact), and the pagination server serves this patched string
|
|
20
|
-
* as an overlay instead of writing it to disk.
|
|
21
|
-
*/
|
|
22
|
-
export declare function patchHtmlStringForPagedjs(html: string, vendorPath: string): string;
|
|
23
|
-
/**
|
|
24
|
-
* File-in-place wrapper around {@link patchHtmlStringForPagedjs}.
|
|
25
|
-
*/
|
|
26
|
-
export declare function patchHtmlForPagedjs(htmlPath: string, vendorPath: string): Promise<void>;
|
package/dist/lib/pagination.d.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import type { Page } from "puppeteer-core";
|
|
2
|
-
/**
|
|
3
|
-
* Render/pagination (ARCH finding #9, extracted from build-runner.ts): drives
|
|
4
|
-
* headless Chromium through Paged.js to fully paginate a staged HTML document,
|
|
5
|
-
* either printing it to PDF or serializing the paginated DOM to static HTML.
|
|
6
|
-
* Owns: the embedded static HTTP file server both render paths need (a local
|
|
7
|
-
* origin so relative asset URLs resolve), the shared navigate+wait+liveness
|
|
8
|
-
* sequence (`paginateAndCapture`), the default PDF renderer, and build-time
|
|
9
|
-
* static-HTML pagination. build-runner.ts's two `OutputStrategy` classes call
|
|
10
|
-
* into this module for the actual pagination work and never touch a puppeteer
|
|
11
|
-
* `Page` themselves.
|
|
12
|
-
*/
|
|
13
|
-
/** An in-memory response served instead of reading a file from disk. */
|
|
14
|
-
export interface ServerOverlay {
|
|
15
|
-
body: string | Buffer;
|
|
16
|
-
contentType: string;
|
|
17
|
-
}
|
|
18
|
-
/** Input handed to a PDF renderer: a URL serving the staged HTML + assets. */
|
|
19
|
-
export interface PdfRenderInput {
|
|
20
|
-
/** URL of the staged HTML on a local HTTP server (assets resolve relative). */
|
|
21
|
-
url: string;
|
|
22
|
-
/** Absolute path the renderer must write the finished PDF to. */
|
|
23
|
-
outPdf: string;
|
|
24
|
-
/** Hard ceiling for navigation + pagination + PDF generation. */
|
|
25
|
-
timeoutMs: number;
|
|
26
|
-
/**
|
|
27
|
-
* When set, after printing the PDF the renderer also serializes the
|
|
28
|
-
* already-paginated DOM (the same DOM it just printed) and writes it to this
|
|
29
|
-
* path as raw static HTML. Lets ONE pagination pass emit BOTH the PDF and the
|
|
30
|
-
* static desktop HTML, so screen and PDF come from the same artifact. The
|
|
31
|
-
* serialize is read-only and runs AFTER `page.pdf()`, so it cannot affect the
|
|
32
|
-
* PDF. Optional; injected renderers that cannot serialize may ignore it.
|
|
33
|
-
*/
|
|
34
|
-
captureStaticHtmlTo?: string;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* A PDF renderer drives a browser engine to load `url`, wait for fonts +
|
|
38
|
-
* Paged.js (`window.__PAGED_RENDERED__ === true`), measure the first
|
|
39
|
-
* `.pagedjs_page`, and write a borderless PDF at that exact page size to
|
|
40
|
-
* `outPdf` with backgrounds printed.
|
|
41
|
-
*
|
|
42
|
-
* The default ({@link puppeteerPdfRenderer}) drives a system/bundled Chromium.
|
|
43
|
-
* The Electron desktop injects one backed by its own `webContents.printToPDF`,
|
|
44
|
-
* so the packaged app needs no external browser (ADR 0002, Phase 4).
|
|
45
|
-
*/
|
|
46
|
-
export type PdfRenderer = (input: PdfRenderInput) => Promise<void>;
|
|
47
|
-
/** Hard ceiling for navigation + pagination + PDF generation. Large books need
|
|
48
|
-
* this budget; it is also the puppeteer protocolTimeout for the pooled browser. */
|
|
49
|
-
export declare const RENDER_TIMEOUT_MS: number;
|
|
50
|
-
/** Tracks the last-seen `.pagedjs_page` count and when it last advanced. */
|
|
51
|
-
export interface PaginationLivenessState {
|
|
52
|
-
count: number;
|
|
53
|
-
lastAdvanceAt: number;
|
|
54
|
-
}
|
|
55
|
-
export interface PaginationLivenessResult {
|
|
56
|
-
stalled: boolean;
|
|
57
|
-
state: PaginationLivenessState;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Pure stall-detection decision (finding #19), extracted out of the polling
|
|
61
|
-
* loop below so it is unit-testable with a fake page-count source instead of
|
|
62
|
-
* a real puppeteer `page`. Given the latest `.pagedjs_page` count poll and the
|
|
63
|
-
* previously tracked state, decide whether pagination has stalled — the count
|
|
64
|
-
* has not advanced for at least `stallWindowMs` — and return the updated
|
|
65
|
-
* state (advancing resets the liveness clock; a flat or regressed count
|
|
66
|
-
* leaves `lastAdvanceAt` untouched).
|
|
67
|
-
*/
|
|
68
|
-
export declare function evaluatePaginationLiveness(count: number, now: number, state: PaginationLivenessState, stallWindowMs: number): PaginationLivenessResult;
|
|
69
|
-
/**
|
|
70
|
-
* Drive a puppeteer `page` to fully paginate the document at `url`: set the
|
|
71
|
-
* viewport + timeouts, navigate (waiting for network idle so vendored assets +
|
|
72
|
-
* the polyfill load), wait for web fonts, then block until Paged.js signals
|
|
73
|
-
* `window.__PAGED_RENDERED__ === true`.
|
|
74
|
-
*
|
|
75
|
-
* While waiting, a background poller checks the `.pagedjs_page` count every
|
|
76
|
-
* `STALL_POLL_INTERVAL_MS` and logs it (so a long build visibly advances
|
|
77
|
-
* instead of sitting silent), and fails fast with a BuildError the moment
|
|
78
|
-
* `evaluatePaginationLiveness` decides the count has stopped advancing for
|
|
79
|
-
* `STALL_WINDOW_MS` — instead of a wedged Paged.js run silently consuming the
|
|
80
|
-
* full `timeoutMs` / `RENDER_TIMEOUT_MS` budget (up to an hour) before anyone
|
|
81
|
-
* finds out. `timeoutMs` (== `RENDER_TIMEOUT_MS` at every call site) remains
|
|
82
|
-
* the outer budget for legitimately slow-but-advancing books.
|
|
83
|
-
*
|
|
84
|
-
* Policy (owner's call, superseding an earlier "warn and ship partial output"
|
|
85
|
-
* behavior): pagination either completes — `__PAGED_RENDERED__` fires — or the
|
|
86
|
-
* build FAILS. A stall (count plateaus, whether at zero or after some pages)
|
|
87
|
-
* and an outright wait timeout are BOTH treated as an incomplete render and
|
|
88
|
-
* throw a `BuildError`; neither path falls through to let a caller print/
|
|
89
|
-
* serialize whatever partial DOM exists. A silently-truncated "successful"
|
|
90
|
-
* print-ready PDF is worse than a build that fails loudly — non-technical
|
|
91
|
-
* authors have no way to notice half their book is missing. The stall check
|
|
92
|
-
* still fails fast (within `stallWindowMs` of the last advance) rather than
|
|
93
|
-
* waiting out the full `timeoutMs` budget.
|
|
94
|
-
*
|
|
95
|
-
* Shared navigate+wait sequence for BOTH render paths. Callers keep their own
|
|
96
|
-
* tails: the PDF path calls `page.pdf()`; the static-HTML path serializes the
|
|
97
|
-
* DOM — neither tail runs unless this function returns normally, i.e. unless
|
|
98
|
-
* pagination actually completed. Per-caller knobs (viewport, timeout,
|
|
99
|
-
* liveness window) are passed in so behavior is never silently changed;
|
|
100
|
-
* `livenessConfig` defaults to the production constants and exists as a seam
|
|
101
|
-
* for tests to shrink the poll/stall windows instead of waiting 60+ real
|
|
102
|
-
* seconds.
|
|
103
|
-
*/
|
|
104
|
-
export declare function paginateAndCapture(page: Page, url: string, timeoutMs: number, viewport?: {
|
|
105
|
-
width: number;
|
|
106
|
-
height: number;
|
|
107
|
-
}, livenessConfig?: {
|
|
108
|
-
pollIntervalMs: number;
|
|
109
|
-
stallWindowMs: number;
|
|
110
|
-
}): Promise<void>;
|
|
111
|
-
/**
|
|
112
|
-
* Close a CSS string value that Paged.js deliberately left unterminated.
|
|
113
|
-
*
|
|
114
|
-
* Paged.js's StringSets handler writes its string-set results as inline custom
|
|
115
|
-
* properties with an OPENING quote and NO closing quote (paged.polyfill.js:
|
|
116
|
-
* `fragment.style.setProperty(\`--pagedjs-string-first-${name}\`, \`"${…}\`)`).
|
|
117
|
-
* Live that is harmless: `setProperty` parses each value in isolation and the
|
|
118
|
-
* CSS tokenizer auto-closes a string token at end-of-value. But the moment the
|
|
119
|
-
* DOM is SERIALIZED, all four of those properties land in ONE `style` attribute
|
|
120
|
-
* joined by `;` — and on reparse the first unterminated string swallows the
|
|
121
|
-
* declarations that follow it (`"1; --pagedjs-string-last-guideSection: "`).
|
|
122
|
-
* The consumer, `content: "C." var(--pagedjs-string-first-guideSection)`, then
|
|
123
|
-
* becomes invalid at computed-value time and the footer disappears.
|
|
124
|
-
*
|
|
125
|
-
* Given the raw property text, return it with a closing `"` appended when the
|
|
126
|
-
* string token does not terminate. Rules follow CSS Syntax §4.3.5 "consume a
|
|
127
|
-
* string token": a backslash escapes the next code point, and a backslash at
|
|
128
|
-
* end-of-input is dropped (it cannot escape the quote we are about to add).
|
|
129
|
-
* Values that are already terminated — including ones this function has already
|
|
130
|
-
* fixed — are returned unchanged, so it is idempotent. Values that are not a
|
|
131
|
-
* quoted string at all are left alone.
|
|
132
|
-
*/
|
|
133
|
-
export declare function closeUnterminatedCssString(value: string): string;
|
|
134
|
-
/**
|
|
135
|
-
* Build-time pagination (SSG model): drive headless Chromium to fully paginate
|
|
136
|
-
* the book with Paged.js, then serialize the resulting already-fragmented DOM
|
|
137
|
-
* to a static HTML string. Paged.js's polisher injects its layout CSS as
|
|
138
|
-
* `<style>` elements INTO the DOM, so the serialized markup carries everything
|
|
139
|
-
* needed to render the pages with NO runtime pagination engine.
|
|
140
|
-
*
|
|
141
|
-
* Serves `outDir` itself (images resolve relative to `book.html`, exactly as
|
|
142
|
-
* they will in the shipped artifact) with the engine supplied as overlays.
|
|
143
|
-
*/
|
|
144
|
-
export declare function paginateToStaticHtml(htmlFile: string): Promise<string>;
|
|
145
|
-
/**
|
|
146
|
-
* Render `htmlFile` (in its own `outDir`) to a PDF, serving that directory with
|
|
147
|
-
* the pagination engine supplied as in-memory overlays.
|
|
148
|
-
*/
|
|
149
|
-
export declare function renderHtmlToPdf(htmlFile: string, outPdf: string, renderer?: PdfRenderer, captureStaticHtmlTo?: string): Promise<void>;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ResolveConflictsOptions, SyncOutcome } from "./sync-types.ts";
|
|
2
|
-
/**
|
|
3
|
-
* `chapter-01.md` → `chapter-01 (online copy).md` (next to the original).
|
|
4
|
-
* `counter` ≥ 2 produces `chapter-01 (online copy 2).md`, … — used to avoid
|
|
5
|
-
* clobbering a pre-existing file with the same name.
|
|
6
|
-
*/
|
|
7
|
-
export declare function onlineCopyPath(filepath: string, counter?: number): string;
|
|
8
|
-
/**
|
|
9
|
-
* Apply the author's per-file choices and sync the combined result
|
|
10
|
-
* (ADR 0006 D5). The merge commit has TWO PARENTS — the local branch tip and
|
|
11
|
-
* the online tip — so both histories remain intact and View History stays
|
|
12
|
-
* honest about what was combined.
|
|
13
|
-
*
|
|
14
|
-
* How each choice is applied WITHOUT conflict markers:
|
|
15
|
-
*
|
|
16
|
-
* - Files edited in both copies are settled inside the merge itself by a
|
|
17
|
-
* custom `mergeDriver` that returns the chosen side's content ("Keep both
|
|
18
|
-
* copies" keeps mine and writes the online version to
|
|
19
|
-
* `<name> (online copy)<ext>` beforehand, committed on the local side so it
|
|
20
|
-
* is part of the merge). Undecided files auto-merge with the same diff3
|
|
21
|
-
* algorithm a plain merge uses.
|
|
22
|
-
* - Delete-involved conflicts never reach a merge driver, so they are settled
|
|
23
|
-
* by equalizing the local side BEFORE the merge (making both sides agree so
|
|
24
|
-
* the merge is clean) and, when the author chose the now-removed side, a
|
|
25
|
-
* small follow-up commit AFTER the merge restores their choice. The merge
|
|
26
|
-
* commit still carries both parents; the around-commits are visible,
|
|
27
|
-
* honestly labeled steps in View History.
|
|
28
|
-
*/
|
|
29
|
-
export declare function resolveConflicts(options: ResolveConflictsOptions): Promise<SyncOutcome>;
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* abort-interrupted-operation.ts — shared skeleton for aborting an unfinished
|
|
3
|
-
* git operation (merge / cherry-pick / rebase) left on disk.
|
|
4
|
-
*
|
|
5
|
-
* WHY (DRY): recover-interrupted-merge, recover-interrupted-cherry-pick, and
|
|
6
|
-
* recover-interrupted-rebase were ~90% identical — same TOCTOU guard, same
|
|
7
|
-
* backup gate, same hadLocalChanges capture, same fault-injection ordering,
|
|
8
|
-
* same force-checkout + marker cleanup + re-verify. The ONLY real differences
|
|
9
|
-
* are: which marker files signal the state, which files to remove, and how to
|
|
10
|
-
* resolve the ref to restore (merge/cherry-pick reset the current branch; a
|
|
11
|
-
* rebase may rewind a named branch to a recorded pre-rebase commit OR restore a
|
|
12
|
-
* detached HEAD). Those three differences are captured in `AbortConfig`; the
|
|
13
|
-
* invariant ordering lives here, once.
|
|
14
|
-
*
|
|
15
|
-
* Abort algorithm (pure isomorphic-git + node:fs — never the system git binary):
|
|
16
|
-
* 1. TOCTOU precondition: if NONE of `markerFiles` exist, the operation was
|
|
17
|
-
* already finished/aborted externally and there is nothing to abort. This
|
|
18
|
-
* is now enforced by the DISPATCHER (dispatch.ts), which calls each
|
|
19
|
-
* kind's exported `stillApplies` — built from `anyMarkerPresent` below —
|
|
20
|
-
* INSIDE withRepoLock, before this function is ever invoked. There used
|
|
21
|
-
* to be a duplicate hand-rolled copy of this same check at the top of
|
|
22
|
-
* this function; it is deleted, not kept alongside the dispatcher probe,
|
|
23
|
-
* because — unlike recover-missing-git-dir.ts — this function has no
|
|
24
|
-
* SECOND re-check later (no re-check after the backup/confirm wait), so
|
|
25
|
-
* the one check and the dispatcher probe cover the exact same window: the
|
|
26
|
-
* dispatcher probe runs immediately before this function's synchronous
|
|
27
|
-
* entry, with no work in between, just as the deleted local check did.
|
|
28
|
-
* 2. withBackupGate (backup → confirm → risky → failsafe). Inside the callback:
|
|
29
|
-
* 3. Capture whether the working tree had in-progress edits (best-effort).
|
|
30
|
-
* 4. Resolve the restore target via `resolveTarget` (default:
|
|
31
|
-
* ctx.branch → git.currentBranch → "HEAD"). resolveTarget MAY throw (e.g.
|
|
32
|
-
* the rebase cannot find its pre-op commit) — that surfaces as
|
|
33
|
-
* failed_backup_available since the backup is already safe.
|
|
34
|
-
* 5. Optionally rewind a named branch ref to a recorded commit, then
|
|
35
|
-
* force-checkout the target (resets index + worktree).
|
|
36
|
-
* 6. Remove the transient `cleanupFiles` inside .git.
|
|
37
|
-
* 7. Verify every `markerFiles` entry is gone; if not → THROW.
|
|
38
|
-
*
|
|
39
|
-
* Inside the risky callback we call ONLY raw git.* / node:fs — never a
|
|
40
|
-
* lock-wrapped lib function — so the dispatcher's per-repo FIFO queue can't
|
|
41
|
-
* deadlock. Re-verification uses direct fs.existsSync, not inspectRepo. All
|
|
42
|
-
* removed paths live INSIDE the repo's own .git and are captured in the verified
|
|
43
|
-
* backup — never user content.
|
|
44
|
-
*
|
|
45
|
-
* Fault injection points (ctx.faults?.before()):
|
|
46
|
-
* after_backup_before_repair — start of the destructive section
|
|
47
|
-
* abort_interrupted_operation — start of the abort proper
|
|
48
|
-
* checkout_branch — before the force checkout
|
|
49
|
-
* remove_operation_state — before deleting the on-disk state files
|
|
50
|
-
*/
|
|
51
|
-
import type { RecoveryContext, RecoveryResult, SyncErrorKind } from "./types.ts";
|
|
52
|
-
/**
|
|
53
|
-
* True when any of `markerFiles` (gitDir-relative) still exists. Shared by
|
|
54
|
-
* each interrupted-* handler's exported `stillApplies` (the dispatcher's
|
|
55
|
-
* precondition probe, see types.ts `StillAppliesFn`) so there is ONE
|
|
56
|
-
* implementation of "is this abort still needed", not three copies.
|
|
57
|
-
*/
|
|
58
|
-
export declare function anyMarkerPresent(ctx: RecoveryContext, markerFiles: string[]): boolean;
|
|
59
|
-
/** Arguments handed to a config's `resolveTarget`. */
|
|
60
|
-
export interface AbortResolveArgs {
|
|
61
|
-
ctx: RecoveryContext;
|
|
62
|
-
/** The git repository root (ctx.repoDir). */
|
|
63
|
-
dir: string;
|
|
64
|
-
/** The resolved `.git` directory for `dir`. */
|
|
65
|
-
gitDir: string;
|
|
66
|
-
}
|
|
67
|
-
/** Which ref to restore and (optionally) which branch ref to rewind first. */
|
|
68
|
-
export interface AbortTargetPlan {
|
|
69
|
-
/** The ref to force-checkout (a branch name, a commit sha, or "HEAD"). */
|
|
70
|
-
checkoutRef: string;
|
|
71
|
-
/**
|
|
72
|
-
* When set, `refs/heads/<writeRefBranch>` is force-written to `writeRefValue`
|
|
73
|
-
* BEFORE the checkout (used to rewind a named branch to a pre-op commit). Omit
|
|
74
|
-
* to leave all refs untouched (merge/cherry-pick and detached-HEAD rebase).
|
|
75
|
-
*/
|
|
76
|
-
writeRefBranch?: string;
|
|
77
|
-
writeRefValue?: string;
|
|
78
|
-
}
|
|
79
|
-
/** The per-operation differences the shared skeleton parameterizes. */
|
|
80
|
-
export interface AbortConfig {
|
|
81
|
-
/** Recovery kind — drives policy (backup/confirm) and guidance copy. */
|
|
82
|
-
kind: SyncErrorKind;
|
|
83
|
-
/**
|
|
84
|
-
* gitDir-relative marker paths whose presence signals the interrupted state.
|
|
85
|
-
* If NONE exist the abort is a benign no-op; after the abort ALL must be gone.
|
|
86
|
-
*/
|
|
87
|
-
markerFiles: string[];
|
|
88
|
-
/** gitDir-relative paths removed during the abort (force; missing is fine). */
|
|
89
|
-
cleanupFiles: string[];
|
|
90
|
-
/**
|
|
91
|
-
* Resolve which ref to restore. Defaults to
|
|
92
|
-
* ctx.branch → git.currentBranch → "HEAD". MAY throw to abort the repair
|
|
93
|
-
* (the verified backup makes that safe → failed_backup_available).
|
|
94
|
-
*/
|
|
95
|
-
resolveTarget?: (args: AbortResolveArgs) => Promise<AbortTargetPlan>;
|
|
96
|
-
/** Build the success message; `hadLocalChanges` reports whether edits were reset. */
|
|
97
|
-
successMessage: (hadLocalChanges: boolean) => string;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Abort an interrupted git operation per `config`. See the module header for the
|
|
101
|
-
* full algorithm and safety invariants.
|
|
102
|
-
*/
|
|
103
|
-
export declare function abortInterruptedOperation(ctx: RecoveryContext, config: AbortConfig): Promise<RecoveryResult>;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Temp-dir zip backup creation and verification for the sync-recovery subsystem.
|
|
3
|
-
*
|
|
4
|
-
* Creates a STORE-method (no compression) ZIP of the project's user files plus
|
|
5
|
-
* .git/, writing it to:
|
|
6
|
-
* <os.tmpdir()>/print-sync-recovery/<repo-slug>/<ISO-timestamp>-<reason>.zip
|
|
7
|
-
*
|
|
8
|
-
* The backup root is `os.tmpdir()` (NOT a hardcoded "/tmp") so it is correct on
|
|
9
|
-
* Windows/macOS/Linux — the desktop ships on all three (CLAUDE.md §8). A literal
|
|
10
|
-
* "/tmp" would make every risky-repair backup throw on Windows.
|
|
11
|
-
*
|
|
12
|
-
* The ZIP bytes are produced by fflate's streaming Zip/ZipPassThrough (STORE
|
|
13
|
-
* method — no compression), replacing the previous hand-rolled writer (#86).
|
|
14
|
-
* fflate is pure JS with zero dependencies, no runtime package.json/data
|
|
15
|
-
* reads, and no computed-path dynamic imports, so it bundles cleanly under
|
|
16
|
-
* bun build --compile (CLAUDE.md §1/§3). It is already a dependency (publish
|
|
17
|
-
* providers use its unzipSync).
|
|
18
|
-
*
|
|
19
|
-
* Verification (assertZipReadable): deliberately NOT fflate. It validates
|
|
20
|
-
* EVERY central-directory entry's signature and bounds (not just the first)
|
|
21
|
-
* via positioned reads of the EOCD + central directory only — never the file
|
|
22
|
-
* data — so it stays memory-safe on multi-GB backups while still catching
|
|
23
|
-
* corruption in any later entry. fflate's unzip needs the whole archive in
|
|
24
|
-
* memory, so it cannot back this check; the small positioned-read parser
|
|
25
|
-
* stays.
|
|
26
|
-
*
|
|
27
|
-
* Retention: createRecoveryZip prunes stale backups (best-effort, never throws)
|
|
28
|
-
* before writing a new one — see pruneOldBackups — so old backups do not fill
|
|
29
|
-
* the disk over time.
|
|
30
|
-
*
|
|
31
|
-
* Exclusions: node_modules/, .print-sync/cache/, and **.git/config**. The git
|
|
32
|
-
* config file is deliberately dropped from the backup because it can carry an
|
|
33
|
-
* embedded credential (e.g. a tokenized remote URL) and is reconstructable on
|
|
34
|
-
* recovery (the remote is reconfigured from the stored connection). All other
|
|
35
|
-
* .git/ contents (objects, refs, HEAD) ARE included so recovery still works.
|
|
36
|
-
* Inclusions: all user-visible files + .git/ (minus config, for full recovery)
|
|
37
|
-
*/
|
|
38
|
-
import type { RecoveryBackup, RecoveryContext } from "./types.ts";
|
|
39
|
-
/**
|
|
40
|
-
* Root directory for all sync-recovery backups.
|
|
41
|
-
*
|
|
42
|
-
* Computed from `os.tmpdir()` (NOT hardcoded "/tmp") so it resolves to a real
|
|
43
|
-
* temp dir on Windows, macOS, and Linux. A literal "/tmp" does not exist on
|
|
44
|
-
* Windows and would make every risky-repair backup throw there (CLAUDE.md §8).
|
|
45
|
-
*/
|
|
46
|
-
export declare const BACKUP_ROOT: string;
|
|
47
|
-
export interface PruneOldBackupsOptions {
|
|
48
|
-
/** Backup root to prune under. Defaults to {@link BACKUP_ROOT}. */
|
|
49
|
-
root?: string;
|
|
50
|
-
/** Restrict pruning to a single repo-slug subfolder. */
|
|
51
|
-
slug?: string;
|
|
52
|
-
/** Remove zips with an mtime older than this many ms. Default 7 days. */
|
|
53
|
-
ttlMs?: number;
|
|
54
|
-
/** After TTL pruning, keep at most this many newest zips per slug. */
|
|
55
|
-
maxPerSlug?: number;
|
|
56
|
-
/** Clock override (tests). Returns epoch ms. */
|
|
57
|
-
now?: () => number;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Remove stale recovery backups so they do not accumulate forever and fill the
|
|
61
|
-
* disk (BUG 2). Two independent policies are applied per repo-slug folder:
|
|
62
|
-
* 1. TTL — delete any zip whose mtime is older than `ttlMs` (default 7 days).
|
|
63
|
-
* 2. Cap — keep only the newest `maxPerSlug` zips (default 20), delete the rest.
|
|
64
|
-
*
|
|
65
|
-
* BEST-EFFORT and SILENT: this never throws and never blocks backup creation.
|
|
66
|
-
* Any unreadable dir, racing delete, or stat failure is ignored — a failure to
|
|
67
|
-
* prune must never prevent the user's work from being backed up.
|
|
68
|
-
*/
|
|
69
|
-
export declare function pruneOldBackups(opts?: PruneOldBackupsOptions): Promise<void>;
|
|
70
|
-
/**
|
|
71
|
-
* Create a temp-dir zip backup of the project directory (user files + .git/,
|
|
72
|
-
* minus .git/config). Calls ctx.faults?.before("backup_create") then
|
|
73
|
-
* ctx.faults?.before("backup_verify") so tests can inject failures at each step.
|
|
74
|
-
*
|
|
75
|
-
* Returns a RecoveryBackup describing the zip, or throws on failure.
|
|
76
|
-
*/
|
|
77
|
-
export declare function createRecoveryZip(ctx: Pick<RecoveryContext, "repoDir" | "repoSlug" | "faults" | "now">, reason: string): Promise<RecoveryBackup>;
|
|
78
|
-
export interface ZipEntryInfo {
|
|
79
|
-
name: string;
|
|
80
|
-
size: number;
|
|
81
|
-
/** The extracted file content. */
|
|
82
|
-
data: Uint8Array;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Extract every entry of a ZIP buffer (fflate unzipSync). Returns [] for a
|
|
86
|
-
* buffer that is not a parseable zip — matching the old hand-rolled parser's
|
|
87
|
-
* "no EOCD found" behavior that some assertions rely on.
|
|
88
|
-
*/
|
|
89
|
-
export declare function parseZipEntries(buf: Buffer): ZipEntryInfo[];
|
|
90
|
-
/**
|
|
91
|
-
* Assert that a zip file at `zipPath` is readable and parseable.
|
|
92
|
-
* Throws with a descriptive error if not.
|
|
93
|
-
*
|
|
94
|
-
* Validates EVERY central-directory entry — each entry's CD signature
|
|
95
|
-
* (0x02014b50) and that its variable-length fields stay within the central
|
|
96
|
-
* directory bounds. A zip whose FIRST entry is intact but whose LATER entries
|
|
97
|
-
* are corrupt is rejected (BUG 4): checking only entry 0 let truncated/garbled
|
|
98
|
-
* tails slip through.
|
|
99
|
-
*
|
|
100
|
-
* MEMORY-SAFE: reads only the end-of-central-directory record (file tail) and
|
|
101
|
-
* the central directory via positioned reads — NEVER the file data. A multi-GB
|
|
102
|
-
* backup is verified without reading it back into memory.
|
|
103
|
-
*/
|
|
104
|
-
export declare function assertZipReadable(zipPath: string): Promise<void>;
|
|
105
|
-
/**
|
|
106
|
-
* Return the list of entries inside a zip file (as ZipEntryInfo objects).
|
|
107
|
-
*
|
|
108
|
-
* @internal TEST-ONLY: reads the WHOLE zip into memory to expose entry content.
|
|
109
|
-
* NEVER call this in a production path — a large backup would OOM. Production
|
|
110
|
-
* verification uses {@link assertZipReadable} (positioned reads, memory-safe).
|
|
111
|
-
* Used by tests to assert which files were backed up and inspect content.
|
|
112
|
-
*/
|
|
113
|
-
export declare function zipEntries(zipPath: string): Promise<ZipEntryInfo[]>;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* buildRecoveryContext — the ONE place a RecoveryContext is resolved from a
|
|
3
|
-
* project directory. Both hosts consume it (the desktop's recovery bridge and
|
|
4
|
-
* `gutterpress repair`); each supplies only its own ConfirmationGate (dialog vs
|
|
5
|
-
* terminal prompt). Keeping the resolution here means repo-root, branch,
|
|
6
|
-
* credential, and slug rules can never drift between hosts.
|
|
7
|
-
*
|
|
8
|
-
* Resolution rules (each learned the hard way — see the audit trail):
|
|
9
|
-
* - repoDir: the project's OWN repo root via detectProjectSource. NEVER
|
|
10
|
-
* findEnclosingRepoDir — it is ancestor-only (skips the project's own
|
|
11
|
-
* .git), so a project that IS its own repo root would resolve to a parent
|
|
12
|
-
* repo (e.g. ~/.git) and the backup step would zip the entire home
|
|
13
|
-
* directory.
|
|
14
|
-
* - branch: remote diagnosis first, then the locally detected branch
|
|
15
|
-
* (local-only repos on non-"main" branches), then "main".
|
|
16
|
-
* - credential: resolved from the token store by remote hostname; stays in
|
|
17
|
-
* the calling process — never serialized to a UI layer.
|
|
18
|
-
* - repoSlug: last path segment, sanitized for backup file naming.
|
|
19
|
-
*/
|
|
20
|
-
import { detectProjectSource } from "../../project-source.ts";
|
|
21
|
-
import { diagnoseProjectRemote } from "../diagnose.ts";
|
|
22
|
-
import { type TokenStore } from "../token-store.ts";
|
|
23
|
-
import type { ConfirmationGate, RecoveryContext } from "./types.ts";
|
|
24
|
-
export interface BuildRecoveryContextOptions {
|
|
25
|
-
/** The directory the user opened (may be a subfolder of its repo). */
|
|
26
|
-
projectDir: string;
|
|
27
|
-
/** Host-specific approval gate (dialog, terminal prompt, …). */
|
|
28
|
-
confirmation: ConfirmationGate;
|
|
29
|
-
/** Credential store for the remote host, when the host has one. */
|
|
30
|
-
tokenStore?: TokenStore;
|
|
31
|
-
/** Display name for snapshot commits created during recovery. */
|
|
32
|
-
authorName?: string;
|
|
33
|
-
/** Email for snapshot commits created during recovery. */
|
|
34
|
-
authorEmail?: string;
|
|
35
|
-
/** Operation-log file shared with the sync path. */
|
|
36
|
-
logFile?: string;
|
|
37
|
-
/**
|
|
38
|
-
* Classification override (tests only — omit in production). Injected the
|
|
39
|
-
* same way as RecoveryContext's `now`/`faults`: bun's mock.module leaks
|
|
40
|
-
* across test files, so cross-cutting modules are never module-mocked.
|
|
41
|
-
*/
|
|
42
|
-
classify?: typeof detectProjectSource;
|
|
43
|
-
/** Diagnosis override (tests only — omit in production). See `classify`. */
|
|
44
|
-
diagnose?: typeof diagnoseProjectRemote;
|
|
45
|
-
}
|
|
46
|
-
/** Resolve everything a recovery handler needs from a project directory. */
|
|
47
|
-
export declare function buildRecoveryContext(options: BuildRecoveryContextOptions): Promise<RecoveryContext>;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recovery dispatcher — INTEGRATE PHASE.
|
|
3
|
-
*
|
|
4
|
-
* Maps each SyncErrorKind to its recover-<x>.ts handler.
|
|
5
|
-
* Also exports the index barrel for the recovery subsystem public surface.
|
|
6
|
-
*
|
|
7
|
-
* The dispatcher is the single call-site for all recovery attempts. Callers
|
|
8
|
-
* (e.g. a guardedSync wrapper) obtain a SyncErrorKind from classifyGitError(),
|
|
9
|
-
* build a RecoveryContext, and call recover(kind, ctx, error).
|
|
10
|
-
*
|
|
11
|
-
* Handler lookup is a plain switch — no dynamic import, no registry object —
|
|
12
|
-
* so TypeScript can verify exhaustiveness at compile time and bun build
|
|
13
|
-
* --compile can tree-shake unused handlers.
|
|
14
|
-
*/
|
|
15
|
-
import type { RecoveryContext, RecoveryResult, SyncErrorKind } from "./types.ts";
|
|
16
|
-
/**
|
|
17
|
-
* Dispatch a recovery attempt.
|
|
18
|
-
*
|
|
19
|
-
* @param kind - The SyncErrorKind from classifyGitError().
|
|
20
|
-
* @param ctx - Full RecoveryContext (repoDir, branch, remoteUrl, etc.).
|
|
21
|
-
* @param error - The original thrown error, if available.
|
|
22
|
-
* @returns RecoveryResult describing what happened.
|
|
23
|
-
*/
|
|
24
|
-
export declare function recover(kind: SyncErrorKind, ctx: RecoveryContext, error?: unknown): Promise<RecoveryResult>;
|
|
25
|
-
export type { RecoverFn, RecoveryContext, RecoveryResult, SyncErrorKind, RecoveryRisk, ManualGuidance, RepoHealth, RecoveryBackup, RepairConfirmation, ConfirmationGate, FaultInjector, FaultPoint, } from "./types.ts";
|
|
26
|
-
export { classifyGitError, classifyFromHealth, RepoNeedsRecoveryError, isRepoNeedsRecoveryError, } from "./classify.ts";
|
|
27
|
-
export { inspectRepo, preflightStructuralReason, buildPreflightDiagnostics, verifyRepoReadable, isUnbornRepo, } from "./inspect.ts";
|
|
28
|
-
export { buildRecoveryContext } from "./context.ts";
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fail-safe wrapper and no-op fallback for the sync-recovery subsystem.
|
|
3
|
-
*
|
|
4
|
-
* withBackupGate() enforces the invariant ordering every risky repair must
|
|
5
|
-
* follow:
|
|
6
|
-
* 1. Look up the policy for the kind.
|
|
7
|
-
* 2. If createBackup → createRecoveryZip; on failure → failSafe
|
|
8
|
-
* "failed_no_changes_made" with NO subsequent writes.
|
|
9
|
-
* 3. If requireConfirmation → confirmRepair; DENIED → "blocked" no-op.
|
|
10
|
-
* 4. Run the risky repair callback.
|
|
11
|
-
* 5. If the risky callback throws AFTER a backup → failSafe
|
|
12
|
-
* "failed_backup_available" (backup is readable, remote is unchanged).
|
|
13
|
-
*
|
|
14
|
-
* failSafeNoRepair() is the terminal no-op: returns
|
|
15
|
-
* - "failed_backup_available" when a backup zip path is supplied
|
|
16
|
-
* - "failed_no_changes_made" otherwise
|
|
17
|
-
* Both branches include ManualGuidance so the host can show useful copy.
|
|
18
|
-
*/
|
|
19
|
-
import type { RecoveryContext, RecoveryResult, SyncErrorKind } from "./types.ts";
|
|
20
|
-
/**
|
|
21
|
-
* Terminal no-op: no repair was attempted (or the repair failed after a
|
|
22
|
-
* backup was created). Returns a RecoveryResult with the appropriate status.
|
|
23
|
-
*/
|
|
24
|
-
export declare function failSafeNoRepair(ctx: Pick<RecoveryContext, "repoSlug" | "remoteUrl">, kind: SyncErrorKind, backupZipPath?: string, error?: unknown): RecoveryResult;
|
|
25
|
-
/**
|
|
26
|
-
* Enforce the invariant ordering for risky repairs:
|
|
27
|
-
* policy → backup → confirmation → risky → failsafe on throw.
|
|
28
|
-
*
|
|
29
|
-
* The `risky` callback receives the backup zip path (or undefined) and
|
|
30
|
-
* must call ctx.faults?.before("after_backup_before_repair") at the start
|
|
31
|
-
* of its destructive section.
|
|
32
|
-
*/
|
|
33
|
-
export declare function withBackupGate(ctx: RecoveryContext, kind: SyncErrorKind, risky: (backupZipPath: string | undefined) => Promise<RecoveryResult>, error?: unknown): Promise<RecoveryResult>;
|
|
@@ -1,28 +0,0 @@
|
|
|
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;
|