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.
- package/README.md +21 -16
- package/dist/api/index.d.ts +6 -2
- package/dist/api/index.js +15 -3
- package/dist/{audit-1jrwwacz.js → audit-nhn2pjz3.js} +5 -5
- package/dist/{build-5s92kgpc.js → build-san7fv2z.js} +5 -5
- package/dist/checks/types.d.ts +6 -0
- package/dist/{cli-hvqjs342.js → cli-e5zhb0xs.js} +2 -2
- package/dist/{cli-7ykx7pp0.js → cli-k4bd06sd.js} +982 -331
- package/dist/{cli-h7phg3ka.js → cli-najycadg.js} +258 -258
- package/dist/{index-1tyj4bk3.js → cli-wchtvxvw.js} +4 -1
- package/dist/{cli-5bxx0c4y.js → cli-yzrh708h.js} +2 -2
- package/dist/cli.js +15 -15
- package/dist/{doctor-25zjt8dz.js → doctor-hrk0kxxz.js} +2 -2
- package/dist/{index-3h3fef7r.js → index-bynn850m.js} +1200 -410
- package/dist/{cli-1tyj4bk3.js → index-wchtvxvw.js} +4 -1
- package/dist/{index-5bxx0c4y.js → index-yzrh708h.js} +2 -2
- package/dist/index.js +15 -3
- package/dist/lib/asset-inline.d.ts +19 -0
- package/dist/lib/build-error.d.ts +0 -1
- package/dist/lib/build-fingerprint.d.ts +14 -0
- package/dist/lib/build-runner.d.ts +21 -1
- package/dist/lib/ghostscript.d.ts +0 -9
- package/dist/lib/manifest-config.d.ts +7 -0
- package/dist/lib/manifest.d.ts +8 -0
- package/dist/lib/markdown/assemble.d.ts +4 -6
- package/dist/lib/markdown/chapter-id.d.ts +1 -1
- package/dist/lib/markdown/index.d.ts +1 -2
- package/dist/lib/npm-plugin-installer.d.ts +1 -1
- package/dist/lib/output-paths.d.ts +0 -1
- package/dist/lib/pagination.d.ts +0 -20
- package/dist/lib/pdf-inspect.d.ts +2 -2
- package/dist/lib/pdf-parse.d.ts +2 -1
- package/dist/lib/plugin-manager.d.ts +0 -2
- package/dist/lib/presets.d.ts +36 -7
- package/dist/lib/project-scaffold.d.ts +39 -2
- package/dist/lib/project-source.d.ts +11 -9
- package/dist/lib/project-templates.d.ts +45 -7
- package/dist/lib/publish/providers/azure-swa.d.ts +0 -1
- package/dist/lib/publish/providers/itch.d.ts +0 -1
- package/dist/lib/publish/providers/shopify.d.ts +0 -1
- package/dist/lib/remote-auth/github-repos.d.ts +0 -2
- package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -5
- package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +3 -2
- package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -8
- package/dist/lib/remote-auth/sync.d.ts +2 -10
- package/dist/lib/remote-auth/test-access.d.ts +0 -2
- package/dist/lib/remote-auth/test-support/git-http-server.d.ts +0 -37
- package/dist/lib/source-provider.d.ts +0 -14
- package/dist/lib/static-serve.d.ts +39 -0
- package/dist/lib/style-resolver.d.ts +10 -1
- package/dist/lib/targets.d.ts +84 -0
- package/dist/lib/theme-import.d.ts +0 -2
- package/dist/lib/theme-manager.d.ts +1 -1
- package/dist/lib/validation-exec.d.ts +4 -3
- package/dist/{lint-f63nxjfj.js → lint-96j9hrj4.js} +5 -5
- package/dist/{manifest.schema-rn1dpdpn.json → manifest.schema-z61rzw44.json} +12 -6
- package/dist/new-8p38wavc.js +185 -0
- package/dist/{pagedjs-interface-80g3xa8p.js → pagedjs-interface-qxvzgwd7.js} +92 -33
- package/dist/{plugin-wf2cbmwc.js → plugin-ees6nhkc.js} +5 -5
- package/dist/{preflight-tg4mpz1h.js → preflight-1q6c2edh.js} +24 -29
- package/dist/preview/file-watcher.d.ts +48 -39
- package/dist/preview/http-server.d.ts +5 -2
- package/dist/preview/server-context.d.ts +24 -0
- package/dist/preview-shell-6dqexx1m.js +581 -0
- package/dist/{preview-h2bwbb2x.js → preview-y5a2zen1.js} +5 -5
- package/dist/{project-source-0tg0r6vm.js → project-source-p0gn1wd5.js} +3 -3
- package/dist/{publish-ztaet53r.js → publish-rm9yb3wh.js} +5 -5
- package/dist/render.js +10 -19
- package/dist/{repair-jkp4n3j8.js → repair-zgq7q2g6.js} +5 -5
- package/dist/schema/manifest.types.d.ts +14 -3
- package/dist/server.d.ts +2 -0
- package/dist/{source-provider-hp96b5jf.js → source-provider-c1rjm2c0.js} +2 -8
- package/dist/types.d.ts +0 -1
- package/dist/{validate-k2drs19j.js → validate-nr0xa6sa.js} +8 -8
- package/package.json +2 -2
- package/dist/chapter-01-jj8ftn4c.md +0 -29
- package/dist/lib/validation-profile.d.ts +0 -18
- package/dist/manifest-ryyg71nf.yaml +0 -9
- package/dist/new-7b08a42h.js +0 -105
- package/dist/preview-shell-rghtfmc8.js +0 -155
- package/dist/theme-0yzvthe8.css +0 -65
- 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
|
-
|
|
35
|
+
type OutcomeStatus = AnyOutcome["status"];
|
|
36
36
|
/** Builds a RecoveryResult from the context and the (already-classified) outcome. */
|
|
37
|
-
|
|
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
|
|
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,
|
|
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
|
|
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", "
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
4
|
+
} from "./cli-k4bd06sd.js";
|
|
5
5
|
import {
|
|
6
6
|
log
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-najycadg.js";
|
|
8
8
|
import {
|
|
9
9
|
EXIT_CODES,
|
|
10
10
|
UsageError,
|
|
11
11
|
rejectExtraPositionals,
|
|
12
12
|
rejectUnknownFlags
|
|
13
|
-
} from "./cli-
|
|
14
|
-
import"./cli-
|
|
15
|
-
import"./cli-
|
|
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": "
|
|
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": "
|
|
521
|
-
"authors": ["
|
|
526
|
+
"title": "Print-Ready Supplement",
|
|
527
|
+
"authors": ["Author Name"],
|
|
522
528
|
"preset": "dtrpg",
|
|
523
529
|
"plugins": [
|
|
524
530
|
{
|
|
525
|
-
"path": "plugins/
|
|
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
|
+
};
|