gutterpress 0.0.0 → 0.9.0-alpha.2

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.
Files changed (82) hide show
  1. package/README.md +21 -16
  2. package/dist/api/index.d.ts +6 -2
  3. package/dist/api/index.js +15 -3
  4. package/dist/{audit-1jrwwacz.js → audit-nhn2pjz3.js} +5 -5
  5. package/dist/{build-5s92kgpc.js → build-san7fv2z.js} +5 -5
  6. package/dist/checks/types.d.ts +6 -0
  7. package/dist/{cli-hvqjs342.js → cli-e5zhb0xs.js} +2 -2
  8. package/dist/{cli-7ykx7pp0.js → cli-k4bd06sd.js} +982 -331
  9. package/dist/{cli-h7phg3ka.js → cli-najycadg.js} +258 -258
  10. package/dist/{index-1tyj4bk3.js → cli-wchtvxvw.js} +4 -1
  11. package/dist/{cli-5bxx0c4y.js → cli-yzrh708h.js} +2 -2
  12. package/dist/cli.js +15 -15
  13. package/dist/{doctor-25zjt8dz.js → doctor-hrk0kxxz.js} +2 -2
  14. package/dist/{index-3h3fef7r.js → index-bynn850m.js} +1200 -410
  15. package/dist/{cli-1tyj4bk3.js → index-wchtvxvw.js} +4 -1
  16. package/dist/{index-5bxx0c4y.js → index-yzrh708h.js} +2 -2
  17. package/dist/index.js +15 -3
  18. package/dist/lib/asset-inline.d.ts +19 -0
  19. package/dist/lib/build-error.d.ts +0 -1
  20. package/dist/lib/build-fingerprint.d.ts +14 -0
  21. package/dist/lib/build-runner.d.ts +21 -1
  22. package/dist/lib/ghostscript.d.ts +0 -9
  23. package/dist/lib/manifest-config.d.ts +7 -0
  24. package/dist/lib/manifest.d.ts +8 -0
  25. package/dist/lib/markdown/assemble.d.ts +4 -6
  26. package/dist/lib/markdown/chapter-id.d.ts +1 -1
  27. package/dist/lib/markdown/index.d.ts +1 -2
  28. package/dist/lib/npm-plugin-installer.d.ts +1 -1
  29. package/dist/lib/output-paths.d.ts +0 -1
  30. package/dist/lib/pagination.d.ts +0 -20
  31. package/dist/lib/pdf-inspect.d.ts +2 -2
  32. package/dist/lib/pdf-parse.d.ts +2 -1
  33. package/dist/lib/plugin-manager.d.ts +0 -2
  34. package/dist/lib/presets.d.ts +36 -7
  35. package/dist/lib/project-scaffold.d.ts +39 -2
  36. package/dist/lib/project-source.d.ts +11 -9
  37. package/dist/lib/project-templates.d.ts +45 -7
  38. package/dist/lib/publish/providers/azure-swa.d.ts +0 -1
  39. package/dist/lib/publish/providers/itch.d.ts +0 -1
  40. package/dist/lib/publish/providers/shopify.d.ts +0 -1
  41. package/dist/lib/remote-auth/github-repos.d.ts +0 -2
  42. package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -5
  43. package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +3 -2
  44. package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -8
  45. package/dist/lib/remote-auth/sync.d.ts +2 -10
  46. package/dist/lib/remote-auth/test-access.d.ts +0 -2
  47. package/dist/lib/remote-auth/test-support/git-http-server.d.ts +0 -37
  48. package/dist/lib/source-provider.d.ts +0 -14
  49. package/dist/lib/static-serve.d.ts +39 -0
  50. package/dist/lib/style-resolver.d.ts +10 -1
  51. package/dist/lib/targets.d.ts +84 -0
  52. package/dist/lib/theme-import.d.ts +0 -2
  53. package/dist/lib/theme-manager.d.ts +1 -1
  54. package/dist/lib/validation-exec.d.ts +4 -3
  55. package/dist/{lint-f63nxjfj.js → lint-96j9hrj4.js} +5 -5
  56. package/dist/{manifest.schema-rn1dpdpn.json → manifest.schema-z61rzw44.json} +12 -6
  57. package/dist/new-8p38wavc.js +185 -0
  58. package/dist/{pagedjs-interface-80g3xa8p.js → pagedjs-interface-qxvzgwd7.js} +92 -33
  59. package/dist/{plugin-wf2cbmwc.js → plugin-ees6nhkc.js} +5 -5
  60. package/dist/{preflight-tg4mpz1h.js → preflight-1q6c2edh.js} +24 -29
  61. package/dist/preview/file-watcher.d.ts +48 -39
  62. package/dist/preview/http-server.d.ts +5 -2
  63. package/dist/preview/server-context.d.ts +24 -0
  64. package/dist/preview-shell-6dqexx1m.js +581 -0
  65. package/dist/{preview-h2bwbb2x.js → preview-y5a2zen1.js} +5 -5
  66. package/dist/{project-source-0tg0r6vm.js → project-source-p0gn1wd5.js} +3 -3
  67. package/dist/{publish-ztaet53r.js → publish-rm9yb3wh.js} +5 -5
  68. package/dist/render.js +10 -19
  69. package/dist/{repair-jkp4n3j8.js → repair-zgq7q2g6.js} +5 -5
  70. package/dist/schema/manifest.types.d.ts +14 -3
  71. package/dist/server.d.ts +2 -0
  72. package/dist/{source-provider-hp96b5jf.js → source-provider-c1rjm2c0.js} +2 -8
  73. package/dist/types.d.ts +0 -1
  74. package/dist/{validate-k2drs19j.js → validate-nr0xa6sa.js} +8 -8
  75. package/package.json +2 -2
  76. package/dist/chapter-01-jj8ftn4c.md +0 -29
  77. package/dist/lib/validation-profile.d.ts +0 -18
  78. package/dist/manifest-ryyg71nf.yaml +0 -9
  79. package/dist/new-7b08a42h.js +0 -105
  80. package/dist/preview-shell-rghtfmc8.js +0 -155
  81. package/dist/theme-0yzvthe8.css +0 -65
  82. package/dist/theme-6a2fngc8.json +0 -6
@@ -86,6 +86,9 @@ async function detectProjectSource(folderPath) {
86
86
  }
87
87
  return { type: "local-folder", path: folderPath };
88
88
  }
89
+ function repoRootForSource(source, fallbackDir) {
90
+ return source.type === "local-git-folder" ? source.repoRoot || fallbackDir : fallbackDir;
91
+ }
89
92
  function capabilitiesFor(source) {
90
93
  switch (source.type) {
91
94
  case "local-folder":
@@ -121,4 +124,4 @@ function capabilitiesFor(source) {
121
124
  }
122
125
  }
123
126
 
124
- export { pickSyncRemote, syncRemoteFor, findEnclosingRepoDir, repoSubPath, detectProjectSource, capabilitiesFor };
127
+ export { syncRemoteFor, findEnclosingRepoDir, repoSubPath, detectProjectSource, repoRootForSource, capabilitiesFor };
@@ -2,7 +2,7 @@ import {
2
2
  capabilitiesFor,
3
3
  detectProjectSource,
4
4
  findEnclosingRepoDir
5
- } from "./index-1tyj4bk3.js";
5
+ } from "./index-wchtvxvw.js";
6
6
 
7
7
  // src/lib/source-provider.ts
8
8
  import * as fs from "node:fs";
@@ -461,4 +461,4 @@ async function restoreVersionWithBackup(options) {
461
461
  });
462
462
  }
463
463
 
464
- export { resolveLogger, shortOid, gitScopeFor, HISTORY_PAGE_LIMIT, SNAPSHOT_STAGING_MARKER, withRepoLock, __repoLockQueueSizeForTests, gitAuthor, readGitAuthor, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup };
464
+ export { resolveLogger, shortOid, gitScopeFor, HISTORY_PAGE_LIMIT, withRepoLock, __repoLockQueueSizeForTests, resolveGitAuthor, listWorkdirChanges, stageChanges, hasPendingChanges, hasUncommittedChanges, snapshotWorkingTreeUnlocked, isNoChangesError, providerFor, gitDirFor, snapshotStagingMarkerPath, RESTORE_BACKUP_MESSAGE, AUTO_SNAPSHOT_MESSAGE, restoreVersionWithBackup };
package/dist/index.js CHANGED
@@ -15,11 +15,15 @@ import {
15
15
  GitHubAuthProvider,
16
16
  MANIFEST_FILENAMES,
17
17
  PLUGINS_DIR,
18
+ PRESETS,
19
+ PRESET_IDS,
18
20
  PublishSelectionsStore,
19
21
  RECOMMENDED_PLUGINS,
20
22
  RepoNeedsRecoveryError,
21
23
  SNIPPETS_DIR,
22
24
  SYNC_SNAPSHOT_MESSAGE,
25
+ TARGETS,
26
+ TARGET_IDS,
23
27
  THEMES_DIR,
24
28
  addLocalPlugin,
25
29
  addNpmPlugin,
@@ -91,6 +95,7 @@ import {
91
95
  publishConnectionStatus,
92
96
  publishCredentialKey,
93
97
  publishProviderFor,
98
+ publishTargetFor,
94
99
  pullChanges,
95
100
  pushChanges,
96
101
  readAppHeartbeat,
@@ -137,7 +142,7 @@ import {
137
142
  validateProjectPlugins,
138
143
  verifyRepoReadable,
139
144
  writeAppHeartbeat
140
- } from "./index-3h3fef7r.js";
145
+ } from "./index-bynn850m.js";
141
146
  import {
142
147
  AUTO_SNAPSHOT_MESSAGE,
143
148
  HISTORY_PAGE_LIMIT,
@@ -147,13 +152,14 @@ import {
147
152
  resolveLogger,
148
153
  restoreVersionWithBackup,
149
154
  shortOid
150
- } from "./index-5bxx0c4y.js";
155
+ } from "./index-yzrh708h.js";
151
156
  import {
152
157
  capabilitiesFor,
153
158
  detectProjectSource,
154
159
  findEnclosingRepoDir,
160
+ repoRootForSource,
155
161
  repoSubPath
156
- } from "./index-1tyj4bk3.js";
162
+ } from "./index-wchtvxvw.js";
157
163
  import"./index-yja077f6.js";
158
164
  import"./index-37x76zdn.js";
159
165
  export {
@@ -195,6 +201,7 @@ export {
195
201
  resolveActiveStyles,
196
202
  reportMissingTools,
197
203
  repoSubPath,
204
+ repoRootForSource,
198
205
  removeProjectTheme,
199
206
  removeAppHeartbeat,
200
207
  redactCredential,
@@ -207,6 +214,7 @@ export {
207
214
  readAppHeartbeat,
208
215
  pushChanges,
209
216
  pullChanges,
217
+ publishTargetFor,
210
218
  publishProviderFor,
211
219
  publishCredentialKey,
212
220
  publishConnectionStatus,
@@ -283,12 +291,16 @@ export {
283
291
  addNpmPlugin,
284
292
  addLocalPlugin,
285
293
  THEMES_DIR,
294
+ TARGET_IDS,
295
+ TARGETS,
286
296
  SYNC_SNAPSHOT_MESSAGE,
287
297
  SNIPPETS_DIR,
288
298
  RepoNeedsRecoveryError,
289
299
  RESTORE_BACKUP_MESSAGE,
290
300
  RECOMMENDED_PLUGINS,
291
301
  PublishSelectionsStore,
302
+ PRESET_IDS,
303
+ PRESETS,
292
304
  PLUGINS_DIR,
293
305
  MANIFEST_FILENAMES,
294
306
  HISTORY_PAGE_LIMIT,
@@ -36,6 +36,25 @@ export interface InlineStylesResult {
36
36
  * Falls back to the raw string for a literal `%` in a filename.
37
37
  */
38
38
  export declare function decodeRef(ref: string): string;
39
+ /**
40
+ * True when `absPath` resolves OUTSIDE `projectDir`. Separator-aware, so a
41
+ * project-root file whose NAME begins with two dots (`..cover.png`) is NOT
42
+ * treated as an escape, and the `isAbsolute` arm catches a cross-drive Windows
43
+ * path that `path.relative` returns absolute. THE one containment predicate for
44
+ * this module's "is this asset in the project" decisions.
45
+ */
46
+ export declare function escapesProjectRoot(projectDir: string, absPath: string): boolean;
47
+ /**
48
+ * The build-failing reason a prose IMAGE ref cannot be used, or `null` when it
49
+ * is a usable in-project reference. THE single source of truth both
50
+ * `planImageCopies` (which rejects at build time) and the pre-build
51
+ * `source.links.local-refs` check consume, so validation and the build agree
52
+ * on the rule — an image referenced from Markdown prose must live inside the
53
+ * book folder — AND on the exact decode (`stripUrlSuffix(decodeRef(...))`) and
54
+ * wording. They previously hand-rolled this apart and had already drifted (the
55
+ * check decoded without stripping the `?query`/`#frag` suffix).
56
+ */
57
+ export declare function proseImageRefError(ref: string, projectDir: string): string | null;
39
58
  /**
40
59
  * Inline the active stylesheets into one CSS string, IN ORDER — the manifest's
41
60
  * `styles:` list is the cascade order (theme first, override second).
@@ -31,7 +31,6 @@ export declare const EXIT_CODES: {
31
31
  readonly USAGE: 2;
32
32
  readonly PIPELINE: 3;
33
33
  };
34
- export type ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];
35
34
  /**
36
35
  * BuildError — the canonical error type for build/pipeline failures.
37
36
  *
@@ -6,7 +6,21 @@ type PdfxFingerprintConfig = {
6
6
  };
7
7
  export type BuildFingerprintInput = {
8
8
  command: "build";
9
+ /** Where the fingerprint FILE is written — the build's ephemeral work dir. */
9
10
  outputDir: string;
11
+ /**
12
+ * The output dir to RECORD in `keyConfig.outputDir`. Defaults to
13
+ * {@link BuildFingerprintInput.outputDir}.
14
+ *
15
+ * These are the same for a caller that writes straight to its destination, but
16
+ * a build always assembles into a scratch work dir and only then publishes it
17
+ * atomically — so the file must be created in the work dir while the value
18
+ * anyone later reads has to be the delivered destination. One field served both
19
+ * roles, so shipped fingerprints recorded a scratch path
20
+ * (`.dist-build-a1b2c3…`, or an OS temp dir) that no longer existed
21
+ * (2026-07-29 audit).
22
+ */
23
+ recordedOutputDir?: string;
10
24
  sourceDir?: string;
11
25
  args: Record<string, unknown>;
12
26
  pdfx: PdfxFingerprintConfig;
@@ -75,7 +75,7 @@ export declare function splitOutPath(outArg: string | undefined, format: BuildFo
75
75
  * variant that reaches different code, so no `--out` value can be deleted —
76
76
  * there is no code path that would do it.
77
77
  */
78
- export type PublishTarget =
78
+ type PublishTarget =
79
79
  /** gutterpress's own `dist/<slug>/`. Replaced wholesale, so stale files vanish. */
80
80
  {
81
81
  kind: "project";
@@ -94,7 +94,27 @@ export type PublishTarget =
94
94
  export interface BuildContext {
95
95
  opts: BuildRunnerOptions;
96
96
  format: BuildFormat;
97
+ /**
98
+ * The directory the author pointed at (`gutterpress build <dir>`). Recorded in
99
+ * the build fingerprint as the source dir; NOT an anchor for resolving
100
+ * manifest-relative paths — see {@link BuildContext.renderDir}.
101
+ */
97
102
  inputDir: string;
103
+ /**
104
+ * THE anchor every manifest-relative path resolves against: `styles:`,
105
+ * `source.files`, authored plugin `path:` entries, and the lint gate's own
106
+ * stylesheet resolution. Equal to {@link BuildContext.manifestDir}.
107
+ *
108
+ * These used to resolve against two different roots in one build (2026-07-29
109
+ * audit): plugins, the lint gate, and the output dir anchored on
110
+ * `manifestDir`, while `styles:`/`source.files` anchored on `inputDir`. They
111
+ * are identical in the normative layout (the manifest lives in the book
112
+ * folder) and diverge only under an explicit `--manifest` pointing outside
113
+ * `--input` — where the docs are unambiguous that both are manifest-relative,
114
+ * so the lint gate was checking a different set of stylesheets than the ones
115
+ * that shipped.
116
+ */
117
+ renderDir: string;
98
118
  outDir: string;
99
119
  manifestDir: string;
100
120
  config: ReturnType<typeof resolveConfig>;
@@ -17,15 +17,6 @@ export declare function resolveGhostscript(targetPlatform?: NodeJS.Platform, env
17
17
  */
18
18
  export declare function stripAnnotations(pdfPath: string, stagingDir: string): Promise<void>;
19
19
  type PdfxFlavor = "x1a" | "x3";
20
- /**
21
- * Generate the PostScript definition file for PDF/X output intent + GCR.
22
- */
23
- export declare function makePdfxDefPs(params: {
24
- iccPath: string;
25
- pdfx: PdfxFlavor;
26
- title: string;
27
- maxTac?: number;
28
- }): string;
29
20
  /**
30
21
  * Stamp the Creator metadata field on an existing PDF.
31
22
  *
@@ -4,6 +4,13 @@ export interface ProjectConfigFields {
4
4
  authors?: string[];
5
5
  /** `source.files` — the markdown inputs (null means "all chapter files"). */
6
6
  sourceFiles?: string[] | null;
7
+ /**
8
+ * `targets:` — the publish destinations this book is validated against
9
+ * (ADR 0008). An empty array is written as an explicit `targets: []`, the
10
+ * visible "no destination policies" opt-out; `undefined` in an update
11
+ * leaves the key untouched.
12
+ */
13
+ targets?: string[];
7
14
  }
8
15
  /**
9
16
  * Apply the author-facing manifest field updates in a single yaml round-trip.
@@ -39,3 +39,11 @@ export declare function loadManifestWithPath(pathOrDir?: string, opts?: {
39
39
  * Any field explicitly set in `cliOverrides` wins, then manifest, then preset.
40
40
  */
41
41
  export declare function resolveConfig(cliOverrides: Partial<GutterpressManifest>, manifest: GutterpressManifest): ResolvedConfig;
42
+ /**
43
+ * Resolve the config AS ONE PUBLISH TARGET SEES IT (ADR 0008): the target's
44
+ * policy overlay is merged onto the preset before the manifest merges over
45
+ * both, giving the single precedence chain cli > manifest > target > preset.
46
+ * The author's explicit manifest values always beat the target's policy —
47
+ * the same sovereignty rule presets follow.
48
+ */
49
+ export declare function resolveConfigForTarget(cliOverrides: Partial<GutterpressManifest>, manifest: GutterpressManifest, targetId: string): ResolvedConfig;
@@ -37,12 +37,10 @@ export interface AssembleBookHtmlOptions {
37
37
  plugins?: LoadedPlugin[];
38
38
  pluginCss?: string;
39
39
  /**
40
- * Add `data-chapter-src="<file>"` to the same source-mapped block elements
41
- * that receive `data-source-line`. The historical option name is retained as
42
- * public API, but no wrapper is emitted: even `display: contents` changes CSS
43
- * selector relationships such as `body > *`, `:first-child`, and cross-file
44
- * siblings, which can change pagination. Off by default; build output is
45
- * unaffected.
40
+ * Wrap each source file in `<div class="gutterpress-chapter"
41
+ * data-chapter-src="<file>">`. Used only by incremental preview so one
42
+ * source can be paginated and replaced independently. Off by default; build
43
+ * output is unaffected.
46
44
  */
47
45
  wrapChapters?: boolean;
48
46
  /**
@@ -5,7 +5,7 @@
5
5
  * boundary: project-root-relative, forward slashes, no `./` prefix, no
6
6
  * duplicate slashes. This single form is used by:
7
7
  *
8
- * - preview source blocks' `data-chapter-src` tagging
8
+ * - preview source wrappers' `data-chapter-src` tagging
9
9
  * (lib/markdown/assemble.ts assembleBookHtml — index.ts's renderChapters
10
10
  * is now just the thin node:fs wrapper around it)
11
11
  * - preview source inspection and chapter-scoped scroll restoration
@@ -2,7 +2,6 @@ import { type LayoutWarning } from "./assemble";
2
2
  import { type AssetCopy } from "../asset-inline";
3
3
  import type { LoadedPlugin } from "./renderer";
4
4
  export type { LayoutWarning } from "./assemble";
5
- export { createMarkdownRenderer } from "./renderer";
6
5
  /**
7
6
  * THE canonical "which markdown files make up this book, and in what order?"
8
7
  * resolver — markdown's counterpart to `resolveActiveStyles` (style-resolver.ts).
@@ -38,7 +37,7 @@ export declare function renderChapters(inputDir: string, opts?: {
38
37
  files?: string[] | null;
39
38
  plugins?: LoadedPlugin[];
40
39
  pluginCss?: string;
41
- /** Add chapter identity to source-mapped blocks for preview inspection. */
40
+ /** Wrap each source file for incremental preview pagination. */
42
41
  wrapChapters?: boolean;
43
42
  /**
44
43
  * ARCH finding #4: per-chapter author-mistake warnings computed by
@@ -1,4 +1,4 @@
1
- export interface NpmPluginInstallLimits {
1
+ interface NpmPluginInstallLimits {
2
2
  metadataBytes: number;
3
3
  packageTarballBytes: number;
4
4
  totalNetworkBytes: number;
@@ -10,7 +10,6 @@
10
10
  *
11
11
  * `--out` remains for per-invocation placement (CI, one-offs).
12
12
  */
13
- export declare const DIST_DIRNAME = "dist";
14
13
  /** The rendered book document. Fixed: the desktop and index.html load it by name. */
15
14
  export declare const BOOK_HTML = "book.html";
16
15
  /** Slug identifying one book. Falls back to `book` for an unsluggable title. */
@@ -15,26 +15,6 @@ export interface ServerOverlay {
15
15
  body: string | Buffer;
16
16
  contentType: string;
17
17
  }
18
- /**
19
- * Start a localhost static file server rooted at `dir`, with optional in-memory
20
- * `overlays` keyed by URL path (e.g. `/book.html`).
21
- *
22
- * Overlays are what let the build paginate WITHOUT staging a second copy of the
23
- * project. `outDir` is served directly; the Paged.js-patched `book.html` and the
24
- * vendored polyfill are supplied from memory, so the engine never has to be
25
- * written into the shipped artifact and no asset is copied twice. This mirrors
26
- * the preview server, which has always served `/vendor/*` as a virtual overlay
27
- * rather than copying it per project.
28
- *
29
- * Path-traversal protection, the MIME map, and the actual file response are
30
- * the shared `./static-serve` primitives (`resolveStaticPath` + `serveFile`)
31
- * also used by preview/http-server.ts — a request that resolves outside `dir`
32
- * gets 403; a missing file gets 404 from `serveFile` itself.
33
- */
34
- export declare function createStaticFileServer(dir: string, defaultFile: string, overlays?: Record<string, ServerOverlay>): Promise<{
35
- port: number;
36
- close: () => Promise<void>;
37
- }>;
38
18
  /** Input handed to a PDF renderer: a URL serving the staged HTML + assets. */
39
19
  export interface PdfRenderInput {
40
20
  /** URL of the staged HTML on a local HTTP server (assets resolve relative). */
@@ -87,7 +87,7 @@ interface TextPass {
87
87
  }>;
88
88
  }
89
89
  export declare function getTextPass(doc: PDFDocumentProxy): Promise<TextPass>;
90
- export interface ImageRef {
90
+ interface ImageRef {
91
91
  /** XObject resource name (or "(inline)" for inline images). */
92
92
  name: string;
93
93
  /** Rendered width/height on the page, in points (from the CTM). */
@@ -96,7 +96,7 @@ export interface ImageRef {
96
96
  /** 1-based page number. */
97
97
  page: number;
98
98
  }
99
- export interface FontRef {
99
+ interface FontRef {
100
100
  name: string;
101
101
  embedded: boolean;
102
102
  }
@@ -25,7 +25,7 @@ export declare function parseInkCov(out: string): {
25
25
  k: number;
26
26
  sum: number;
27
27
  }[];
28
- export interface InkCoveragePage {
28
+ interface InkCoveragePage {
29
29
  page: number;
30
30
  c: number;
31
31
  m: number;
@@ -55,3 +55,4 @@ export type InkCoverageResult = {
55
55
  * callers must surface the failure, not treat it as "0 pages measured".
56
56
  */
57
57
  export declare function getPerPageInkCoverage(pdfPath: string): Promise<InkCoverageResult>;
58
+ export {};
@@ -22,8 +22,6 @@ interface PluginManagerInstallOptions extends NpmPluginInstallOptions {
22
22
  exportName?: string;
23
23
  __testFailBeforeManifestCommit?: () => void | Promise<void>;
24
24
  }
25
- /** Serialize all plugin filesystem + manifest mutations for one project. */
26
- export declare function withPluginMutationLock<T>(projectDir: string, mutation: () => Promise<T>): Promise<T>;
27
25
  /** Result of attempting to load one configured plugin. */
28
26
  export interface PluginValidationResult {
29
27
  ref: string;
@@ -1,5 +1,26 @@
1
1
  import type { ResolvedConfig } from "../schema/manifest.types";
2
- export type VendorPreset = Omit<ResolvedConfig, "title" | "authors">;
2
+ /**
3
+ * A preset is HOW THE BOOK IS DESIGNED (ADR 0008): the base defaults for the
4
+ * resolved config, chiefly page/trim geometry, plus the publish targets a
5
+ * book of this kind validates against when the manifest lists none. Where
6
+ * the book is PUBLISHED is a separate concern — see ./targets.ts.
7
+ *
8
+ * Every preset value is overridable from the manifest, leaf by leaf
9
+ * (resolveConfig's mergeShape; precedence cli > manifest > target > preset).
10
+ */
11
+ export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" | "targets" | "page"> {
12
+ /**
13
+ * Base page geometry in points, or `null` for `custom` — the one preset
14
+ * with no built-in trim, which therefore REQUIRES the manifest to supply
15
+ * `page.width` and `page.height` (resolveConfig enforces it).
16
+ */
17
+ page: ResolvedConfig["page"] | null;
18
+ /** Publish-target ids validated by default when the manifest has no `targets:`. */
19
+ defaultTargets: readonly string[];
20
+ }
21
+ /** The built-in preset ids, in the order pickers should offer them. */
22
+ export declare const PRESET_IDS: readonly ["dtrpg", "book", "custom"];
23
+ export type PresetId = (typeof PRESET_IDS)[number];
3
24
  export declare const DTRPG_PRESET: VendorPreset;
4
25
  /**
5
26
  * A neutral, vendor-agnostic default for authors who aren't targeting a
@@ -24,7 +45,14 @@ export declare const DTRPG_PRESET: VendorPreset;
24
45
  * distribution.
25
46
  */
26
47
  export declare const BOOK_PRESET: VendorPreset;
27
- export declare const PRESETS: Record<string, VendorPreset>;
48
+ /**
49
+ * `custom` — the author supplies the trim. Policy defaults are the neutral
50
+ * `book` ones (no vendor TAC cap, no PDF/X forcing); geometry is `null`, so
51
+ * resolveConfig demands explicit `page.width`/`page.height` (points) and
52
+ * errors, naming the missing fields, when they are absent.
53
+ */
54
+ export declare const CUSTOM_PRESET: VendorPreset;
55
+ export declare const PRESETS: Record<PresetId, VendorPreset>;
28
56
  export declare function warnOnce(id: string, message: string, sink?: (msg: string) => void): void;
29
57
  /** Test-only reset hook — clears all warn-once dedup state. */
30
58
  export declare function resetWarnOnce(): void;
@@ -32,11 +60,12 @@ export declare function resetWarnOnce(): void;
32
60
  * Resolve a manifest/CLI `preset` value to the {@link VendorPreset} it names
33
61
  * (UX finding M48).
34
62
  *
35
- * - Unset (`undefined`): defaults to `dtrpg` for backward compatibility
36
- * flipping the silent default to the new neutral `book` preset would change
37
- * output geometry (trim size, TAC cap, PDF/X forcing) for every existing
38
- * preset-less project. Emits a one-line warning instead so authors notice
39
- * and choose explicitly; a future major version may change the default.
63
+ * - Unset (`undefined`): defaults to `dtrpg`. That is the PRODUCT default,
64
+ * not a compatibility accident (ADR 0008): Gutterpress's primary audience
65
+ * is TTRPG authors producing print-on-demand content, and a preset-less
66
+ * manifest should come out print-ready for DriveThruRPG. Creation flows
67
+ * always write an explicit `preset:`, so this only applies to
68
+ * hand-written manifests — a one-line notice says which default applied.
40
69
  * - Unknown (typo'd) value: throws a {@link UsageError} naming the known
41
70
  * presets, instead of silently falling back to `dtrpg` — the previous
42
71
  * behavior turned a typo'd `preset: a4` into 621x810pt DriveThruRPG
@@ -1,10 +1,11 @@
1
+ import { type PresetId } from "./presets.ts";
1
2
  /**
2
3
  * Which embedded starter template to scaffold from. Each id maps to a directory
3
4
  * under `assets/templates/<id>/` (a `manifest.yaml` + a `chapter-01.md`), baked
4
5
  * into the binary via `embedded-assets.ts`. `"book"` is the default; the others
5
6
  * give non-technical authors a head start for common formats (#29).
6
7
  */
7
- export type ProjectTemplateId = "book" | "ttrpg" | "zine" | "technical";
8
+ export type ProjectTemplateId = "book" | "zine" | "technical";
8
9
  /**
9
10
  * How (or whether) to put the new project under local version history.
10
11
  *
@@ -40,9 +41,45 @@ export interface CreateProjectOptions {
40
41
  * author picks a saved/imported template.
41
42
  */
42
43
  templateDir?: string;
44
+ /**
45
+ * Which vendor preset the new book is designed for (ADR 0008). REQUIRED
46
+ * when scaffolding a built-in template — creation flows make the author
47
+ * choose; the resolveConfig dtrpg fallback exists for hand-written
48
+ * manifests, not for tooling. Written into the generated manifest as an
49
+ * explicit `preset:` line. Ignored with `templateDir`: a saved template's
50
+ * manifest carries its preset as part of the captured design.
51
+ */
52
+ preset?: PresetId;
53
+ /**
54
+ * Publish-target ids written into the generated manifest as an explicit
55
+ * `targets:` list (ADR 0008). Defaults to the chosen preset's
56
+ * `defaultTargets`; pass `[]` to opt out of every destination policy
57
+ * (e.g. when the tools a destination's checks need aren't installed).
58
+ * Like `preset`, the creation flow always records the choice explicitly —
59
+ * the preset-derived fallback exists for hand-written manifests only.
60
+ * Ignored with `templateDir` (the saved template's manifest is kept).
61
+ */
62
+ targets?: string[];
63
+ /**
64
+ * Page geometry written into the generated manifest (points; 72pt = 1in).
65
+ * REQUIRED when `preset` is `"custom"` (it has no built-in trim); allowed
66
+ * with any preset to override its trim. These are the validation bounds
67
+ * the built PDF is checked against — the actual trim comes from the
68
+ * stylesheet's `@page` rule, and the two should match.
69
+ */
70
+ customPage?: CustomPageOptions;
43
71
  /** Version-history mode for the new project. Defaults to `"local-git"`. */
44
72
  versionHistory?: ProjectVersionHistoryMode;
45
73
  }
74
+ /** Page bounds for {@link CreateProjectOptions.customPage}. */
75
+ export interface CustomPageOptions {
76
+ /** Trim width in points (72pt = 1in). */
77
+ width: number;
78
+ /** Trim height in points (72pt = 1in). */
79
+ height: number;
80
+ /** Allowed deviation in points when validating a built PDF. Default 0.5. */
81
+ tolerance?: number;
82
+ }
46
83
  /** The result of a successful scaffold. */
47
84
  export interface CreateProjectResult {
48
85
  /** Absolute path of the created project directory. */
@@ -68,7 +105,7 @@ export interface CreateProjectResult {
68
105
  * message and the CLI can choose an exit code. Scaffolding is fail-fast and
69
106
  * NEVER deletes or overwrites anything that already exists.
70
107
  */
71
- export type CreateProjectErrorCode = "parent-not-writable" | "target-exists" | "invalid-name" | "scaffold-io";
108
+ export type CreateProjectErrorCode = "parent-not-writable" | "target-exists" | "invalid-name" | "preset-required" | "custom-page-required" | "invalid-targets" | "scaffold-io";
72
109
  export interface CreateProjectError extends Error {
73
110
  code: CreateProjectErrorCode;
74
111
  }
@@ -65,15 +65,6 @@ export interface ProjectCapabilities {
65
65
  canRestoreSnapshot: boolean;
66
66
  authManagedByApp: boolean;
67
67
  }
68
- /**
69
- * The remote a project syncs against: `origin` when one exists, else the first
70
- * remote. Pure pick over a `listRemotes` result — see {@link syncRemoteFor}
71
- * for the I/O helper detection and the sync transport share.
72
- */
73
- export declare function pickSyncRemote<T extends {
74
- remote: string;
75
- url?: string;
76
- }>(remotes: T[]): T | undefined;
77
68
  /**
78
69
  * THE one answer to "which remote does this repo sync against, and what's its
79
70
  * URL" — shared by detection (`detectProjectSource`) and the sync transport
@@ -123,6 +114,17 @@ export declare function repoSubPath(repoRoot: string, folderPath: string): strin
123
114
  * `hasRemote: false`). Never returns `managed-github`.
124
115
  */
125
116
  export declare function detectProjectSource(folderPath: string): Promise<ProjectSource>;
117
+ /**
118
+ * The repository root a project's git operations act on, or `fallbackDir` when
119
+ * the project has no repo. THE one answer to "given a classified source, what
120
+ * repo does its snapshot/sync/log belong to" (R9: a project is its git repo) —
121
+ * so callers stop re-deriving `source.type === "local-git-folder" ?
122
+ * source.repoRoot : dir` inline (it was hand-rolled, and double-cast to an
123
+ * ad-hoc `{ type?; repoRoot? }` in two SvelteKit routes that lacked the type).
124
+ * A `managed-github` source (never produced by {@link detectProjectSource})
125
+ * has no local repo root either, so it also falls back. Pure; no I/O.
126
+ */
127
+ export declare function repoRootForSource(source: ProjectSource, fallbackDir: string): string;
126
128
  /**
127
129
  * Map a {@link ProjectSource} to the actions the UI may offer. Pure; no I/O.
128
130
  *
@@ -1,5 +1,5 @@
1
1
  /** The built-in templates shipped as embedded assets. */
2
- export declare const BUILT_IN_TEMPLATE_IDS: readonly ["book", "ttrpg", "zine", "technical"];
2
+ export declare const BUILT_IN_TEMPLATE_IDS: readonly ["book", "zine", "technical"];
3
3
  /** Author-friendly metadata for one template (built-in or custom). */
4
4
  export interface TemplateInfo {
5
5
  /** Stable id. For built-ins, the {@link ProjectTemplateId}; for custom, a slug. */
@@ -12,9 +12,36 @@ export interface TemplateInfo {
12
12
  kind: "builtin" | "custom";
13
13
  /** For custom templates: absolute directory the files live in. */
14
14
  dir?: string;
15
+ /**
16
+ * The `preset:` this template's manifest declares (ADR 0008), when it has
17
+ * one. Creation flows use it as the starting point for the preset choice —
18
+ * the template says what kind of book it is, so choosing one seeds
19
+ * everything under it.
20
+ */
21
+ preset?: string;
22
+ /** The `targets:` this template's manifest declares, when it has one. */
23
+ targets?: string[];
15
24
  }
16
- /** List the built-in templates (metadata only — no fs access). */
25
+ /**
26
+ * List the built-in templates. The `preset`/`targets` each one starts a book
27
+ * from are read from that template's own embedded manifest — one source of
28
+ * truth, so changing a template's manifest changes what the wizard offers.
29
+ */
17
30
  export declare function listBuiltInTemplates(): Promise<TemplateInfo[]>;
31
+ /**
32
+ * What to do with manifest references that point OUTSIDE the captured book — a
33
+ * repo-nested book's `../../shared/...` styles and authored plugins, which
34
+ * would dangle once the template is scaffolded somewhere else.
35
+ *
36
+ * - `"vendor"` (default): copy the referenced files — for a stylesheet, its
37
+ * whole `@import`/`url()` closure — INTO the template book-local, preserving
38
+ * the layout so the CSS's own relative refs still resolve, and rewrite the
39
+ * manifest entries to the book-local paths. Keeps the look; the template is
40
+ * a self-contained fork of the shared design at save time.
41
+ * - `"exclude"`: drop the escaping entries (copy nothing), leaving a
42
+ * book-local-only template.
43
+ */
44
+ export type SharedRefMode = "vendor" | "exclude";
18
45
  export interface SaveProjectAsTemplateOptions {
19
46
  /** Absolute path of the project to capture. */
20
47
  projectDir: string;
@@ -22,15 +49,26 @@ export interface SaveProjectAsTemplateOptions {
22
49
  name: string;
23
50
  /** Absolute directory custom templates are stored under. */
24
51
  templatesRoot: string;
52
+ /** How to handle out-of-book (`../../shared/...`) refs. Defaults to `"vendor"`. */
53
+ sharedRefs?: SharedRefMode;
54
+ }
55
+ /** The outcome of reconciling a captured book's out-of-book manifest refs. */
56
+ export interface SharedRefOutcome {
57
+ /** Book-local paths the escaping refs were vendored to (`"vendor"` mode). */
58
+ vendoredRefs: string[];
59
+ /** Manifest entries dropped because they pointed outside the book (`"exclude"` mode). */
60
+ excludedRefs: string[];
25
61
  }
26
62
  /**
27
63
  * Capture an existing project as a reusable custom template. Copies the whole
28
- * project tree (minus build/VCS dirs) into `<templatesRoot>/<slug(name)>/`, then
29
- * re-tokenises the project's title back to `{{TITLE}}` in the manifest so the
30
- * saved template scaffolds cleanly for the next book. Refuses to overwrite an
31
- * existing template directory (never deletes user data).
64
+ * project tree (minus build/VCS dirs) into `<templatesRoot>/<slug(name)>/`,
65
+ * reconciles any out-of-book (`../../shared/...`) refs so the template is
66
+ * portable ({@link SharedRefMode} vendor by default), then re-tokenises the
67
+ * project's title back to `{{TITLE}}` in the manifest so the saved template
68
+ * scaffolds cleanly for the next book. Refuses to overwrite an existing
69
+ * template directory (never deletes user data).
32
70
  */
33
- export declare function saveProjectAsTemplate(options: SaveProjectAsTemplateOptions): Promise<TemplateInfo>;
71
+ export declare function saveProjectAsTemplate(options: SaveProjectAsTemplateOptions): Promise<TemplateInfo & SharedRefOutcome>;
34
72
  /**
35
73
  * List custom templates saved under `templatesRoot`. Returns `[]` when the root
36
74
  * doesn't exist. Each subdirectory is a template; its label comes from the
@@ -1,3 +1,2 @@
1
1
  import { type PublishProvider } from "../types.ts";
2
- export declare const AZURE_SWA_HOST = "azure-swa";
3
2
  export declare const azureSwaProvider: PublishProvider;
@@ -1,5 +1,4 @@
1
1
  import { type PublishProvider } from "../types.ts";
2
- export declare const ITCH_HOST = "itch.io";
3
2
  /** `user/game` → the public project page URL. */
4
3
  export declare function itchProjectUrl(target: string): string;
5
4
  export declare const itchProvider: PublishProvider;
@@ -1,5 +1,4 @@
1
1
  import { type PublishProvider } from "../types.ts";
2
- export declare const SHOPIFY_HOST = "shopify";
3
2
  /** `gid://shopify/Product/123` → `123` (for admin deep links). */
4
3
  export declare function shopifyLegacyId(gid: string): string;
5
4
  export declare const shopifyProvider: PublishProvider;