gutterpress 0.9.0-alpha.2 → 0.10.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +18 -4
  2. package/dist/api/index.d.ts +11 -5
  3. package/dist/api/index.js +20 -15
  4. package/dist/assets/preview/scripts/preview-bridge.d.ts +1 -0
  5. package/dist/assets/preview/scripts/preview-interface.d.ts +1 -0
  6. package/dist/{audit-nhn2pjz3.js → audit-xxegv0t0.js} +10 -7
  7. package/dist/{build-san7fv2z.js → build-w0vb6mgq.js} +14 -8
  8. package/dist/checks/source/index.d.ts +1 -0
  9. package/dist/checks/source/layout-markers.d.ts +3 -0
  10. package/dist/checks/source/local-ref-parser.d.ts +28 -0
  11. package/dist/cli-46ycxe6r.js +18 -0
  12. package/dist/{cli-k4bd06sd.js → cli-5czby0dd.js} +4986 -5889
  13. package/dist/cli-c41yr7he.js +241 -0
  14. package/dist/{cli-e5zhb0xs.js → cli-f4rcbt2t.js} +17 -24
  15. package/dist/{cli-najycadg.js → cli-gk3wsdpb.js} +69 -253
  16. package/dist/{index-yzrh708h.js → cli-k1065rkg.js} +2 -5
  17. package/dist/{cli-506tg37g.js → cli-revgt4pr.js} +2 -1
  18. package/dist/{cli-wchtvxvw.js → cli-v5mp7a6q.js} +14 -1
  19. package/dist/cli-yp3p0sf2.js +2343 -0
  20. package/dist/cli.js +20 -18
  21. package/dist/{doctor-hrk0kxxz.js → doctor-ctgn1dnt.js} +4 -2
  22. package/dist/engine/compiler/build.d.ts +139 -0
  23. package/dist/engine/compiler/postprocess.d.ts +23 -0
  24. package/dist/engine/compiler/tier2.d.ts +78 -0
  25. package/dist/engine/shared/cdp.d.ts +104 -0
  26. package/dist/engine/shared/content-value.d.ts +64 -0
  27. package/dist/engine/shared/gcpm-extract.d.ts +119 -0
  28. package/dist/engine/shared/margin-box-support.d.ts +12 -0
  29. package/dist/engine/shared/pdf-inspect.d.ts +24 -0
  30. package/dist/engine/shared/synthesis.d.ts +155 -0
  31. package/dist/engine-eng4xh2q.js +41 -0
  32. package/dist/engine-q5skhgms.js +40 -0
  33. package/dist/gutterpress-agent-1ctgfz92.js +576 -0
  34. package/dist/gutterpress-viewer-cem7dmr5.js +2349 -0
  35. package/dist/index-a4kr77td.js +1907 -0
  36. package/dist/{cli-yzrh708h.js → index-mdefp0y5.js} +1 -1
  37. package/dist/index-mvehxh0e.js +708 -0
  38. package/dist/{index-bynn850m.js → index-s416qpdv.js} +3049 -4015
  39. package/dist/{index-wchtvxvw.js → index-v5mp7a6q.js} +14 -1
  40. package/dist/index.d.ts +3 -1
  41. package/dist/index.js +24 -17
  42. package/dist/lib/asset-inline.d.ts +36 -0
  43. package/dist/lib/browser-pool.d.ts +18 -0
  44. package/dist/lib/build-error.d.ts +1 -1
  45. package/dist/lib/build-preflight.d.ts +35 -2
  46. package/dist/lib/build-runner.d.ts +45 -17
  47. package/dist/lib/build-staging.d.ts +9 -55
  48. package/dist/lib/cli-args.d.ts +8 -0
  49. package/dist/lib/desktop.d.ts +2 -2
  50. package/dist/lib/embedded-assets.d.ts +1 -1
  51. package/dist/lib/engine.d.ts +33 -0
  52. package/dist/lib/ghostscript.d.ts +46 -1
  53. package/dist/lib/markdown/assemble.d.ts +18 -10
  54. package/dist/lib/markdown/gp-pin-scope.d.ts +1 -0
  55. package/dist/lib/markdown/gutterpress-css.d.ts +125 -0
  56. package/dist/lib/markdown/images.d.ts +26 -0
  57. package/dist/lib/markdown/index.d.ts +4 -2
  58. package/dist/lib/markdown/inline-source.d.ts +10 -0
  59. package/dist/lib/markdown/markers.d.ts +32 -0
  60. package/dist/lib/markdown/renderer.d.ts +8 -3
  61. package/dist/lib/markdown/source-range.d.ts +61 -0
  62. package/dist/lib/missing-asset-placeholder.d.ts +52 -0
  63. package/dist/lib/presets.d.ts +1 -1
  64. package/dist/lib/printsafe.d.ts +2 -3
  65. package/dist/lib/remote-auth/converge-merge.d.ts +48 -0
  66. package/dist/lib/remote-auth/image-clash.d.ts +17 -0
  67. package/dist/lib/remote-auth/recovery/classify.d.ts +55 -67
  68. package/dist/lib/remote-auth/recovery/inspect.d.ts +14 -13
  69. package/dist/lib/remote-auth/recovery/locks.d.ts +20 -0
  70. package/dist/lib/remote-auth/recovery/repair.d.ts +29 -0
  71. package/dist/lib/remote-auth/recovery/types.d.ts +8 -204
  72. package/dist/lib/remote-auth/sync-messages.d.ts +2 -3
  73. package/dist/lib/remote-auth/sync-types.d.ts +35 -63
  74. package/dist/lib/remote-auth/sync.d.ts +11 -18
  75. package/dist/lib/remote-auth/transport.d.ts +14 -7
  76. package/dist/lib/theme-import.d.ts +4 -5
  77. package/dist/{lint-96j9hrj4.js → lint-gsh6qwrq.js} +10 -7
  78. package/dist/{manifest.schema-z61rzw44.json → manifest.schema-zxgxnbg7.json} +21 -0
  79. package/dist/{new-8p38wavc.js → new-nkhycqy4.js} +12 -8
  80. package/dist/{plugin-ees6nhkc.js → plugin-129wcs93.js} +10 -7
  81. package/dist/{preflight-1q6c2edh.js → preflight-41hcejg9.js} +10 -7
  82. package/dist/preview/file-watcher.d.ts +13 -17
  83. package/dist/preview/lifecycle.d.ts +1 -1
  84. package/dist/{pagedjs-bridge-vn4hk9fx.js → preview-bridge-fz7vpk8m.js} +8 -0
  85. package/dist/preview-interface-fnqb2y4v.js +1015 -0
  86. package/dist/{preview-y5a2zen1.js → preview-pftsvr9z.js} +16 -9
  87. package/dist/preview-shell-c5mfa3q0.js +346 -0
  88. package/dist/{project-source-p0gn1wd5.js → project-source-ekcyp63q.js} +1 -1
  89. package/dist/{publish-rm9yb3wh.js → publish-dnjvb4jg.js} +10 -7
  90. package/dist/render.d.ts +3 -4
  91. package/dist/render.js +728 -64
  92. package/dist/{repair-zgq7q2g6.js → repair-a3enjttn.js} +45 -79
  93. package/dist/schema/manifest.types.d.ts +38 -0
  94. package/dist/{source-provider-c1rjm2c0.js → source-provider-3tcj6qg2.js} +2 -2
  95. package/dist/source-provider-vanafrt9.js +40 -0
  96. package/dist/{theme-zz2ktzqs.css → theme-h5recz6c.css} +8 -7
  97. package/dist/{theme-570zmh2t.css → theme-j2bagrfx.css} +8 -7
  98. package/dist/{theme-nya4nqh6.css → theme-nn6d53zy.css} +8 -7
  99. package/dist/types.d.ts +7 -0
  100. package/dist/{validate-nr0xa6sa.js → validate-ph1xffqy.js} +10 -7
  101. package/package.json +6 -6
  102. package/dist/cli-yja077f6.js +0 -92
  103. package/dist/git-http-yrb4ag6z.js +0 -17
  104. package/dist/index-yja077f6.js +0 -92
  105. package/dist/lib/markdown/markdown-it-paged.d.ts +0 -30
  106. package/dist/lib/pagedjs-marker.d.ts +0 -42
  107. package/dist/lib/pagedjs.d.ts +0 -26
  108. package/dist/lib/pagination.d.ts +0 -149
  109. package/dist/lib/remote-auth/conflict-resolution.d.ts +0 -29
  110. package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +0 -103
  111. package/dist/lib/remote-auth/recovery/backup.d.ts +0 -113
  112. package/dist/lib/remote-auth/recovery/context.d.ts +0 -47
  113. package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -28
  114. package/dist/lib/remote-auth/recovery/failsafe.d.ts +0 -33
  115. package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +0 -28
  116. package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +0 -59
  117. package/dist/lib/remote-auth/recovery/policy.d.ts +0 -47
  118. package/dist/lib/remote-auth/recovery/recover-auth.d.ts +0 -42
  119. package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +0 -37
  120. package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +0 -40
  121. package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +0 -70
  122. package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +0 -23
  123. package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +0 -28
  124. package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +0 -39
  125. package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +0 -34
  126. package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +0 -37
  127. package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +0 -56
  128. package/dist/lib/remote-auth/recovery/recover-network.d.ts +0 -34
  129. package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +0 -27
  130. package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -68
  131. package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +0 -44
  132. package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +0 -35
  133. package/dist/lib/remote-auth/resolution-plan.d.ts +0 -64
  134. package/dist/paged.polyfill-n95pbxfn.js +0 -33288
  135. package/dist/pagedjs-interface-qxvzgwd7.js +0 -557
  136. package/dist/preview-shell-6dqexx1m.js +0 -581
  137. /package/dist/assets/{preview/scripts/pagedjs-bridge.d.ts → engine/gutterpress-agent.d.ts} +0 -0
  138. /package/dist/assets/{preview/scripts/pagedjs-interface.d.ts → engine/gutterpress-viewer.d.ts} +0 -0
@@ -29,6 +29,19 @@ async function gitEntryKind(dir) {
29
29
  return "none";
30
30
  }
31
31
  }
32
+ async function looksLikeRepo(dir) {
33
+ const kind = await gitEntryKind(dir);
34
+ if (kind === "none")
35
+ return false;
36
+ if (kind === "file")
37
+ return true;
38
+ try {
39
+ await stat(path.join(dir, ".git", "HEAD"));
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
32
45
  async function findEnclosingRepoDir(folderPath) {
33
46
  const home = path.resolve(os.homedir());
34
47
  let dir = path.resolve(folderPath);
@@ -39,7 +52,7 @@ async function findEnclosingRepoDir(folderPath) {
39
52
  dir = parent;
40
53
  if (dir === home)
41
54
  return;
42
- if (await gitEntryKind(dir) !== "none")
55
+ if (await looksLikeRepo(dir))
43
56
  return dir;
44
57
  }
45
58
  return;
package/dist/index.d.ts CHANGED
@@ -27,6 +27,8 @@ export type { PlatformAdapter, FileStat, FileWriteResult } from "./platform.ts";
27
27
  export { inspectImage } from "./lib/image-inspect.ts";
28
28
  export type { ImageInfo, ColorSpace } from "./lib/image-inspect.ts";
29
29
  export { checkCss } from "./lib/printsafe.ts";
30
+ export { BUILD_DIAGNOSTIC_CODES } from "./engine/compiler/build.ts";
31
+ export type { BuildDiagnostic, BuildDiagnosticCode } from "./engine/compiler/build.ts";
30
32
  export type { PrintSafeWarning } from "./lib/printsafe.ts";
31
- export { ruleRemoteUrls, ruleRiskyProps, rulePagedjsCrashSelectors, ruleSyntax, } from "./lib/printsafe.ts";
33
+ export { ruleRemoteUrls, ruleRiskyProps, ruleSyntax, } from "./lib/printsafe.ts";
32
34
  export type { GutterpressPlugin, GutterpressPluginExport, GutterpressPluginMetadata, } from "./lib/markdown/plugins.ts";
package/dist/index.js CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  AUTO_SYNC_MIN_MINUTES,
9
9
  BUILT_IN_TEMPLATE_IDS,
10
10
  BUILT_IN_THEME_IDS,
11
- BuildError,
12
11
  FileTokenStore,
13
12
  GITHUB_HOST,
14
13
  GenericTokenAuthProvider,
@@ -21,6 +20,7 @@ import {
21
20
  RECOMMENDED_PLUGINS,
22
21
  RepoNeedsRecoveryError,
23
22
  SNIPPETS_DIR,
23
+ STALE_LOCK_MIN_AGE_MS,
24
24
  SYNC_SNAPSHOT_MESSAGE,
25
25
  TARGETS,
26
26
  TARGET_IDS,
@@ -33,11 +33,9 @@ import {
33
33
  autoSnapshotDelayMs,
34
34
  autoSyncDelayMs,
35
35
  buildPreflightDiagnostics,
36
- buildRecoveryContext,
37
36
  checkCss,
38
37
  checkToolAvailability,
39
38
  classifyFromHealth,
40
- classifyGitError,
41
39
  cloneRepository,
42
40
  connectGenericHost,
43
41
  connectPublishProvider,
@@ -66,9 +64,11 @@ import {
66
64
  inspectRepo,
67
65
  isAppHeartbeatFresh,
68
66
  isGitInternalPath,
67
+ isLikelyRepoCorruption,
69
68
  isRepoNeedsRecoveryError,
70
69
  isSshRemoteUrl,
71
70
  isUnbornRepo,
71
+ keepImageVersion,
72
72
  knownForgeTokenUrl,
73
73
  listBuiltInTemplates,
74
74
  listBuiltInThemes,
@@ -87,7 +87,6 @@ import {
87
87
  log,
88
88
  migrateUrlCredential,
89
89
  normalizeForgeHost,
90
- onlineCopyPath,
91
90
  openPath,
92
91
  parseRemoteOrigin,
93
92
  preflightStructuralReason,
@@ -99,25 +98,24 @@ import {
99
98
  pullChanges,
100
99
  pushChanges,
101
100
  readAppHeartbeat,
101
+ readImageVersion,
102
102
  readManifestFields,
103
103
  readProjectProvenance,
104
104
  readPublishSettings,
105
105
  readSnippet,
106
106
  readThemeCss,
107
- recover,
108
107
  redactCredential,
109
108
  removeAppHeartbeat,
110
109
  removeProjectTheme,
110
+ repairRepo,
111
111
  reportMissingTools,
112
112
  resolveActiveStyles,
113
113
  resolveBuiltInTheme,
114
114
  resolveCheckSelectors,
115
115
  resolveConfig,
116
- resolveConflicts,
117
116
  resolveGitHubClientId,
118
117
  resolvePublishRequest,
119
118
  revertTheme,
120
- rulePagedjsCrashSelectors,
121
119
  ruleRemoteUrls,
122
120
  ruleRiskyProps,
123
121
  ruleSyntax,
@@ -142,7 +140,15 @@ import {
142
140
  validateProjectPlugins,
143
141
  verifyRepoReadable,
144
142
  writeAppHeartbeat
145
- } from "./index-bynn850m.js";
143
+ } from "./index-s416qpdv.js";
144
+ import {
145
+ BUILD_DIAGNOSTIC_CODES
146
+ } from "./index-a4kr77td.js";
147
+ import {
148
+ BuildError,
149
+ DEFAULT_PRINT_OPTS,
150
+ readyProbeExpr
151
+ } from "./index-mvehxh0e.js";
146
152
  import {
147
153
  AUTO_SNAPSHOT_MESSAGE,
148
154
  HISTORY_PAGE_LIMIT,
@@ -152,15 +158,14 @@ import {
152
158
  resolveLogger,
153
159
  restoreVersionWithBackup,
154
160
  shortOid
155
- } from "./index-yzrh708h.js";
161
+ } from "./index-mdefp0y5.js";
156
162
  import {
157
163
  capabilitiesFor,
158
164
  detectProjectSource,
159
165
  findEnclosingRepoDir,
160
166
  repoRootForSource,
161
167
  repoSubPath
162
- } from "./index-wchtvxvw.js";
163
- import"./index-yja077f6.js";
168
+ } from "./index-v5mp7a6q.js";
164
169
  import"./index-37x76zdn.js";
165
170
  export {
166
171
  writeAppHeartbeat,
@@ -188,13 +193,11 @@ export {
188
193
  ruleSyntax,
189
194
  ruleRiskyProps,
190
195
  ruleRemoteUrls,
191
- rulePagedjsCrashSelectors,
192
196
  revertTheme,
193
197
  restoreVersionWithBackup,
194
198
  resolvePublishRequest,
195
199
  resolveLogger,
196
200
  resolveGitHubClientId,
197
- resolveConflicts,
198
201
  resolveConfig,
199
202
  resolveCheckSelectors,
200
203
  resolveBuiltInTheme,
@@ -202,15 +205,17 @@ export {
202
205
  reportMissingTools,
203
206
  repoSubPath,
204
207
  repoRootForSource,
208
+ repairRepo,
205
209
  removeProjectTheme,
206
210
  removeAppHeartbeat,
207
211
  redactCredential,
208
- recover,
212
+ readyProbeExpr,
209
213
  readThemeCss,
210
214
  readSnippet,
211
215
  readPublishSettings,
212
216
  readProjectProvenance,
213
217
  readManifestFields,
218
+ readImageVersion,
214
219
  readAppHeartbeat,
215
220
  pushChanges,
216
221
  pullChanges,
@@ -223,7 +228,6 @@ export {
223
228
  preflightStructuralReason,
224
229
  parseRemoteOrigin,
225
230
  openPath,
226
- onlineCopyPath,
227
231
  normalizeForgeHost,
228
232
  migrateUrlCredential,
229
233
  log,
@@ -242,10 +246,12 @@ export {
242
246
  listBuiltInThemes,
243
247
  listBuiltInTemplates,
244
248
  knownForgeTokenUrl,
249
+ keepImageVersion,
245
250
  isUnbornRepo,
246
251
  isSshRemoteUrl,
247
252
  isRepoNeedsRecoveryError,
248
253
  isNoChangesError,
254
+ isLikelyRepoCorruption,
249
255
  isGitInternalPath,
250
256
  isAppHeartbeatFresh,
251
257
  inspectRepo,
@@ -276,12 +282,10 @@ export {
276
282
  connectPublishProvider,
277
283
  connectGenericHost,
278
284
  cloneRepository,
279
- classifyGitError,
280
285
  classifyFromHealth,
281
286
  checkToolAvailability,
282
287
  checkCss,
283
288
  capabilitiesFor,
284
- buildRecoveryContext,
285
289
  buildPreflightDiagnostics,
286
290
  autoSyncDelayMs,
287
291
  autoSnapshotDelayMs,
@@ -294,6 +298,7 @@ export {
294
298
  TARGET_IDS,
295
299
  TARGETS,
296
300
  SYNC_SNAPSHOT_MESSAGE,
301
+ STALE_LOCK_MIN_AGE_MS,
297
302
  SNIPPETS_DIR,
298
303
  RepoNeedsRecoveryError,
299
304
  RESTORE_BACKUP_MESSAGE,
@@ -308,9 +313,11 @@ export {
308
313
  GenericTokenAuthProvider,
309
314
  GITHUB_HOST,
310
315
  FileTokenStore,
316
+ DEFAULT_PRINT_OPTS,
311
317
  BuildError,
312
318
  BUILT_IN_THEME_IDS,
313
319
  BUILT_IN_TEMPLATE_IDS,
320
+ BUILD_DIAGNOSTIC_CODES,
314
321
  AUTO_SYNC_MIN_MINUTES,
315
322
  AUTO_SYNC_MAX_MINUTES,
316
323
  AUTO_SYNC_DEFAULT_MINUTES,
@@ -31,6 +31,17 @@ export interface InlineStylesResult {
31
31
  /** Non-fatal notices (e.g. a remote `url()` left untouched). */
32
32
  warnings: string[];
33
33
  }
34
+ /**
35
+ * A rendered destination that must not be resolved against the book's
36
+ * filesystem. Shared by validation and asset planning so schemes cannot be
37
+ * ignored by one path but staged as bogus files by the other.
38
+ *
39
+ * `file:` is deliberately excluded: author-supplied file URLs must reach the
40
+ * relative/containment policy and fail loudly, never bypass it as a remote
41
+ * resource. Empty, fragment-only, and query-only references fetch no distinct
42
+ * book asset and are ignored.
43
+ */
44
+ export declare function isNonFilesystemRef(url: string): boolean;
34
45
  /**
35
46
  * Decode percent-escapes so `my%20photo.png` resolves to `my photo.png`.
36
47
  * Falls back to the raw string for a literal `%` in a filename.
@@ -89,4 +100,29 @@ export declare function collectStyleDependencies(projectDir: string, stylePaths:
89
100
  export declare function planImageCopies(projectDir: string, refs: Iterable<string>): Promise<{
90
101
  copies: AssetCopy[];
91
102
  errors: string[];
103
+ destinations: Map<string, string>;
92
104
  }>;
105
+ /**
106
+ * Inline every `--gp-shape:url("…")` style-attribute value in rendered book
107
+ * HTML as a data: URI, reading the referenced file relative to `baseDir`
108
+ * (the staged book directory, where `planImageCopies` already landed it).
109
+ *
110
+ * Why this exists: `shape-outside` READS PIXELS, so unlike `<img>` rendering
111
+ * it requires CORS-clean image data. The print path loads the staged
112
+ * `book.html` via `file://`, where every file is an opaque origin — a
113
+ * file-path shape silently degrades to the margin box (MEASURED, Chromium
114
+ * 141: identical fixture wraps over http:// and does nothing over file://),
115
+ * while the http-served preview shows the shape. That is a
116
+ * preview-vs-print divergence, the exact failure class the parity gate
117
+ * exists to prevent — and it depends on browser launch flags we do not
118
+ * control on every host (Electron's export window enforces web security).
119
+ * A data: URI is same-origin everywhere, so staging — which already inlines
120
+ * fonts as data: URIs — makes the print input origin-independent.
121
+ *
122
+ * The value matched is exactly what `images.ts` emits after markdown-it's
123
+ * attribute escaping: `--gp-shape:url(&quot;<src>&quot;)`. Remote and data:
124
+ * URLs, files that escape `baseDir`, and unreadable files are left
125
+ * untouched — the shape then falls back to the margin box, the same
126
+ * behavior as an image that fails to load.
127
+ */
128
+ export declare function inlineShapeUrls(html: string, baseDir: string): Promise<string>;
@@ -1,4 +1,7 @@
1
1
  import type { Browser } from "puppeteer-core";
2
+ /** Hard ceiling for navigation + rendering. Large books need this budget; it
3
+ * is also the puppeteer protocolTimeout for the pooled browser. */
4
+ export declare const RENDER_TIMEOUT_MS: number;
2
5
  /**
3
6
  * Start the Chromium launch in the background without awaiting it, so the cold
4
7
  * start overlaps with other build work. Safe to call repeatedly; only the first
@@ -10,6 +13,21 @@ export declare function prewarmBrowser(timeoutMs: number): void;
10
13
  * across renders in the same process.
11
14
  */
12
15
  export declare function getBrowser(timeoutMs: number): Promise<Browser>;
16
+ /**
17
+ * Get the shared browser and open a fresh page on it, tolerating a browser
18
+ * that died between the last health check and this call (e.g. a CI runner
19
+ * that OOM-kills Chromium under memory pressure, or the pooled instance's own
20
+ * devtools connection dropping): `browser.newPage()` on a disconnected
21
+ * browser rejects with a websocket/"Connection ended"-shaped error rather
22
+ * than triggering the `disconnected` listener in time to save this caller.
23
+ * One retry after dropping the stale pool entry converts that into a clean
24
+ * relaunch instead of a hard failure — the same shape of resilience
25
+ * `getBrowser`/`prewarmBrowser` already give a launch that fails outright.
26
+ */
27
+ export declare function getBrowserPage(timeoutMs: number): Promise<{
28
+ browser: Browser;
29
+ page: Awaited<ReturnType<Browser["newPage"]>>;
30
+ }>;
13
31
  /**
14
32
  * Close the shared browser. A one-shot CLI build calls this when done so the
15
33
  * process can exit; a preview/watch server calls it on shutdown.
@@ -37,7 +37,7 @@ export declare const EXIT_CODES: {
37
37
  * Deliberately dependency-free and in its own module: consumers that only need
38
38
  * the error type (e.g. `utils/file-utils.ts`, used by the preview server) must
39
39
  * NOT drag in `build-runner.ts`'s whole pipeline graph (markdown rendering,
40
- * ghostscript, paged.js, the browser pool) just to reference an Error class.
40
+ * Ghostscript, native pagination, the browser pool) just to reference an Error class.
41
41
  * `build-runner.ts` re-exports this so existing `import { BuildError } from
42
42
  * "./build-runner"` call sites keep working.
43
43
  *
@@ -1,5 +1,4 @@
1
1
  import type { BuildFormat } from "./build-runner";
2
- import type { PdfRenderer } from "./pagination";
3
2
  /**
4
3
  * Probe for every tool this build will actually spawn, BEFORE the pipeline
5
4
  * starts running for real. Fails fast with one error that lists every
@@ -16,14 +15,48 @@ import type { PdfRenderer } from "./pagination";
16
15
  * stampCreator) and needs no system tool at all.
17
16
  * qpdf is REQUIRED for pdfx + stripAnnotations (default true).
18
17
  */
18
+ /**
19
+ * Does this build render in the POOLED/external Chromium? The one rule,
20
+ * defined once: HTML builds never paginate here; an injected `engineBrowser`
21
+ * (the desktop's Electron host) replaces the pool. Every caller that gates
22
+ * preflight, prewarm, or the milestone check derives from THIS predicate.
23
+ */
24
+ export declare function rendersInPooledChromium(format: BuildFormat, opts: {
25
+ engineBrowser?: unknown;
26
+ }): boolean;
19
27
  export declare function preflightBuildTools(format: BuildFormat, opts: {
20
28
  stripAnnotations?: boolean;
21
- pdfRenderer?: PdfRenderer;
29
+ engineBrowser?: unknown;
22
30
  }, config: {
23
31
  pdfx: {
24
32
  stripAnnotations: boolean;
25
33
  };
26
34
  }): Promise<void>;
35
+ /**
36
+ * Native engine only: verify the browser-pool's Chromium meets
37
+ * `engine/shared/cdp.ts`'s `REQUIRED_MILESTONE`, with an early, actionable
38
+ * error instead of the late, cryptic one `connectChromium` throws from deep
39
+ * inside `buildNativePdf` (reached only after quality gates + the markdown
40
+ * render have already run). `preflightBuildTools`'s presence check
41
+ * (`resolveChromiumExecutable`) doesn't catch a resolved-but-too-old binary —
42
+ * this does.
43
+ *
44
+ * Deliberately NOT called from `preflightBuildTools` itself: that would
45
+ * force `runBuild` to await a full Chromium cold start (~1-2s) BEFORE
46
+ * lint/validate even start, defeating the whole point of `prewarmBrowser()`
47
+ * firing in parallel with them. Call this instead right after quality gates
48
+ * finish and before rendering — by then the prewarmed browser is usually
49
+ * already warm, and any quality-gate failure (the common case) never pays
50
+ * this cost at all. Reuses `getBrowser()` (the same cached instance
51
+ * `buildNativePdf`'s `connectChromium` will reuse), so this is one cold
52
+ * start, not two.
53
+ *
54
+ * Not reached by the desktop: it injects its own `engineBrowser` (Electron's
55
+ * bundled Chromium, 148 as of Electron 42.1.0 = `REQUIRED_MILESTONE`), which
56
+ * `runBuild` skips this check for and `buildNativePdf` milestone-checks
57
+ * directly instead. This is the pooled/external-Chromium path only.
58
+ */
59
+ export declare function verifyNativeChromiumMilestone(): Promise<void>;
27
60
  export interface Gates {
28
61
  lint: boolean;
29
62
  preValidate: boolean;
@@ -1,8 +1,11 @@
1
1
  import { resolveConfig } from "./manifest";
2
+ import { RENDER_TIMEOUT_MS } from "./browser-pool";
2
3
  import { BuildError } from "./build-error";
4
+ import type { BuildDiagnostic } from "../engine/compiler/build.ts";
5
+ import type { Browser as EngineBrowser, Session as EngineSession } from "../engine/shared/cdp.ts";
3
6
  import { type Gates } from "./build-preflight";
4
- import { type PdfRenderer, type PdfRenderInput } from "./pagination";
5
- export type { PdfRenderer, PdfRenderInput };
7
+ export { RENDER_TIMEOUT_MS };
8
+ export type { EngineBrowser, EngineSession };
6
9
  export type BuildFormat = "html" | "pdf" | "pdfx";
7
10
  export type PdfxFlavor = "x1a" | "x3";
8
11
  export { BuildError };
@@ -19,12 +22,6 @@ export interface BuildRunnerOptions {
19
22
  skipLint?: boolean;
20
23
  skipPreValidate?: boolean;
21
24
  skipPostValidate?: boolean;
22
- /**
23
- * Optional PDF renderer override. When provided, the build uses it instead of
24
- * launching Chromium via puppeteer, and the Chromium preflight is skipped.
25
- * The Electron desktop injects one backed by `webContents.printToPDF`.
26
- */
27
- pdfRenderer?: PdfRenderer;
28
25
  /**
29
26
  * Keep the pooled headless browser alive after the build returns. A one-shot
30
27
  * CLI build leaves this false so the process can exit; a long-lived
@@ -34,6 +31,24 @@ export interface BuildRunnerOptions {
34
31
  */
35
32
  keepBrowserAlive?: boolean;
36
33
  rawArgs: Record<string, unknown>;
34
+ /**
35
+ * CLI `--engine` override. Paged.js has been removed — the native engine is
36
+ * the only engine — so this is a deprecated no-op accepted for backward
37
+ * compatibility only: `"paged"` triggers a one-line warning
38
+ * (`manifest.ts`'s resolution) and the build proceeds natively regardless.
39
+ */
40
+ engine?: "paged" | "native";
41
+ /**
42
+ * Optional injected engine-Chromium factory for native builds
43
+ * (`engine.ts`'s `buildNativePdf`). When omitted (the CLI's default), the
44
+ * native engine attaches to `browser-pool.ts`'s pooled external Chromium,
45
+ * and the usual Chromium preflight / milestone check apply. When supplied
46
+ * (the desktop, over its own Electron `BrowserWindow` — see
47
+ * `packages/desktop/electron`'s engine-browser module), it is used
48
+ * instead, no external Chromium is required, and both of those checks are
49
+ * skipped.
50
+ */
51
+ engineBrowser?: () => Promise<EngineBrowser>;
37
52
  }
38
53
  export interface BuildRunnerResult {
39
54
  outDir: string;
@@ -47,6 +62,12 @@ export interface BuildRunnerResult {
47
62
  pdfPath: string | null;
48
63
  /** As {@link htmlPath}: `null` when nothing but the artifact was published. */
49
64
  fingerprintPath: string | null;
65
+ /**
66
+ * Author-facing print-quality findings from the render (native engine only
67
+ * — Paged.js has no equivalent audit). Empty for a clean build. The desktop
68
+ * maps these into the Problems panel; the CLI logs them.
69
+ */
70
+ diagnostics: BuildDiagnostic[];
50
71
  }
51
72
  export interface SplitOutPath {
52
73
  outDir?: string;
@@ -131,6 +152,13 @@ export interface BuildContext {
131
152
  workDir: string;
132
153
  /** What to do with `workDir` once the build succeeds. */
133
154
  target: PublishTarget;
155
+ /**
156
+ * Exact layout-marker findings already printed by pre-build validation.
157
+ * Render-time parsing consults this set so only true duplicates disappear;
158
+ * a disabled/failed/skipped marker check never suppresses a legitimate
159
+ * warning from the final render path.
160
+ */
161
+ prevalidatedLayoutWarningKeys: Set<string>;
134
162
  }
135
163
  /**
136
164
  * Stage 1 — load the manifest, merge CLI overrides into the resolved config,
@@ -145,7 +173,7 @@ export declare function resolveBuildContext(opts: BuildRunnerOptions): Promise<B
145
173
  * rendered book.html both output strategies then paginate. This is the shared
146
174
  * pre-format work; the per-format tails live in the strategies.
147
175
  *
148
- * ARCH finding #4: markdown-it-paged computes typed, line-numbered
176
+ * ARCH finding #4: Gutterpress's marker parser computes typed, line-numbered
149
177
  * author-mistake warnings (`env.layoutWarnings`) that every real render path
150
178
  * used to discard silently. `renderChaptersToFile`'s `onChapterWarnings`
151
179
  * threads them back out here so a final artifact never omits a marker
@@ -164,18 +192,18 @@ export declare function renderBook(ctx: BuildContext): Promise<string>;
164
192
  export declare function resolveIccProfile(icc: string, manifestDir: string, explicitIccPath: string | undefined): Promise<string>;
165
193
  /**
166
194
  * Orchestrate a build: resolve the context, mkdir the output, preflight tools
167
- * (non-html), pre-warm the browser when this build will paginate in Chromium,
195
+ * (non-html), pre-warm the browser when this build will render in Chromium,
168
196
  * run the quality gates, render the book, then hand off to the per-format output
169
- * strategy for pagination + finalize. The heavy lifting lives in the named
170
- * stages + strategies above (plus ./build-preflight, ./build-staging, and
171
- * ./pagination); this reads as the pipeline it is.
197
+ * strategy for rendering + finalize. The heavy lifting lives in the named
198
+ * stages + strategies above (plus ./build-preflight and ./build-staging);
199
+ * this reads as the pipeline it is.
172
200
  *
173
201
  * Everything from the prewarm decision onward runs inside a try/finally that
174
202
  * closes the pooled browser (unless `keepBrowserAlive` is set) — finding #50:
175
203
  * previously the close only happened on the success tail (inside
176
- * `finalizeBuild`), so a prewarmed Chromium leaked whenever a quality gate,
177
- * the render, or pagination itself threw. `closeBrowser()` is a no-op if
178
- * nothing was launched (including the injected-renderer path, which never
179
- * uses the pool), so it is safe to call unconditionally here.
204
+ * `finalizeBuild`), so a prewarmed Chromium leaked whenever a quality gate or
205
+ * the render itself threw. `closeBrowser()` is a no-op if nothing was
206
+ * launched (including the injected-`engineBrowser` path, which never uses
207
+ * the pool), so it is safe to call unconditionally here.
180
208
  */
181
209
  export declare function runBuild(opts: BuildRunnerOptions): Promise<BuildRunnerResult>;
@@ -1,61 +1,15 @@
1
1
  /**
2
- * HTML string-rewriting for the shippable artifact: turns the raw
3
- * serialized-paginated DOM that pagination produces into the static desktop HTML
4
- * (or the runtime-pagination fallback). Pure string/fs transforms — no browser.
5
- *
6
- * The former `stagePaginationInput` is GONE. It copied `book.html` plus every
7
- * asset directory into a temp dir purely so relative URLs would resolve against
8
- * some root, which cost a second full copy of every asset per build and made the
9
- * staged tree a second place assets could go missing. `book.html` is now
10
- * self-contained (CSS and fonts inlined by `lib/asset-inline.ts`), so the
11
- * pagination pass serves `outDir` directly with in-memory overlays for the
12
- * engine — see `createStaticFileServer`'s `overlays` in `./pagination.ts`.
2
+ * `--format html`: ship the self-contained `book.html` (already fully
3
+ * inlined — see `lib/asset-inline.ts`) alongside a copy of the native engine's
4
+ * viewer bundle, with one `<script src="engine/gutterpress-viewer.js">`
5
+ * injected before `</head>`. The viewer paginates the document in the
6
+ * browser on load — no headless Chromium at build time, no DOM
7
+ * serialization (see the "Not snapshotting the viewer's fragmented DOM" note
8
+ * in the migration plan).
13
9
  */
10
+ export declare function shipViewerHtml(htmlFile: string, outDir: string): Promise<void>;
14
11
  /**
15
- * Remove the Paged.js pagination ENGINE from an already-paginated, serialized
16
- * document so the browser renders the static pages as-is and never re-paginates.
17
- * Strips (a) the polyfill `<script src>` and (b) the inline break-inside handler.
18
- * Navigation toolbar scripts are NOT touched — they only scroll between pages
19
- * that already exist, which is not DOM-pagination.
20
- */
21
- export declare function stripPaginationRuntime(html: string): string;
22
- /**
23
- * Rewrite the build's ephemeral pagination origin back to document-relative
24
- * URLs.
25
- *
26
- * Paged.js absolutizes every non-`data:` CSS `url()` against the sheet's origin
27
- * (`replaceUrls`, paged.polyfill.js), so the serialized document comes back
28
- * pointing at `http://127.0.0.1:<port>/…` — a port that dies with the build.
29
- * Left alone, a shipped `book.html` references a dead origin for every
30
- * content-addressed image.
31
- *
32
- * The leading slash is stripped along with the origin ON PURPOSE: `book.html`
33
- * sits at the artifact root, so `assets/x.png` is correct and `/assets/x.png`
34
- * would break any deployment under a subpath (GitHub Pages project sites).
35
- */
36
- export declare function stripPaginationOrigin(html: string): string;
37
- /**
38
- * Inject the navigation-only toolbar scripts (page nav, zoom, view modes) into
39
- * the static document head. These read the pre-rendered `.pagedjs_page`
40
- * elements; they do not paginate.
41
- */
42
- export declare function injectNavigationScripts(html: string): string;
43
- /**
44
- * Turn a raw serialized paginated document into the shippable static desktop
45
- * `book.html`: copy the navigation toolbar scripts, strip the pagination engine
46
- * and the build's ephemeral origin, wire the nav scripts, and write the file.
47
- * Shared by the HTML format and the PDF unification path.
48
- */
49
- export declare function finalizeStaticBook(rawSerializedHtml: string, htmlFile: string, outDir: string): Promise<void>;
50
- /**
51
- * Fallback for `--format html` when no headless browser is available: ship the
52
- * Paged.js polyfill + nav scripts so the BROWSER paginates at load time (the
53
- * pre-SSG behavior). Slower at runtime and not pre-paginated, but it works with
54
- * no Chromium at build.
55
- */
56
- export declare function shipRuntimePaginatedHtml(htmlFile: string, outDir: string): Promise<void>;
57
- /**
58
- * Create a unique scratch directory under the OS temp dir. Now used ONLY for
12
+ * Create a unique scratch directory under the OS temp dir. Used only for
59
13
  * PDF/X intermediates (`raw.pdf`, Ghostscript work files) — never for staging
60
14
  * assets. Must not be resolved against `process.cwd()`: `runBuild` is exported
61
15
  * and called by the desktop host, so writing scratch dirs into the caller's
@@ -41,5 +41,13 @@ export declare function parseFormat(raw: unknown, opts: {
41
41
  }): BuildFormat;
42
42
  /** Parse `--pdfx-flavor`, which is only valid alongside `--format pdfx`. */
43
43
  export declare function parsePdfxFlavor(raw: unknown, format: BuildFormat): PdfxFlavor | undefined;
44
+ /**
45
+ * Parse `--engine`, shared by `build` and `preview`. Paged.js has been
46
+ * removed — the native engine is the only engine — so this flag is now an
47
+ * accepted-but-ignored no-op: `--engine paged` produces a one-line warning
48
+ * (`manifest.ts`'s resolution) and the build proceeds natively regardless.
49
+ * `undefined` means "not passed".
50
+ */
51
+ export declare function parseEngine(raw: unknown): "paged" | "native" | undefined;
44
52
  /** Parse `--port`, defaulting to {@link NETWORK.DEFAULT_PORT} (0 = OS-assigned). */
45
53
  export declare function resolvePort(raw: unknown): number;
@@ -5,8 +5,8 @@
5
5
  * picker + GH clone modal) into the build output so the directory was a
6
6
  * self-hostable site. That chrome was removed 2026-05-18 when the desktop
7
7
  * was extracted into packages/desktop (Electron + SvelteKit). The CLI build
8
- * now produces a "naked" book.html with Paged.js polyfill and the
9
- * pagedjs-interface/bridge scripts injected — no toolbar.
8
+ * now produces a "naked" book.html with Gutterpress's native live-viewer and
9
+ * preview-interface/bridge scripts injected — no toolbar.
10
10
  *
11
11
  * Power users who want a hosted UI launch the desktop app or write their
12
12
  * own iframe wrapper around book.html.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Embedded preview assets (favicon, manifest schema, paged.js polyfill,
2
+ * Embedded preview assets (favicon, manifest schema, native engine bundles,
3
3
  * iframe interface + cross-origin bridge).
4
4
  *
5
5
  * `with { type: "file" }` imports are the canonical embedding mechanism for
@@ -0,0 +1,33 @@
1
+ import { type BuildDiagnostic } from "../engine/compiler/build.ts";
2
+ import { type Browser as EngineBrowser } from "../engine/shared/cdp.ts";
3
+ export type { EngineBrowser };
4
+ /** The subset of `BuildOptions` that has a manifest/CLI source today (B.12). */
5
+ export interface NativePdfOptions {
6
+ title?: string;
7
+ author?: string;
8
+ signature?: number;
9
+ }
10
+ /**
11
+ * Render `htmlFile` to `outPdf` via the Gutterpress engine. No HTTP staging,
12
+ * no Paged.js polyfill — but the pooled/pre-warmed Chromium IS reused (see
13
+ * module doc comment): this module never launches or closes a browser itself.
14
+ *
15
+ * Returns the build's author-facing diagnostics so the caller can surface
16
+ * them (the desktop Problems panel, the CLI's own output). Dropping them here
17
+ * is what made the engine's print-quality audits invisible in every real
18
+ * build path — they only ever reached the engine dev CLI.
19
+ *
20
+ * `getEngineBrowser` is an optional injected factory, mirroring the
21
+ * engine-browser injection seam in `build-runner.ts`: when omitted (the CLI's
22
+ * default), this module gets `browser-pool.ts`'s pooled
23
+ * puppeteer browser and attaches `cdp.ts`'s `connectChromium()` to it, same as
24
+ * always. When supplied (the desktop, over its own Electron `BrowserWindow` —
25
+ * see `packages/desktop/electron`'s engine-browser module), that browser is
26
+ * used directly instead — no pooled/external Chromium involved at all. Either
27
+ * way this function owns closing whatever browser it ends up with: the pooled
28
+ * path because `connectChromium`'s close only drops OUR websocket (never the
29
+ * pool), and the injected path because the desktop hands over a browser built
30
+ * fresh for exactly this one build (`newPage()` -> one window per build), so
31
+ * nothing else is going to close it.
32
+ */
33
+ export declare function buildNativePdf(htmlFile: string, outPdf: string, options?: NativePdfOptions, getEngineBrowser?: () => Promise<EngineBrowser>): Promise<BuildDiagnostic[]>;