gutterpress 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/README.md +422 -0
  2. package/dist/CGATS21_CRPC1-g0e3k7kr.icc +0 -0
  3. package/dist/api/index.d.ts +76 -0
  4. package/dist/api/index.js +272 -0
  5. package/dist/assets/preview/scripts/pagedjs-bridge.d.ts +1 -0
  6. package/dist/assets/preview/scripts/pagedjs-interface.d.ts +1 -0
  7. package/dist/assets/preview/scripts/preview-shell.d.ts +1 -0
  8. package/dist/audit-1jrwwacz.js +66 -0
  9. package/dist/build-5s92kgpc.js +77 -0
  10. package/dist/chapter-01-6fn9pvd0.md +31 -0
  11. package/dist/chapter-01-7jwqg1ct.md +20 -0
  12. package/dist/chapter-01-jj8ftn4c.md +29 -0
  13. package/dist/chapter-01-rkpyvjna.md +26 -0
  14. package/dist/checks/asset/approved-fonts.d.ts +3 -0
  15. package/dist/checks/asset/extensions.d.ts +41 -0
  16. package/dist/checks/asset/font-license.d.ts +3 -0
  17. package/dist/checks/asset/image-alpha.d.ts +3 -0
  18. package/dist/checks/asset/image-color-space.d.ts +3 -0
  19. package/dist/checks/asset/image-file-size.d.ts +3 -0
  20. package/dist/checks/asset/image-resolution.d.ts +3 -0
  21. package/dist/checks/asset/image-tac.d.ts +3 -0
  22. package/dist/checks/asset/index.d.ts +7 -0
  23. package/dist/checks/formatter.d.ts +3 -0
  24. package/dist/checks/heuristic/index.d.ts +4 -0
  25. package/dist/checks/heuristic/layer-count.d.ts +3 -0
  26. package/dist/checks/heuristic/placement-variance.d.ts +3 -0
  27. package/dist/checks/heuristic/section-density.d.ts +3 -0
  28. package/dist/checks/heuristic/text-density.d.ts +3 -0
  29. package/dist/checks/pdf/bleed.d.ts +3 -0
  30. package/dist/checks/pdf/bookmarks.d.ts +3 -0
  31. package/dist/checks/pdf/color-spaces.d.ts +3 -0
  32. package/dist/checks/pdf/cross-refs.d.ts +3 -0
  33. package/dist/checks/pdf/embedded-fonts.d.ts +3 -0
  34. package/dist/checks/pdf/image-resolution.d.ts +3 -0
  35. package/dist/checks/pdf/index.d.ts +15 -0
  36. package/dist/checks/pdf/ink-coverage.d.ts +3 -0
  37. package/dist/checks/pdf/page-labels.d.ts +3 -0
  38. package/dist/checks/pdf/page-size.d.ts +3 -0
  39. package/dist/checks/pdf/pdfx-markers.d.ts +3 -0
  40. package/dist/checks/pdf/pdfx-metadata.d.ts +3 -0
  41. package/dist/checks/pdf/pdfx-structure.d.ts +6 -0
  42. package/dist/checks/pdf/qpdf-structure.d.ts +3 -0
  43. package/dist/checks/pdf/rasterized-pages.d.ts +3 -0
  44. package/dist/checks/pdf/toc-links.d.ts +3 -0
  45. package/dist/checks/pdf/transparency.d.ts +3 -0
  46. package/dist/checks/policy.d.ts +61 -0
  47. package/dist/checks/register-builtins.d.ts +17 -0
  48. package/dist/checks/registry.d.ts +40 -0
  49. package/dist/checks/runner.d.ts +25 -0
  50. package/dist/checks/source/accessibility-alt-text.d.ts +3 -0
  51. package/dist/checks/source/accessibility-heading-order.d.ts +3 -0
  52. package/dist/checks/source/config-file.d.ts +8 -0
  53. package/dist/checks/source/htmlhint.d.ts +3 -0
  54. package/dist/checks/source/index.d.ts +6 -0
  55. package/dist/checks/source/local-refs.d.ts +3 -0
  56. package/dist/checks/source/markdownlint.d.ts +3 -0
  57. package/dist/checks/source/stylelint.d.ts +3 -0
  58. package/dist/checks/tool-check.d.ts +23 -0
  59. package/dist/checks/types.d.ts +52 -0
  60. package/dist/cli-1tyj4bk3.js +124 -0
  61. package/dist/cli-37x76zdn.js +4 -0
  62. package/dist/cli-506tg37g.js +26 -0
  63. package/dist/cli-5bxx0c4y.js +464 -0
  64. package/dist/cli-7ykx7pp0.js +12378 -0
  65. package/dist/cli-h7phg3ka.js +746 -0
  66. package/dist/cli-hvqjs342.js +284 -0
  67. package/dist/cli-yja077f6.js +92 -0
  68. package/dist/cli.js +157 -0
  69. package/dist/constants.d.ts +17 -0
  70. package/dist/doctor-25zjt8dz.js +57 -0
  71. package/dist/favicon-wkbm9cjn.ico +0 -0
  72. package/dist/git-http-yrb4ag6z.js +17 -0
  73. package/dist/index-1tyj4bk3.js +124 -0
  74. package/dist/index-37x76zdn.js +4 -0
  75. package/dist/index-3h3fef7r.js +15218 -0
  76. package/dist/index-5bxx0c4y.js +464 -0
  77. package/dist/index-yja077f6.js +92 -0
  78. package/dist/index.d.ts +32 -0
  79. package/dist/index.js +310 -0
  80. package/dist/lib/app-heartbeat.d.ts +102 -0
  81. package/dist/lib/asset-inline.d.ts +73 -0
  82. package/dist/lib/browser-pool.d.ts +17 -0
  83. package/dist/lib/build-error.d.ts +52 -0
  84. package/dist/lib/build-fingerprint.d.ts +15 -0
  85. package/dist/lib/build-preflight.d.ts +43 -0
  86. package/dist/lib/build-runner.d.ts +161 -0
  87. package/dist/lib/build-staging.d.ts +64 -0
  88. package/dist/lib/chromium.d.ts +20 -0
  89. package/dist/lib/cli-args.d.ts +45 -0
  90. package/dist/lib/desktop.d.ts +14 -0
  91. package/dist/lib/diagnostics.d.ts +64 -0
  92. package/dist/lib/embedded-assets.d.ts +29 -0
  93. package/dist/lib/exec.d.ts +84 -0
  94. package/dist/lib/fetch-timeout.d.ts +41 -0
  95. package/dist/lib/ghostscript.d.ts +48 -0
  96. package/dist/lib/host-policy.d.ts +75 -0
  97. package/dist/lib/image-inspect.d.ts +32 -0
  98. package/dist/lib/install-hints.d.ts +21 -0
  99. package/dist/lib/lint-runner.d.ts +10 -0
  100. package/dist/lib/manifest-config.d.ts +31 -0
  101. package/dist/lib/manifest-doc.d.ts +25 -0
  102. package/dist/lib/manifest.d.ts +41 -0
  103. package/dist/lib/markdown/assemble.d.ts +78 -0
  104. package/dist/lib/markdown/chapter-id.d.ts +24 -0
  105. package/dist/lib/markdown/images.d.ts +34 -0
  106. package/dist/lib/markdown/index.d.ts +78 -0
  107. package/dist/lib/markdown/markdown-it-paged.d.ts +30 -0
  108. package/dist/lib/markdown/plugins.d.ts +69 -0
  109. package/dist/lib/markdown/renderer.d.ts +111 -0
  110. package/dist/lib/npm-plugin-installer.d.ts +33 -0
  111. package/dist/lib/open-path.d.ts +5 -0
  112. package/dist/lib/output-paths.d.ts +23 -0
  113. package/dist/lib/pagedjs-marker.d.ts +42 -0
  114. package/dist/lib/pagedjs.d.ts +26 -0
  115. package/dist/lib/pagination.d.ts +169 -0
  116. package/dist/lib/pdf-inspect.d.ts +123 -0
  117. package/dist/lib/pdf-parse.d.ts +57 -0
  118. package/dist/lib/plugin-manager.d.ts +108 -0
  119. package/dist/lib/plugin-vendor.d.ts +107 -0
  120. package/dist/lib/presets.d.ts +45 -0
  121. package/dist/lib/printsafe.d.ts +17 -0
  122. package/dist/lib/project-scaffold.d.ts +116 -0
  123. package/dist/lib/project-source.d.ts +140 -0
  124. package/dist/lib/project-templates.d.ts +49 -0
  125. package/dist/lib/publish/butler.d.ts +10 -0
  126. package/dist/lib/publish/command-runner.d.ts +18 -0
  127. package/dist/lib/publish/connect.d.ts +23 -0
  128. package/dist/lib/publish/manifest-publish.d.ts +9 -0
  129. package/dist/lib/publish/providers/azure-swa.d.ts +3 -0
  130. package/dist/lib/publish/providers/drivethrurpg.d.ts +10 -0
  131. package/dist/lib/publish/providers/guided.d.ts +3 -0
  132. package/dist/lib/publish/providers/itch.d.ts +5 -0
  133. package/dist/lib/publish/providers/kdp.d.ts +9 -0
  134. package/dist/lib/publish/providers/shopify.d.ts +5 -0
  135. package/dist/lib/publish/registry.d.ts +5 -0
  136. package/dist/lib/publish/run-publish.d.ts +30 -0
  137. package/dist/lib/publish/selections.d.ts +25 -0
  138. package/dist/lib/publish/types.d.ts +258 -0
  139. package/dist/lib/remote-auth/clone.d.ts +87 -0
  140. package/dist/lib/remote-auth/conflict-resolution.d.ts +29 -0
  141. package/dist/lib/remote-auth/diagnose.d.ts +77 -0
  142. package/dist/lib/remote-auth/generic-auth.d.ts +97 -0
  143. package/dist/lib/remote-auth/git-http.d.ts +85 -0
  144. package/dist/lib/remote-auth/github-auth.d.ts +69 -0
  145. package/dist/lib/remote-auth/github-repos.d.ts +64 -0
  146. package/dist/lib/remote-auth/operation-log.d.ts +66 -0
  147. package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +103 -0
  148. package/dist/lib/remote-auth/recovery/backup.d.ts +113 -0
  149. package/dist/lib/remote-auth/recovery/classify.d.ts +109 -0
  150. package/dist/lib/remote-auth/recovery/context.d.ts +47 -0
  151. package/dist/lib/remote-auth/recovery/dispatch.d.ts +33 -0
  152. package/dist/lib/remote-auth/recovery/failsafe.d.ts +33 -0
  153. package/dist/lib/remote-auth/recovery/inspect.d.ts +74 -0
  154. package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +28 -0
  155. package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +58 -0
  156. package/dist/lib/remote-auth/recovery/policy.d.ts +47 -0
  157. package/dist/lib/remote-auth/recovery/recover-auth.d.ts +42 -0
  158. package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +37 -0
  159. package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +40 -0
  160. package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +70 -0
  161. package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +23 -0
  162. package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +28 -0
  163. package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +39 -0
  164. package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +34 -0
  165. package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +37 -0
  166. package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +56 -0
  167. package/dist/lib/remote-auth/recovery/recover-network.d.ts +34 -0
  168. package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +27 -0
  169. package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +76 -0
  170. package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +44 -0
  171. package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +35 -0
  172. package/dist/lib/remote-auth/recovery/types.d.ts +235 -0
  173. package/dist/lib/remote-auth/resolution-plan.d.ts +64 -0
  174. package/dist/lib/remote-auth/sync-messages.d.ts +26 -0
  175. package/dist/lib/remote-auth/sync-types.d.ts +225 -0
  176. package/dist/lib/remote-auth/sync.d.ts +45 -0
  177. package/dist/lib/remote-auth/test-access.d.ts +47 -0
  178. package/dist/lib/remote-auth/test-support/git-http-server.d.ts +71 -0
  179. package/dist/lib/remote-auth/token-store.d.ts +95 -0
  180. package/dist/lib/remote-auth/transport.d.ts +105 -0
  181. package/dist/lib/slug.d.ts +28 -0
  182. package/dist/lib/snippets.d.ts +39 -0
  183. package/dist/lib/source-provider.d.ts +244 -0
  184. package/dist/lib/static-serve.d.ts +54 -0
  185. package/dist/lib/style-resolver.d.ts +30 -0
  186. package/dist/lib/theme-import.d.ts +70 -0
  187. package/dist/lib/theme-manager.d.ts +128 -0
  188. package/dist/lib/tool-probe.d.ts +37 -0
  189. package/dist/lib/validation-exec.d.ts +47 -0
  190. package/dist/lib/validation-profile.d.ts +18 -0
  191. package/dist/lib/version.d.ts +9 -0
  192. package/dist/lint-f63nxjfj.js +65 -0
  193. package/dist/manifest-n1gh3qw5.yaml +9 -0
  194. package/dist/manifest-ryyg71nf.yaml +9 -0
  195. package/dist/manifest.schema-rn1dpdpn.json +564 -0
  196. package/dist/new-7b08a42h.js +105 -0
  197. package/dist/paged.polyfill-n95pbxfn.js +33288 -0
  198. package/dist/pagedjs-bridge-vn4hk9fx.js +111 -0
  199. package/dist/pagedjs-interface-80g3xa8p.js +498 -0
  200. package/dist/platform.d.ts +100 -0
  201. package/dist/plugin-wf2cbmwc.js +129 -0
  202. package/dist/preflight-tg4mpz1h.js +222 -0
  203. package/dist/preview/file-watcher.d.ts +174 -0
  204. package/dist/preview/http-server.d.ts +45 -0
  205. package/dist/preview/lifecycle.d.ts +67 -0
  206. package/dist/preview/server-context.d.ts +36 -0
  207. package/dist/preview-h2bwbb2x.js +102 -0
  208. package/dist/preview-shell-rghtfmc8.js +155 -0
  209. package/dist/project-source-0tg0r6vm.js +17 -0
  210. package/dist/publish-ztaet53r.js +233 -0
  211. package/dist/render.d.ts +20 -0
  212. package/dist/render.js +682 -0
  213. package/dist/repair-jkp4n3j8.js +171 -0
  214. package/dist/schema/manifest.types.d.ts +218 -0
  215. package/dist/server.d.ts +34 -0
  216. package/dist/source-provider-hp96b5jf.js +46 -0
  217. package/dist/test-helpers/testkit.d.ts +91 -0
  218. package/dist/theme-0yzvthe8.css +65 -0
  219. package/dist/theme-570zmh2t.css +58 -0
  220. package/dist/theme-6a2fngc8.json +6 -0
  221. package/dist/theme-cd6efzjw.json +6 -0
  222. package/dist/theme-nya4nqh6.css +70 -0
  223. package/dist/theme-tqfypc1w.json +6 -0
  224. package/dist/theme-xt0hjb1b.json +6 -0
  225. package/dist/theme-zz2ktzqs.css +60 -0
  226. package/dist/types.d.ts +31 -0
  227. package/dist/utils/file-utils.d.ts +53 -0
  228. package/dist/utils/logger.d.ts +59 -0
  229. package/dist/validate-k2drs19j.js +99 -0
  230. package/package.json +102 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Platform abstraction contract (GitHub #41).
3
+ *
4
+ * The desktop talks to its host through ONE seam so that the app code never
5
+ * branches on `electron` vs `web` directly. Today the only host is Electron
6
+ * (IPC + native dialogs). When the PWA lands (0.6.0, #...), a `WebAdapter`
7
+ * backed by the File System Access API drops in behind the same interface and
8
+ * the app code does not change.
9
+ *
10
+ * This module is **types only** — it is imported by the browser SPA via
11
+ * `import type`, so it must never pull in a Node runtime dependency.
12
+ *
13
+ * Canonical home: `gutterpress`. The desktop re-exports these types
14
+ * from `src/lib/platform/` and implements them in `electron-adapter.ts` /
15
+ * `web-adapter.ts`. Keep the implementations in lockstep with this contract.
16
+ */
17
+ /**
18
+ * The narrow set of capabilities whose implementation genuinely differs between
19
+ * a desktop (Electron) host and a browser (PWA) host: native dialogs, raw file
20
+ * IO, filesystem watching, and OS-keychain-backed secrets.
21
+ *
22
+ * Host RPC services that are *also* host-divergent (preview/build/doctor/prefs/
23
+ * updater) are modelled separately as {@link HostServices} so this primitive
24
+ * surface stays small and easy to reason about. The full thing the app consumes
25
+ * is {@link Platform} = `PlatformAdapter & HostServices`.
26
+ */
27
+ /**
28
+ * Filesystem metadata for a single path (GitHub #44 — external-edit detection).
29
+ * `mtimeMs` is the modification time in epoch milliseconds; `exists` is `false`
30
+ * (with `mtimeMs`/`size` = 0) when the path is absent rather than throwing, so
31
+ * the editor can distinguish "deleted out from under us" from a read error.
32
+ */
33
+ export interface FileStat {
34
+ mtimeMs: number;
35
+ size: number;
36
+ exists: boolean;
37
+ }
38
+ /**
39
+ * Result of a successful {@link PlatformAdapter.writeFile} (GitHub #44). Carries
40
+ * the post-write modification time so the editor can record the on-disk baseline
41
+ * mtime without a follow-up `statFile` round-trip — this is what lets
42
+ * external-edit detection suppress the self-echo of our own debounced save.
43
+ */
44
+ export interface FileWriteResult {
45
+ mtimeMs: number;
46
+ }
47
+ export interface PlatformAdapter {
48
+ /** Which host backs this adapter. Lets the rare unavoidable branch be explicit. */
49
+ readonly platform: "electron" | "web";
50
+ /**
51
+ * Prompt the user to choose a project folder.
52
+ * @returns the absolute path, or `null` if the user cancelled.
53
+ * Electron: native directory dialog. Web: File System Access API.
54
+ */
55
+ openFolder(): Promise<string | null>;
56
+ /**
57
+ * Read a UTF-8 text file by absolute path.
58
+ * Editor seam for #38/#39 — no current consumer in 0.4.0.
59
+ */
60
+ readFile(path: string): Promise<string>;
61
+ /**
62
+ * Write a UTF-8 text file by absolute path, creating/overwriting it.
63
+ * Resolves with the post-write {@link FileWriteResult} (`{ mtimeMs }`) so the
64
+ * editor (#44) can record the on-disk baseline mtime; callers that ignore the
65
+ * value are unaffected (additive widening — was `Promise<void>`).
66
+ */
67
+ writeFile(path: string, content: string): Promise<FileWriteResult>;
68
+ /**
69
+ * List the immediate entries of a directory (single level, no recursion).
70
+ * @returns each entry's `name`, absolute `path`, and whether it `isDir`.
71
+ * Editor seam for #38 (file-tree sidebar). Electron: `node:fs/promises`
72
+ * `readdir`. Web: File System Access API (0.6.0).
73
+ */
74
+ listDir(path: string): Promise<Array<{
75
+ name: string;
76
+ path: string;
77
+ isDir: boolean;
78
+ }>>;
79
+ /**
80
+ * Stat a file by absolute path (GitHub #44). Used by the editor to confirm a
81
+ * `watchFolder` event reflects a real on-disk change (mtime moved) versus the
82
+ * self-echo of our own `writeFile`. Resolves with `exists: false` rather than
83
+ * rejecting when the path is absent. Web throws until 0.6.0 (FS Access API).
84
+ */
85
+ statFile(path: string): Promise<FileStat>;
86
+ /**
87
+ * Watch a folder for changes, invoking `cb` on each change.
88
+ * @returns an unsubscribe function.
89
+ * Web has no general recursive-watch primitive — `WebAdapter` throws here
90
+ * until 0.6.0. Electron wiring lands with the in-app editor (#38).
91
+ */
92
+ watchFolder(path: string, cb: () => void): () => void;
93
+ /**
94
+ * Read a secret by key (e.g. a GitHub token). Lands with #12.
95
+ * Electron: OS keychain via `safeStorage`. Web: encrypted localStorage.
96
+ */
97
+ getSecret(key: string): Promise<string | null>;
98
+ /** Store a secret by key. Lands with #12. */
99
+ setSecret(key: string, value: string): Promise<void>;
100
+ }
@@ -0,0 +1,129 @@
1
+ import {
2
+ addNpmPlugin,
3
+ parseNpmPluginSpec
4
+ } from "./cli-7ykx7pp0.js";
5
+ import"./cli-h7phg3ka.js";
6
+ import {
7
+ EXIT_CODES,
8
+ UsageError,
9
+ rejectExtraPositionals,
10
+ rejectUnknownFlags
11
+ } from "./cli-hvqjs342.js";
12
+ import"./cli-5bxx0c4y.js";
13
+ import"./cli-1tyj4bk3.js";
14
+ import"./cli-yja077f6.js";
15
+ import"./cli-37x76zdn.js";
16
+
17
+ // src/commands/plugin.ts
18
+ import { defineCommand } from "citty";
19
+ import { stat } from "node:fs/promises";
20
+ import path from "node:path";
21
+ var pluginAddArgs = {
22
+ package: {
23
+ type: "positional",
24
+ description: "npm package name, optionally followed by @version or @dist-tag",
25
+ required: true
26
+ },
27
+ dir: {
28
+ type: "positional",
29
+ description: "Project directory (defaults to the current directory)",
30
+ required: false
31
+ },
32
+ export: {
33
+ type: "string",
34
+ description: "Named module export to use as the plugin function",
35
+ required: false
36
+ }
37
+ };
38
+ var parentArgs = {};
39
+ function exitForUsage(error) {
40
+ if (error instanceof UsageError) {
41
+ console.error(error.message);
42
+ process.exit(error.exitCode);
43
+ }
44
+ throw error;
45
+ }
46
+ function rejectParentFlags(rawArgs) {
47
+ if (rawArgs[0] === "add" || rawArgs[0] === undefined || !rawArgs[0].startsWith("-")) {
48
+ return;
49
+ }
50
+ try {
51
+ rejectUnknownFlags(rawArgs, parentArgs, "plugin");
52
+ } catch (error) {
53
+ exitForUsage(error);
54
+ }
55
+ }
56
+ var add = defineCommand({
57
+ meta: {
58
+ name: "add",
59
+ description: "Download, verify, and vendor an npm markdown-it plugin"
60
+ },
61
+ args: pluginAddArgs,
62
+ async run({ args, rawArgs }) {
63
+ try {
64
+ rejectUnknownFlags(rawArgs, pluginAddArgs, "plugin add");
65
+ rejectExtraPositionals(args._, 2, "plugin add");
66
+ } catch (error) {
67
+ exitForUsage(error);
68
+ }
69
+ const projectDir = path.resolve(typeof args.dir === "string" && args.dir ? args.dir : process.cwd());
70
+ const packageSpec = String(args.package);
71
+ const exportName = typeof args.export === "string" ? args.export.trim() : undefined;
72
+ try {
73
+ try {
74
+ parseNpmPluginSpec(packageSpec);
75
+ } catch (error) {
76
+ throw new UsageError(error instanceof Error ? error.message : String(error));
77
+ }
78
+ if (typeof args.export === "string" && !exportName) {
79
+ throw new UsageError("gutterpress plugin add: --export requires a non-empty name");
80
+ }
81
+ let projectInfo;
82
+ try {
83
+ projectInfo = await stat(projectDir);
84
+ } catch (error) {
85
+ if (error.code === "ENOENT") {
86
+ throw new UsageError(`Project directory does not exist: ${projectDir}`);
87
+ }
88
+ throw error;
89
+ }
90
+ if (!projectInfo.isDirectory()) {
91
+ throw new UsageError(`Project path is not a directory: ${projectDir}`);
92
+ }
93
+ } catch (error) {
94
+ exitForUsage(error);
95
+ }
96
+ try {
97
+ const installed = await addNpmPlugin(projectDir, packageSpec, exportName);
98
+ if (installed.version) {
99
+ console.log(`Installed ${installed.ref}@${installed.version}`);
100
+ console.log(` project: ${projectDir}`);
101
+ console.log(" vendored under: plugins/npm");
102
+ console.log(" manifest: exact version pinned");
103
+ for (const warning of installed.warnings ?? [])
104
+ console.warn(`Warning: ${warning}`);
105
+ } else {
106
+ console.log(`Enabled bundled plugin ${installed.ref}`);
107
+ console.log(` project: ${projectDir}`);
108
+ }
109
+ } catch (error) {
110
+ console.error(`Could not install plugin: ${error instanceof Error ? error.message : String(error)}`);
111
+ process.exit(EXIT_CODES.PIPELINE);
112
+ }
113
+ }
114
+ });
115
+ var plugin_default = defineCommand({
116
+ meta: {
117
+ name: "plugin",
118
+ description: "Manage project markdown-it plugins"
119
+ },
120
+ args: parentArgs,
121
+ setup({ rawArgs }) {
122
+ rejectParentFlags(rawArgs);
123
+ },
124
+ subCommands: { add }
125
+ });
126
+ export {
127
+ pluginAddArgs,
128
+ plugin_default as default
129
+ };
@@ -0,0 +1,222 @@
1
+ import {
2
+ executeValidation,
3
+ reportMissingTools
4
+ } from "./cli-7ykx7pp0.js";
5
+ import {
6
+ log
7
+ } from "./cli-h7phg3ka.js";
8
+ import {
9
+ EXIT_CODES,
10
+ UsageError,
11
+ rejectExtraPositionals,
12
+ rejectUnknownFlags
13
+ } from "./cli-hvqjs342.js";
14
+ import"./cli-5bxx0c4y.js";
15
+ import"./cli-1tyj4bk3.js";
16
+ import"./cli-yja077f6.js";
17
+ import"./cli-37x76zdn.js";
18
+
19
+ // src/commands/preflight.ts
20
+ import { defineCommand } from "citty";
21
+ import { mkdir, writeFile } from "node:fs/promises";
22
+ import { basename, dirname, extname, join, resolve } from "node:path";
23
+ function computeStatus(errors, warnings, missingTools, skippedRequired) {
24
+ if (errors > 0 || skippedRequired)
25
+ return "NO-GO";
26
+ if (warnings > 0 || missingTools > 0)
27
+ return "FIX";
28
+ return "GO";
29
+ }
30
+ function severityRank(severity) {
31
+ if (severity === "error")
32
+ return 0;
33
+ if (severity === "warning")
34
+ return 1;
35
+ return 2;
36
+ }
37
+ function buildRequiredChecks(execution) {
38
+ if (execution.profile !== "dtrpg")
39
+ return [];
40
+ const requiredIds = [
41
+ "pdf.structure.qpdf",
42
+ "pdf.print.pdfx-markers",
43
+ "pdf.print.pdfx-metadata",
44
+ "pdf.print.embedded-fonts"
45
+ ];
46
+ const skipped = new Set(execution.tools.skippedChecks);
47
+ const failed = new Set(execution.report.errors.map((item) => item.checkId));
48
+ const passed = new Set(execution.report.passed);
49
+ return requiredIds.map((id) => {
50
+ if (skipped.has(id))
51
+ return { id, status: "skipped" };
52
+ if (failed.has(id))
53
+ return { id, status: "fail" };
54
+ if (passed.has(id))
55
+ return { id, status: "pass" };
56
+ return { id, status: "fail" };
57
+ });
58
+ }
59
+ function buildPreflightPayload(execution) {
60
+ const sortedIssues = [...execution.report.results].sort((a, b) => {
61
+ const severityCmp = severityRank(a.severity) - severityRank(b.severity);
62
+ if (severityCmp !== 0)
63
+ return severityCmp;
64
+ const checkCmp = a.checkId.localeCompare(b.checkId);
65
+ if (checkCmp !== 0)
66
+ return checkCmp;
67
+ const fileCmp = (a.file ?? "").localeCompare(b.file ?? "");
68
+ if (fileCmp !== 0)
69
+ return fileCmp;
70
+ const lineCmp = (a.line ?? 0) - (b.line ?? 0);
71
+ if (lineCmp !== 0)
72
+ return lineCmp;
73
+ const colCmp = (a.column ?? 0) - (b.column ?? 0);
74
+ if (colCmp !== 0)
75
+ return colCmp;
76
+ return a.message.localeCompare(b.message);
77
+ }).map((item) => ({
78
+ checkId: item.checkId,
79
+ severity: item.severity,
80
+ message: item.message,
81
+ file: item.file,
82
+ line: item.line,
83
+ column: item.column
84
+ }));
85
+ const requiredChecks = buildRequiredChecks(execution);
86
+ const skippedRequired = requiredChecks.some((check) => check.status === "skipped");
87
+ return {
88
+ schemaVersion: 1,
89
+ profile: execution.profile ?? null,
90
+ status: computeStatus(execution.report.summary.errors, execution.report.summary.warnings, execution.tools.missing.length, skippedRequired),
91
+ pdf: execution.context.pdfPath ?? "",
92
+ summary: { ...execution.report.summary },
93
+ tools: {
94
+ missing: [...execution.tools.missing].sort((a, b) => a.localeCompare(b)),
95
+ skippedChecks: [...execution.tools.skippedChecks].sort((a, b) => a.localeCompare(b))
96
+ },
97
+ requiredChecks,
98
+ issues: sortedIssues
99
+ };
100
+ }
101
+ function toPreflightMarkdown(payload) {
102
+ const lines = [];
103
+ lines.push("# gutterpress preflight");
104
+ lines.push("");
105
+ lines.push(`- Status: **${payload.status}**`);
106
+ lines.push(`- Profile: ${payload.profile ?? "none"}`);
107
+ lines.push(`- PDF: ${payload.pdf}`);
108
+ lines.push(`- Summary: ${payload.summary.errors} error(s), ${payload.summary.warnings} warning(s), ${payload.summary.passed}/${payload.summary.total} checks passed`);
109
+ if (payload.tools.missing.length > 0) {
110
+ lines.push("");
111
+ lines.push("## Missing Tools");
112
+ for (const tool of payload.tools.missing) {
113
+ lines.push(`- ${tool}`);
114
+ }
115
+ }
116
+ if (payload.requiredChecks.length > 0) {
117
+ lines.push("");
118
+ lines.push("## Required Checks");
119
+ for (const check of payload.requiredChecks) {
120
+ lines.push(`- ${check.id}: ${check.status}`);
121
+ }
122
+ }
123
+ if (payload.issues.length > 0) {
124
+ lines.push("");
125
+ lines.push("## Issues");
126
+ for (const issue of payload.issues) {
127
+ lines.push(`- [${issue.severity}] ${issue.checkId}: ${issue.message}`);
128
+ }
129
+ }
130
+ return `${lines.join(`
131
+ `)}
132
+ `;
133
+ }
134
+ function defaultReportName(pdfPath) {
135
+ const ext = extname(pdfPath);
136
+ const base = ext ? basename(pdfPath, ext) : basename(pdfPath);
137
+ return `${base}.preflight`;
138
+ }
139
+ var preflightArgs = {
140
+ dir: {
141
+ type: "positional",
142
+ description: "Project directory (default: cwd). Sets the pre-build source directory unless --input is also given.",
143
+ required: false
144
+ },
145
+ pdf: {
146
+ type: "string",
147
+ description: "Path to the PDF file to preflight",
148
+ required: true
149
+ },
150
+ input: {
151
+ type: "string",
152
+ description: "Optional source directory for pre-build checks (overrides the positional directory)"
153
+ },
154
+ manifest: {
155
+ type: "string",
156
+ description: "Path to manifest.yaml"
157
+ },
158
+ profile: {
159
+ type: "string",
160
+ description: "Validation profile lock (currently: dtrpg)"
161
+ },
162
+ "report-dir": {
163
+ type: "string",
164
+ description: "Output directory for preflight reports"
165
+ },
166
+ name: {
167
+ type: "string",
168
+ description: "Base filename for report outputs"
169
+ }
170
+ };
171
+ var preflight_default = defineCommand({
172
+ meta: {
173
+ name: "preflight",
174
+ description: "Run deterministic print preflight for a built PDF"
175
+ },
176
+ args: preflightArgs,
177
+ async run({ args, rawArgs }) {
178
+ const positionalDir = typeof args.dir === "string" ? args.dir : undefined;
179
+ const inputFlag = typeof args.input === "string" ? args.input : undefined;
180
+ const input = inputFlag ?? positionalDir;
181
+ let execution;
182
+ try {
183
+ rejectUnknownFlags(rawArgs, preflightArgs, "preflight");
184
+ rejectExtraPositionals(args._, 1, "preflight");
185
+ execution = await executeValidation({
186
+ manifest: typeof args.manifest === "string" ? args.manifest : undefined,
187
+ pdf: typeof args.pdf === "string" ? args.pdf : undefined,
188
+ input,
189
+ phase: "post-build",
190
+ profile: typeof args.profile === "string" ? args.profile : undefined
191
+ });
192
+ } catch (error) {
193
+ log.error(error instanceof Error ? error.message : String(error));
194
+ process.exit(error instanceof UsageError ? error.exitCode : EXIT_CODES.USAGE);
195
+ }
196
+ if (!execution)
197
+ return;
198
+ reportMissingTools(execution.tools);
199
+ const payload = buildPreflightPayload(execution);
200
+ const pdfPath = execution.context.pdfPath;
201
+ const reportDir = resolve(typeof args["report-dir"] === "string" ? args["report-dir"] : dirname(pdfPath));
202
+ const baseName = typeof args.name === "string" && args.name.trim().length > 0 ? args.name.trim() : defaultReportName(pdfPath);
203
+ const jsonPath = join(reportDir, `${baseName}.json`);
204
+ const markdownPath = join(reportDir, `${baseName}.md`);
205
+ await mkdir(reportDir, { recursive: true });
206
+ await writeFile(jsonPath, `${JSON.stringify(payload, null, 2)}
207
+ `, "utf8");
208
+ await writeFile(markdownPath, toPreflightMarkdown(payload), "utf8");
209
+ log.info(`Preflight status: ${payload.status}`);
210
+ log.info(`JSON report: ${jsonPath}`);
211
+ log.info(`Markdown report: ${markdownPath}`);
212
+ if (payload.status === "NO-GO") {
213
+ process.exit(EXIT_CODES.FINDINGS);
214
+ }
215
+ }
216
+ });
217
+ export {
218
+ toPreflightMarkdown,
219
+ preflightArgs,
220
+ preflight_default as default,
221
+ buildPreflightPayload
222
+ };
@@ -0,0 +1,174 @@
1
+ /**
2
+ * File watcher setup and management for preview server
3
+ *
4
+ * Handles watching input files and triggering rebuilds on changes.
5
+ * Uses the simplified markdown pipeline (renderChapters from lib/markdown).
6
+ */
7
+ import { type FSWatcher } from 'chokidar';
8
+ import type { ServerState } from './server-context';
9
+ import type { ResolvedPluginConfig } from '../schema/manifest.types';
10
+ /**
11
+ * Whether the double-buffered preview shell is active. The historical env name
12
+ * is retained because users may already set it; chapter splicing is gone and
13
+ * every source change now performs a full-document pagination.
14
+ */
15
+ export declare function incrementalPreviewEnabled(): boolean;
16
+ /**
17
+ * Shared preview render path. renderChapters() does all Markdown, CSS, and
18
+ * Paged.js-slot work.
19
+ *
20
+ * Named `renderPreviewBook` (ARCH finding #53) to distinguish it from
21
+ * build-runner.ts's `renderBook` — same name, different module, and a
22
+ * genuinely different contract: this one is degrade-and-report (a plugin the
23
+ * author enabled but hasn't installed yet is skipped with a loud warning so
24
+ * the rest of the live preview still renders); build-runner.ts's is
25
+ * fail-fast, because a final artifact must never silently drop a plugin.
26
+ * Both preambles now share {@link loadPluginsWithCss}.
27
+ */
28
+ export declare function renderPreviewBook(inputPath: string, config: {
29
+ title?: string;
30
+ styles?: string[];
31
+ plugins?: ResolvedPluginConfig[];
32
+ }, opts: {
33
+ files: string[] | null;
34
+ wrapChapters: boolean;
35
+ }): Promise<string>;
36
+ /**
37
+ * Rewrite rendered book HTML for the live preview. Injects, in order:
38
+ * 1. pagedjs-interface.js — defines window.previewAPI for in-iframe controls
39
+ * 2. pagedjs-bridge.js — postMessage bridge for cross-origin toolbar (desktop)
40
+ * 3. BREAK_INSIDE_HANDLER — polyfill for break-inside: avoid
41
+ * 4. Paged.js polyfill itself, served directly from the process-wide
42
+ * embedded-assets dir by the HTTP server (see http-server.ts route
43
+ * for /vendor/*). We no longer copy it into the per-project tempDir
44
+ * because:
45
+ * - The polyfill is 904 KB and copying it per open is wasted IO
46
+ * (one of the worst-case Defender scan targets on Windows).
47
+ * - The per-process extracted copy is identical across opens so
48
+ * serving it from a stable disk path lets the OS file-cache
49
+ * and Defender hash-cache stay warm across sessions.
50
+ *
51
+ * PAGINATION FIDELITY: this injects NO page-break rule. It used to add
52
+ * `<style>.gutterpress-chapter{break-before:page}</style>` whenever the incremental
53
+ * preview was on (i.e. by default), which started EVERY source file on a new
54
+ * page in the live view. `gutterpress build` emits no such rule — assembleBookHtml
55
+ * in lib/markdown/assemble.ts concatenates source files flat into <body> and
56
+ * breaks only where project CSS or a markdown-it-paged marker says to — so any
57
+ * project that splits one chapter across several source files previewed with
58
+ * different page boundaries than the PDF it built. Preview and build now take
59
+ * their breaks from exactly the same rules.
60
+ *
61
+ * Chapter identity is attached to existing source-mapped blocks. No wrapper or
62
+ * preview-only CSS is emitted, so selectors see exactly the same document tree
63
+ * as the build.
64
+ */
65
+ export declare function injectPreviewScripts(html: string): string;
66
+ /**
67
+ * Generate HTML from markdown and write book.html to the temp directory.
68
+ * The desktop's iframe loads `book.html` via a relative URL — same name in
69
+ * dev and in published static-site builds.
70
+ *
71
+ * Empty `inputPath` writes a static placeholder — the desktop app (packages/desktop)
72
+ * supplies a real path via its own folder picker.
73
+ */
74
+ export declare function generateAndWriteHtml(inputPath: string, tempDir: string, config: {
75
+ title?: string;
76
+ styles?: string[];
77
+ source?: {
78
+ files?: string[] | null;
79
+ };
80
+ plugins?: ResolvedPluginConfig[];
81
+ }): Promise<void>;
82
+ /**
83
+ * Chokidar 5's `ignored` matcher (regex or function) is tested against the
84
+ * fully-qualified, forward-slash-normalized ABSOLUTE path — never a path
85
+ * relative to whatever root was passed to `watch()` (see chokidar's internal
86
+ * `matchPatterns`/`normalizePath`, which run before any matcher, function or
87
+ * regex, ever sees the string). The previous `ignored: /(^|[\/\\])\../`
88
+ * therefore matched a dot segment ANYWHERE in that absolute path — including
89
+ * every ANCESTOR directory of the project, not just the project's own dot
90
+ * files/dirs. A project rooted under a dot-prefixed parent (e.g.
91
+ * `~/.local/share/gutterpress/books/mybook`, or any `~/.config/...` tree) had
92
+ * every single path rejected by this rule, which silently disabled the
93
+ * watcher entirely — no error, just a preview that never picked up an edit.
94
+ *
95
+ * The fix: strip the (normalized) watch root off the front of the path
96
+ * first, and apply the dotfile rule only to what's left — i.e. to the
97
+ * PROJECT's own files/dirs, regardless of which ancestor directories the
98
+ * project itself happens to live under.
99
+ */
100
+ export declare function isDotPathUnderRoot(candidatePath: string, root: string): boolean;
101
+ /**
102
+ * Whether the watcher should ignore a path.
103
+ *
104
+ * IN-PROJECT paths get the dotfile rule ({@link isDotPathUnderRoot}) — a
105
+ * project's own `.git/`, `.DS_Store`, editor swap files, and so on are noise.
106
+ *
107
+ * A path OUTSIDE the project is only ever seen because it is a DECLARED
108
+ * external dependency the manifest named explicitly (see
109
+ * {@link externalWatchTargets}); it is never ignored. Running the dotfile rule
110
+ * on it would test every ANCESTOR segment of its absolute path — so a shared
111
+ * foundation checked out under, say, `~/.local/share/books/shared/` would be
112
+ * silently dropped, which is the exact failure mode the in-project rule was
113
+ * rewritten to avoid.
114
+ */
115
+ export declare function isIgnoredWatchPath(candidatePath: string, projectRoot: string): boolean;
116
+ /**
117
+ * External watcher roots may be broader than one dependency when a declared
118
+ * file or its parent directory does not exist yet. Traverse only paths that are
119
+ * either a desired file or one of its ancestors; this keeps a nearest-existing
120
+ * ancestor watch from scanning unrelated sibling trees such as `.git/`.
121
+ */
122
+ export declare function isExternalWatchCandidate(candidatePath: string, targets: Iterable<string>): boolean;
123
+ /** Directories chokidar must subscribe to for exact external file targets. */
124
+ export declare function externalWatchRoots(targets: Iterable<string>): Promise<string[]>;
125
+ /**
126
+ * Everything the book reads from OUTSIDE its own folder — absolute paths,
127
+ * deduped. The project watch root covers in-book files; this is what it can't
128
+ * see.
129
+ *
130
+ * A multi-book repository keeps its shared foundation next to the books
131
+ * (`shared/styles/publisher-components.css`, `shared/plugins/components.js`)
132
+ * and each book's manifest points at it directly: a `styles:` entry is a path
133
+ * to READ (asset-inline.ts inlines it; nothing is staged or copied) and an
134
+ * authored plugin `path:` resolves against the manifest directory. Both may sit
135
+ * above the book root.
136
+ *
137
+ * The stylesheet side is the DEPENDENCY CLOSURE, not just the declared entry —
138
+ * `collectStyleDependencies` follows each active stylesheet's `@import` chain
139
+ * and every local `url()` it references. A shared theme's
140
+ * `url("../../fonts/Publisher.woff2")` is a file a design tool can replace
141
+ * without touching one line of CSS; watching only `theme.css` would leave the
142
+ * preview stale after that swap with nothing downstream to correct it. The
143
+ * closure is computed from ALL active stylesheets, including in-book ones,
144
+ * because a local stylesheet can reference a shared font just as easily — only
145
+ * the results that land outside the book are added here.
146
+ *
147
+ * Watching is per-FILE, never per-directory, so the set stays exact and cannot
148
+ * accidentally pull a large sibling tree into the watcher.
149
+ */
150
+ export declare function externalWatchTargets(projectDir: string, config: {
151
+ styles?: string[];
152
+ plugins?: ResolvedPluginConfig[];
153
+ }): Promise<string[]>;
154
+ /**
155
+ * Create and configure a file watcher for the project's input directory plus
156
+ * the book's declared external dependencies (see {@link externalWatchTargets}).
157
+ *
158
+ * Nothing is mirrored anywhere: the old external-asset-root watching (a sibling
159
+ * `../_shared` directory copied under its own name into the temp dir) went away
160
+ * with `copyAssets` and the manifest's `source.assets` field it depended on.
161
+ * The project is served straight from disk and stylesheets are inlined at
162
+ * render time, so an external dependency needs watching, not staging.
163
+ */
164
+ export declare function createFileWatcher(state: ServerState): FSWatcher;
165
+ /**
166
+ * Start file watching if not disabled via options. No-input mode skips the
167
+ * watcher entirely (nothing to watch yet) — restartPreview wires up a
168
+ * watcher once the user picks a directory through the desktop.
169
+ */
170
+ export declare function startFileWatcher(state: ServerState): void;
171
+ /**
172
+ * Stop the file watcher and clean up resources
173
+ */
174
+ export declare function stopFileWatcher(state: ServerState): Promise<void>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Node.js-compatible preview HTTP server with WebSocket-based live reload.
3
+ *
4
+ * Uses node:http + the `ws` package in place of Bun.serve so this module
5
+ * runs under both Bun (dev / compiled binary) and Node.js (Electron in-process).
6
+ *
7
+ * This module deliberately avoids any bundler runtime (vite/rollup/esbuild)
8
+ * — see ADR `docs/adr/0001-no-bundlers-at-runtime.md`.
9
+ */
10
+ import type { ServerState } from './server-context.ts';
11
+ /**
12
+ * Public handle for the running preview server.
13
+ */
14
+ export interface PreviewServer {
15
+ /** Port the server is listening on. */
16
+ port: number;
17
+ /** Stop the server and close all connections. */
18
+ close(): Promise<void>;
19
+ /**
20
+ * Broadcast a `{ type: "full-reload" }` message to every connected HMR
21
+ * client. Safe to call after `close()` (no-op).
22
+ */
23
+ broadcastReload(): void;
24
+ }
25
+ export declare function isPortAvailable(port: number, host?: string): Promise<boolean>;
26
+ /**
27
+ * Find the next available port starting from `startPort`.
28
+ *
29
+ * @throws {UsageError} If the start port itself is invalid.
30
+ * @throws {BuildError} If valid ports are exhausted or the host cannot bind.
31
+ */
32
+ export declare function findAvailablePort(startPort: number, host?: string): Promise<number>;
33
+ /**
34
+ * Create and start a preview HTTP+WebSocket server.
35
+ *
36
+ * `book.html` — the one file gutterpress generates — is served from
37
+ * `state.tempDir`. Everything else a served page can ask for (an `<img src>`
38
+ * at its authored project-relative path, or any other project file) is
39
+ * served DIRECTLY from `state.currentInputPath`, the real project directory,
40
+ * not a copy of it — see lifecycle.ts's `initializePreviewDirectories` for
41
+ * why. This is what makes preview asset resolution identical to the build's
42
+ * BY CONSTRUCTION: both resolve a reference against the same real project
43
+ * tree, so a path that works in one works in the other.
44
+ */
45
+ export declare function createPreviewServer(state: ServerState, port: number): Promise<PreviewServer>;