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
@@ -28,8 +28,6 @@ export interface GitHubApiOptions {
28
28
  /** Injectable fetch for tests. Defaults to global fetch. */
29
29
  fetchImpl?: typeof fetch;
30
30
  }
31
- /** "Reconnect" message for revoked/expired tokens (ADR 0006 D7). */
32
- export declare const RECONNECT_MESSAGE = "Your GitHub connection has expired. Reconnect GitHub and try again.";
33
31
  /**
34
32
  * List every repository the user can access — own, collaborator, and org
35
33
  * member — via `GET /user/repos` (paginated). The OAuth `repo` scope makes
@@ -26,8 +26,3 @@ export type { RecoverFn, RecoveryContext, RecoveryResult, SyncErrorKind, Recover
26
26
  export { classifyGitError, classifyFromHealth, RepoNeedsRecoveryError, isRepoNeedsRecoveryError, } from "./classify.ts";
27
27
  export { inspectRepo, preflightStructuralReason, buildPreflightDiagnostics, verifyRepoReadable, isUnbornRepo, } from "./inspect.ts";
28
28
  export { buildRecoveryContext } from "./context.ts";
29
- export type { BuildRecoveryContextOptions } from "./context.ts";
30
- export { recoveryPolicy, policyFor, detachedHeadWithLocalChangesPolicy } from "./policy.ts";
31
- export { createRecoveryZip, assertZipReadable, zipEntries } from "./backup.ts";
32
- export { makeManualGuidance } from "./manual-guidance.ts";
33
- export { failSafeNoRepair, withBackupGate } from "./failsafe.ts";
@@ -32,9 +32,9 @@ export declare const RETRY_AFTER_MS = 30000;
32
32
  /** The typed outcome unions the recovery handlers translate. */
33
33
  export type AnyOutcome = SyncOutcome | PullOutcome;
34
34
  /** Every status either outcome union can carry. */
35
- export type OutcomeStatus = AnyOutcome["status"];
35
+ type OutcomeStatus = AnyOutcome["status"];
36
36
  /** Builds a RecoveryResult from the context and the (already-classified) outcome. */
37
- export type OutcomeBuilder = (ctx: RecoveryContext, outcome: AnyOutcome) => RecoveryResult;
37
+ type OutcomeBuilder = (ctx: RecoveryContext, outcome: AnyOutcome) => RecoveryResult;
38
38
  /** Per-status overrides a handler supplies where it intentionally differs. */
39
39
  export type OutcomeOverrides = Partial<Record<OutcomeStatus, OutcomeBuilder>>;
40
40
  /**
@@ -56,3 +56,4 @@ export declare function syncOptionsFrom(ctx: RecoveryContext): {
56
56
  * first, then default — matching each handler's original `default:` arm.
57
57
  */
58
58
  export declare function mapOutcomeToResult(ctx: RecoveryContext, outcome: AnyOutcome, overrides?: OutcomeOverrides): RecoveryResult;
59
+ export {};
@@ -48,14 +48,6 @@
48
48
  * says "leftover lock" / "temporary lock", never "lock file"/"index"/"ref".
49
49
  */
50
50
  import type { RecoveryContext, RecoveryResult } from "./types.ts";
51
- /**
52
- * Known top-level lock files git may leave directly under `.git/`. These are
53
- * scanned in addition to a shallow walk of `.git/refs/**` for `*.lock` files.
54
- * Exported (with findLockCandidates) as the SINGLE lock-discovery
55
- * implementation — inspect.ts's health probe consumes the same scan, so
56
- * health and handler can never disagree about which locks exist.
57
- */
58
- export declare const TOP_LEVEL_LOCK_NAMES: readonly ["index.lock", "HEAD.lock", "config.lock", "packed-refs.lock"];
59
51
  export interface LockCandidate {
60
52
  /** Absolute path to the lock file. */
61
53
  path: string;
@@ -1,17 +1,9 @@
1
1
  import { conflictFilesFrom, onAuthFor } from "./transport.ts";
2
- import type { PullOutcome, PushOutcome, SyncOutcome, SyncProjectOptions, SyncRetryOptions } from "./sync-types.ts";
2
+ import type { PullOutcome, PushOutcome, SyncOutcome, SyncProjectOptions } from "./sync-types.ts";
3
3
  export { onAuthFor, conflictFilesFrom };
4
4
  export { onlineCopyPath, resolveConflicts } from "./conflict-resolution.ts";
5
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">>;
6
+ export type { ConflictFile, ConflictKind, ConflictResolution, PullOutcome, PushOutcome, ResolveConflictsOptions, SyncOutcome, SyncProjectOptions, } from "./sync-types.ts";
15
7
  /**
16
8
  * Snapshot-first sync (ADR 0006 D5) — the composition of {@link pullChanges}
17
9
  * then {@link pushChanges}. If someone pushes between our pull and our push,
@@ -33,8 +33,6 @@ export declare function isSshRemoteUrl(url: string): boolean;
33
33
  * Exported for the wording guard in test-access.test.ts.
34
34
  */
35
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
36
  /**
39
37
  * Probe a remote with a single refs listing (the `git ls-remote` equivalent).
40
38
  *
@@ -2,34 +2,10 @@ import type httpNode from "isomorphic-git/http/node";
2
2
  type HttpClient = typeof httpNode;
3
3
  export declare function pkt(data: string | Buffer): Buffer;
4
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
5
  export declare function createFixtureRepo(dir: string): Promise<{
17
6
  head: string;
18
7
  first: string;
19
8
  }>;
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
9
  export interface GitServer {
34
10
  url: string;
35
11
  /** Authorization headers the server saw, in order. */
@@ -55,17 +31,4 @@ export declare function tempDir(prefix: string): Promise<string>;
55
31
  * dangling-tracking-ref failure). Shared by the transport and recovery tests.
56
32
  */
57
33
  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
34
  export {};
@@ -90,7 +90,6 @@ export interface SourceProvider {
90
90
  listHistoryPage(projectDir: string, options?: ListHistoryOptions): Promise<HistoryPage>;
91
91
  restore(options: RestoreSnapshotOptions): Promise<void>;
92
92
  }
93
- export declare const SNAPSHOT_STAGING_MARKER = "gutterpress-snapshot-staging";
94
93
  /**
95
94
  * Run `fn` exclusively per resolved project dir (FIFO promise chaining).
96
95
  * Exported for the remote-clone surface (#15) so clone/fetch operations share
@@ -104,19 +103,6 @@ export declare function withRepoLock<T>(projectDir: string, fn: () => Promise<T>
104
103
  * plugins.ts's `__resetPathPluginCacheForTests`.
105
104
  */
106
105
  export declare function __repoLockQueueSizeForTests(): number;
107
- /**
108
- * Resolve the commit author identity from an optional display name (exported
109
- * for the sync surface so merge commits carry the same identity as
110
- * snapshots).
111
- */
112
- export declare function gitAuthor(name?: string, email?: string): {
113
- name: string;
114
- email: string;
115
- };
116
- export declare function readGitAuthor(dir: string): Promise<{
117
- name?: string;
118
- email?: string;
119
- }>;
120
106
  /**
121
107
  * Resolve the author for a commit in `dir`, PER FIELD: caller-supplied →
122
108
  * existing repo config (`user.name` / `user.email`) → the gutterpress default.
@@ -33,11 +33,50 @@ export declare const STATIC_MIME: Record<string, string>;
33
33
  * query value as a filesystem-relative path must run it through this guard.
34
34
  */
35
35
  export declare function resolveWithinRoot(relPath: string, root: string): string | null;
36
+ /**
37
+ * True if any path segment of `urlPathname` is a dotfile/dot-directory (e.g.
38
+ * `/.env`, `/.git/config`, `/foo/.hidden/bar`).
39
+ *
40
+ * This is the guard any server needs when it resolves request paths against a
41
+ * REAL project tree rather than a staged copy of generated output (the preview
42
+ * server's serve-in-place model). Containment alone is not sufficient there:
43
+ * `.env` and `.git/config` live INSIDE the served root, so
44
+ * {@link resolveWithinRoot} happily returns them — this is the one thing
45
+ * standing between a request and the project's actual secrets.
46
+ *
47
+ * It runs on the DECODED path so a percent-encoded segment (`%2e%2e`) can't
48
+ * spell a dot past a naive string check, and an encoding that fails to decode
49
+ * is refused rather than guessed at.
50
+ *
51
+ * BOTH `/` and `\` count as separators, on every platform. A raw backslash
52
+ * never survives the WHATWG URL parser (it normalizes to `/` for special
53
+ * schemes), but a percent-encoded one does: `/%5C.env` decodes to `/\.env`,
54
+ * whose only `/`-delimited segment is `\.env` — which does not START with a
55
+ * dot. `path.win32.resolve` DOES treat `\` as a separator, so on Windows that
56
+ * request resolved to `<root>\.env`, inside the root, and the preview served
57
+ * the project's real `.env`. Splitting on both separators closes that hole,
58
+ * and doing it platform-independently is what lets a POSIX test run pin the
59
+ * Windows behavior.
60
+ */
61
+ export declare function hasDotSegment(urlPathname: string): boolean;
36
62
  /**
37
63
  * Resolve a request URL pathname to an absolute path inside `root`, guarding
38
64
  * against path traversal (`..`, encoded separators, absolute-looking paths).
39
65
  * Returns `null` if the pathname cannot be decoded or the resolved path
40
66
  * escapes `root` — callers turn that into a 403/404 as fits their route.
67
+ *
68
+ * Containment only — a caller serving a real project tree must ALSO run
69
+ * {@link hasDotSegment}, since dotfiles are contained but must stay unreachable.
70
+ *
71
+ * LEXICAL, deliberately: it normalizes `..`/`.` but does not resolve symlinks, so
72
+ * a symlink INSIDE the served root that points outside it resolves to its target
73
+ * when the file is read. That is a considered choice, not an oversight (reviewed
74
+ * 2026-07-29): these are localhost preview/pagination servers reading a tree the
75
+ * author owns and controls, and a project-local symlink into a shared foundation
76
+ * is a legitimate authoring layout — the desktop's own fs routes, which serve
77
+ * renderer-supplied paths and therefore face a real attacker, use the
78
+ * canonicalizing `isWithinAnyRootCanonical` instead. If this guard is ever reused
79
+ * for a path an untrusted party supplies, canonicalize first.
41
80
  */
42
81
  export declare function resolveStaticPath(urlPathname: string, root: string): string | null;
43
82
  /**
@@ -26,5 +26,14 @@ export declare function resolveActiveStyles(projectDir: string, manifestStyles?:
26
26
  * (`resolveActiveStyles`, marked `active: true`) followed by the project's OTHER
27
27
  * discovered `.css` files (alphabetical). `projectDir` must be absolute. Returns
28
28
  * `[]` for a project with no stylesheets at all.
29
+ *
30
+ * Pass `repoRoot` for a book that lives inside a repository: the repo's shared
31
+ * stylesheets are then offered too (see {@link discoverSharedCssFiles}), named
32
+ * the way the manifest stores them — project-relative, e.g.
33
+ * `../../shared/styles/components.css` — so toggling one on writes the right
34
+ * entry. Omit it (or pass the project itself) for the single-project case, which
35
+ * behaves exactly as before.
29
36
  */
30
- export declare function listProjectStyles(projectDir: string): Promise<ProjectStyle[]>;
37
+ export declare function listProjectStyles(projectDir: string, opts?: {
38
+ repoRoot?: string;
39
+ }): Promise<ProjectStyle[]>;
@@ -0,0 +1,84 @@
1
+ import type { ResolvedConfig } from "../schema/manifest.types";
2
+ import { type VendorPreset } from "./presets";
3
+ /**
4
+ * Publish targets — WHERE THE BOOK IS PUBLISHED (ADR 0008).
5
+ *
6
+ * A target is a named validation-policy overlay: the requirements a
7
+ * destination (DriveThruRPG, itch.io, …) places on the built PDF. Targets
8
+ * never change how the book renders — they change what the validator
9
+ * demands of the output — so one source can be validated against several
10
+ * destinations (`targets: [dtrpg, itch]` in the manifest).
11
+ *
12
+ * A target's overlay is DATA merged between the preset and the manifest,
13
+ * giving one precedence chain everywhere: cli > manifest > target > preset.
14
+ * The author's explicit manifest values therefore always win — the same
15
+ * sovereignty rule presets follow. The only hard enforcement a target adds
16
+ * is {@link PublishTarget.requiredChecks}: a required check that gets
17
+ * skipped because its tools are missing becomes a synthetic error, so
18
+ * "validated for dtrpg" can never silently mean "the dtrpg checks didn't
19
+ * run".
20
+ *
21
+ * Grown from the old `validate --profile dtrpg` (validation-profile.ts,
22
+ * deleted): the profile was this exact idea hardcoded for one destination.
23
+ */
24
+ /** The deep-partial preset shape a target may overlay. */
25
+ export type TargetOverlay = {
26
+ pdfx?: Partial<ResolvedConfig["pdfx"]>;
27
+ ink?: Partial<ResolvedConfig["ink"]>;
28
+ validate?: {
29
+ checks?: ResolvedConfig["validate"]["checks"];
30
+ assets?: Partial<ResolvedConfig["validate"]["assets"]>;
31
+ pdf?: Partial<ResolvedConfig["validate"]["pdf"]>;
32
+ };
33
+ };
34
+ export interface PublishTarget {
35
+ id: string;
36
+ /** Display name for reports and pickers. */
37
+ label: string;
38
+ /** Policy defaults merged between the preset and the manifest. */
39
+ overlay: TargetOverlay;
40
+ /**
41
+ * Check ids that must actually RUN for this target: when tool
42
+ * availability forces one to be skipped, validation reports a synthetic
43
+ * error instead of a false green. (An author who explicitly disables one
44
+ * of these in the manifest owns that choice — this guards silent
45
+ * environment gaps, not deliberate configuration.)
46
+ */
47
+ requiredChecks: readonly string[];
48
+ /**
49
+ * External system tools this destination's full pipeline depends on —
50
+ * building the compliant output AND running the required checks. Creation
51
+ * flows use this to warn, at target-selection time, that a compliant file
52
+ * can't be produced or verified until these are installed. Must cover at
53
+ * least the union of {@link requiredChecks}' own `requiredTools`
54
+ * (enforced by targets.test.ts); may add build-time tools the checks
55
+ * alone don't reveal (e.g. Ghostscript for the PDF/X conversion).
56
+ */
57
+ requiredTools: readonly string[];
58
+ }
59
+ export declare const TARGETS: Record<string, PublishTarget>;
60
+ /** The registered target ids, in the order pickers should offer them. */
61
+ export declare const TARGET_IDS: string[];
62
+ /** Look up a target id; unknown ids are a usage error naming the registry. */
63
+ export declare function publishTargetFor(id: string): PublishTarget;
64
+ /**
65
+ * Resolve the effective target-id list for a run: the requested list when
66
+ * given (manifest `targets:` or `--target`), else the preset's defaults.
67
+ * Validates every id and dedupes while preserving order.
68
+ */
69
+ export declare function resolveTargets(requested: readonly string[] | undefined, fallback: readonly string[]): string[];
70
+ /**
71
+ * Merge a target's overlay onto a preset, producing the effective preset a
72
+ * target-scoped `resolveConfig` run merges the manifest over. This is what
73
+ * puts the overlay BELOW the manifest in the precedence chain.
74
+ */
75
+ export declare function overlayPreset(preset: VendorPreset, target: PublishTarget): VendorPreset;
76
+ /**
77
+ * The strict-PDF fill-ins applied to EVERY post-build PDF validation
78
+ * regardless of preset/target: any of these left `undefined` by the config
79
+ * becomes an error-severity check, without overwriting an author's explicit
80
+ * choice (including an explicit disable). Moved verbatim from
81
+ * validation-profile.ts (ARCH #21).
82
+ */
83
+ export declare const DEFAULT_STRICT_PDF_CHECKS: readonly ["pdf.structure.qpdf", "pdf.print.pdfx-markers", "pdf.print.pdfx-metadata", "pdf.print.embedded-fonts"];
84
+ export declare function applyDefaultPdfStrictChecks(config: ResolvedConfig): ResolvedConfig;
@@ -2,8 +2,6 @@ import { type PrintSafeWarning } from "./printsafe.ts";
2
2
  import { type ThemeInfo } from "./theme-manager.ts";
3
3
  /** Reject a raw archive larger than this before unzipping (zip-bomb surface). */
4
4
  export declare const MAX_THEME_ARCHIVE_BYTES: number;
5
- /** Reject an archive whose entries expand past this in total (zip-bomb surface). */
6
- export declare const MAX_THEME_UNZIPPED_BYTES: number;
7
5
  /** A non-fatal issue surfaced to the author after a successful import. */
8
6
  export interface ThemeImportWarning {
9
7
  code: "print-safety" | "no-theme-json" | "unnamed-theme" | "extra-files";
@@ -1,7 +1,7 @@
1
1
  /** Folder (relative to the project root) themes are copied into on apply/import. */
2
2
  export declare const THEMES_DIR = "themes";
3
3
  /** The built-in themes shipped as embedded assets (ids are folder names). */
4
- export declare const BUILT_IN_THEME_IDS: readonly ["clean-book", "ttrpg-supplement", "zine", "technical-doc"];
4
+ export declare const BUILT_IN_THEME_IDS: readonly ["clean-book", "zine", "technical-doc"];
5
5
  export type BuiltInThemeId = (typeof BUILT_IN_THEME_IDS)[number];
6
6
  /** Parsed `theme.json` metadata (every field optional in the file). */
7
7
  export interface ThemeMetadata {
@@ -2,7 +2,6 @@ import { type OutputFormat } from "../checks/formatter";
2
2
  import { type RunnerOptions, type RunnerReport } from "../checks/runner";
3
3
  import { type ToolCheckResult } from "../checks/tool-check";
4
4
  import type { CheckContext, CheckResult } from "../checks/types";
5
- import { type ValidationProfile } from "./validation-profile";
6
5
  import type { ResolvedConfig } from "../schema/manifest.types";
7
6
  import "../checks/register-builtins";
8
7
  export interface ValidationExecutionArgs {
@@ -13,11 +12,13 @@ export interface ValidationExecutionArgs {
13
12
  only?: string;
14
13
  skip?: string;
15
14
  phase?: string;
16
- profile?: string;
15
+ /** Publish-target ids (CSV) to validate against, overriding the manifest's `targets:` for this run. */
16
+ target?: string;
17
17
  }
18
18
  export interface ValidationExecutionResult {
19
19
  config: ResolvedConfig;
20
- profile?: ValidationProfile;
20
+ /** The publish targets this run validated against (may be empty). */
21
+ targets: string[];
21
22
  context: CheckContext;
22
23
  runnerOptions: RunnerOptions;
23
24
  tools: ToolCheckResult;
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  MANIFEST_FILENAMES,
3
3
  runLint
4
- } from "./cli-7ykx7pp0.js";
4
+ } from "./cli-k4bd06sd.js";
5
5
  import {
6
6
  log
7
- } from "./cli-h7phg3ka.js";
7
+ } from "./cli-najycadg.js";
8
8
  import {
9
9
  EXIT_CODES,
10
10
  UsageError,
11
11
  rejectExtraPositionals,
12
12
  rejectUnknownFlags
13
- } from "./cli-hvqjs342.js";
14
- import"./cli-5bxx0c4y.js";
15
- import"./cli-1tyj4bk3.js";
13
+ } from "./cli-e5zhb0xs.js";
14
+ import"./cli-yzrh708h.js";
15
+ import"./cli-wchtvxvw.js";
16
16
  import"./cli-yja077f6.js";
17
17
  import"./cli-37x76zdn.js";
18
18
 
@@ -25,9 +25,15 @@
25
25
  },
26
26
  "preset": {
27
27
  "type": "string",
28
- "description": "Vendor preset supplying the defaults for every other section (page geometry, ink limits, PDF/X settings, validation checks). Omitting it defaults to \"dtrpg\" for backward compatibility and logs a warning set it explicitly.",
29
- "enum": ["dtrpg", "book"],
30
- "examples": ["dtrpg", "book"]
28
+ "description": "How the book is designed (ADR 0008): the base defaults for every other section page geometry, ink limits, PDF/X settings, validation checks all overridable field-by-field below. \"dtrpg\" = DriveThruRPG print-on-demand (the product default when omitted); \"book\" = neutral 6x9in trade book; \"custom\" = you supply page.width and page.height yourself (required).",
29
+ "enum": ["dtrpg", "book", "custom"],
30
+ "examples": ["dtrpg", "book", "custom"]
31
+ },
32
+ "targets": {
33
+ "type": "array",
34
+ "description": "Where the book is published (ADR 0008): publish targets whose requirements `gutterpress validate` checks the built PDF against — each target is validated separately, so one book can target several destinations. Omitting it uses the preset's default (dtrpg -> [\"dtrpg\"]; book/custom -> []).",
35
+ "items": { "type": "string", "enum": ["dtrpg", "itch"] },
36
+ "examples": [["dtrpg"], ["dtrpg", "itch"]]
31
37
  },
32
38
  "styles": {
33
39
  "type": "array",
@@ -517,12 +523,12 @@
517
523
  }
518
524
  },
519
525
  {
520
- "title": "TTRPG Supplement",
521
- "authors": ["Game Master"],
526
+ "title": "Print-Ready Supplement",
527
+ "authors": ["Author Name"],
522
528
  "preset": "dtrpg",
523
529
  "plugins": [
524
530
  {
525
- "path": "plugins/dimm-city-plugin.js",
531
+ "path": "plugins/my-plugin.js",
526
532
  "priority": 100
527
533
  }
528
534
  ],
@@ -0,0 +1,185 @@
1
+ import {
2
+ BUILT_IN_TEMPLATE_IDS,
3
+ PRESETS,
4
+ PRESET_IDS,
5
+ TARGETS,
6
+ TARGET_IDS,
7
+ scaffoldProject
8
+ } from "./cli-k4bd06sd.js";
9
+ import {
10
+ isToolAvailable,
11
+ resolveGhostscript
12
+ } from "./cli-najycadg.js";
13
+ import {
14
+ EXIT_CODES,
15
+ UsageError,
16
+ rejectExtraPositionals,
17
+ rejectUnknownFlags
18
+ } from "./cli-e5zhb0xs.js";
19
+ import"./cli-yzrh708h.js";
20
+ import"./cli-wchtvxvw.js";
21
+ import"./cli-yja077f6.js";
22
+ import"./cli-37x76zdn.js";
23
+
24
+ // src/commands/new.ts
25
+ import { defineCommand } from "citty";
26
+ import { resolve } from "node:path";
27
+ var newArgs = {
28
+ name: {
29
+ type: "positional",
30
+ description: "Project name (becomes the title and folder name)",
31
+ required: true
32
+ },
33
+ preset: {
34
+ type: "string",
35
+ description: `Vendor preset the book is designed for: ${PRESET_IDS.join(", ")} (required; custom also needs --page-width/--page-height)`
36
+ },
37
+ author: {
38
+ type: "string",
39
+ description: "Author name to record in the project"
40
+ },
41
+ dir: {
42
+ type: "string",
43
+ description: "Parent directory to create the project in (default: current directory)"
44
+ },
45
+ folder: {
46
+ type: "string",
47
+ description: "Folder name to create (default: a slug of the project name)"
48
+ },
49
+ template: {
50
+ type: "string",
51
+ description: `Starter template: ${BUILT_IN_TEMPLATE_IDS.join(", ")} (default: book)`
52
+ },
53
+ targets: {
54
+ type: "string",
55
+ description: `Publish targets recorded in the manifest (comma-separated: ${TARGET_IDS.join(", ")}; or "none") — default: the preset's`
56
+ },
57
+ "page-width": {
58
+ type: "string",
59
+ description: "Trim width in points, 72pt = 1in (required with --preset custom; optional override otherwise)"
60
+ },
61
+ "page-height": {
62
+ type: "string",
63
+ description: "Trim height in points, 72pt = 1in (required with --preset custom; optional override otherwise)"
64
+ },
65
+ "page-tolerance": {
66
+ type: "string",
67
+ description: "Allowed trim deviation in points when validating a built PDF (default: 0.5)"
68
+ },
69
+ git: {
70
+ type: "boolean",
71
+ description: "Initialise local version history (default: true; use --no-git to skip)",
72
+ default: true
73
+ }
74
+ };
75
+ function parsePoints(raw, flag) {
76
+ if (typeof raw !== "string" || raw === "")
77
+ return;
78
+ const value = Number(raw);
79
+ if (!Number.isFinite(value) || value <= 0) {
80
+ console.error(`Invalid ${flag} value: "${raw}". Expected a positive number of points (72pt = 1in).`);
81
+ process.exit(EXIT_CODES.USAGE);
82
+ }
83
+ return value;
84
+ }
85
+ var new_default = defineCommand({
86
+ meta: {
87
+ name: "new",
88
+ description: "Create a new Gutterpress project from a starter template"
89
+ },
90
+ args: newArgs,
91
+ async run({ args, rawArgs }) {
92
+ try {
93
+ rejectUnknownFlags(rawArgs, newArgs, "new");
94
+ rejectExtraPositionals(args._, 1, "new");
95
+ } catch (error) {
96
+ if (error instanceof UsageError) {
97
+ console.error(error.message);
98
+ process.exit(error.exitCode);
99
+ }
100
+ throw error;
101
+ }
102
+ const name = String(args.name);
103
+ const parentDir = resolve(typeof args.dir === "string" && args.dir ? args.dir : process.cwd());
104
+ let template;
105
+ if (typeof args.template === "string" && args.template) {
106
+ if (!BUILT_IN_TEMPLATE_IDS.includes(args.template)) {
107
+ console.error(`Unknown template "${args.template}". Choose one of: ${BUILT_IN_TEMPLATE_IDS.join(", ")}.`);
108
+ process.exit(EXIT_CODES.USAGE);
109
+ }
110
+ template = args.template;
111
+ }
112
+ const preset = typeof args.preset === "string" && args.preset ? args.preset : undefined;
113
+ if (!preset || !PRESET_IDS.includes(preset)) {
114
+ console.error(preset ? `Unknown preset "${preset}". Choose one of: ${PRESET_IDS.join(", ")}.` : `A preset is required: --preset <${PRESET_IDS.join("|")}>. ` + `Use "dtrpg" for DriveThruRPG print-on-demand, "book" for a neutral 6x9in trade book, ` + `or "custom" with --page-width/--page-height (points; 72pt = 1in).`);
115
+ process.exit(EXIT_CODES.USAGE);
116
+ }
117
+ let targets;
118
+ if (typeof args.targets === "string" && args.targets) {
119
+ targets = args.targets.trim().toLowerCase() === "none" ? [] : args.targets.split(",").map((s) => s.trim()).filter(Boolean);
120
+ const unknown = targets.filter((id) => !TARGETS[id]);
121
+ if (unknown.length > 0) {
122
+ console.error(`Unknown publish target${unknown.length > 1 ? "s" : ""} "${unknown.join('", "')}". ` + `Choose from: ${TARGET_IDS.join(", ")} — or "none" for no destination policies.`);
123
+ process.exit(EXIT_CODES.USAGE);
124
+ }
125
+ }
126
+ const pageWidth = parsePoints(args["page-width"], "--page-width");
127
+ const pageHeight = parsePoints(args["page-height"], "--page-height");
128
+ const pageTolerance = parsePoints(args["page-tolerance"], "--page-tolerance");
129
+ const customPage = pageWidth !== undefined && pageHeight !== undefined ? { width: pageWidth, height: pageHeight, ...pageTolerance !== undefined ? { tolerance: pageTolerance } : {} } : undefined;
130
+ if (preset === "custom" && !customPage) {
131
+ console.error("The custom preset needs a trim size: --page-width and --page-height in points (72pt = 1in — e.g. US Letter is 612 x 792).");
132
+ process.exit(EXIT_CODES.USAGE);
133
+ }
134
+ try {
135
+ const result = await scaffoldProject({
136
+ name,
137
+ author: typeof args.author === "string" ? args.author : undefined,
138
+ parentDir,
139
+ folderName: typeof args.folder === "string" && args.folder ? args.folder : undefined,
140
+ template,
141
+ preset,
142
+ targets,
143
+ customPage,
144
+ versionHistory: args.git === false ? "none" : "local-git"
145
+ });
146
+ const effectiveTargets = targets ?? [...PRESETS[preset].defaultTargets];
147
+ console.log(`Created project: ${result.projectDir}`);
148
+ console.log(` manifest: ${result.manifestPath}`);
149
+ console.log(` publish targets: ${effectiveTargets.length > 0 ? effectiveTargets.join(", ") : "none"}`);
150
+ console.log(` start writing in: ${result.openFile}`);
151
+ if (result.versionHistory === "local-git") {
152
+ console.log(" version history: enabled (local snapshots)");
153
+ } else if (result.versionHistoryError) {
154
+ console.log(` version history: not enabled (${result.versionHistoryError})`);
155
+ }
156
+ const neededTools = [
157
+ ...new Set(effectiveTargets.flatMap((id) => TARGETS[id]?.requiredTools ?? []))
158
+ ];
159
+ const missingTools = [];
160
+ for (const tool of neededTools) {
161
+ const found = tool === "gs" ? !!await resolveGhostscript() : await isToolAvailable(tool);
162
+ if (!found)
163
+ missingTools.push(tool === "gs" ? "Ghostscript (gs)" : tool);
164
+ }
165
+ if (missingTools.length > 0) {
166
+ const plural = missingTools.length > 1;
167
+ console.log("");
168
+ console.log(` note: ${missingTools.join(" and ")} ${plural ? "are" : "is"} not installed — a ` + `print-compliant (PDF/X) file can't be built or verified until ${plural ? "they are" : "it is"}.`);
169
+ console.log(" Install them (User Guide, Chapter 7 — System Setup), or set `targets: []` in");
170
+ console.log(" manifest.yaml to skip destination checks for now.");
171
+ }
172
+ console.log("");
173
+ console.log(`Next: gutterpress preview "${result.projectDir}"`);
174
+ } catch (e) {
175
+ const err = e;
176
+ const code = err && typeof err.code === "string" ? err.code : "scaffold-io";
177
+ console.error(`Could not create project: ${err?.message ?? String(e)}`);
178
+ process.exit(code === "scaffold-io" ? EXIT_CODES.PIPELINE : EXIT_CODES.USAGE);
179
+ }
180
+ }
181
+ });
182
+ export {
183
+ newArgs,
184
+ new_default as default
185
+ };