gutterpress 0.11.21 → 0.11.22-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +11 -0
  2. package/dist/api/index.d.ts +5 -1
  3. package/dist/api/index.js +68 -47
  4. package/dist/{build-0neyk5h6.js → build-84retk4k.js} +16 -10
  5. package/dist/{index-sth219v1.js → cli-2ppw6q9v.js} +26 -6
  6. package/dist/cli-3twpesca.js +103 -0
  7. package/dist/cli-5et23z83.js +2089 -0
  8. package/dist/{cli-j79451n6.js → cli-5qhjwne5.js} +5 -76
  9. package/dist/cli-80t93x2r.js +75 -0
  10. package/dist/{cli-j0drzb6f.js → cli-885pcze8.js} +885 -5481
  11. package/dist/{cli-b57adna7.js → cli-9pk65fqf.js} +9 -201
  12. package/dist/{cli-sqkccnm7.js → cli-d8xjns66.js} +1 -102
  13. package/dist/cli-h3phdstf.js +200 -0
  14. package/dist/{index-qf7y4d85.js → cli-habkc5ng.js} +12 -258
  15. package/dist/cli-hndkg7bn.js +196 -0
  16. package/dist/cli-pn3z0y9p.js +3073 -0
  17. package/dist/cli.js +15 -13
  18. package/dist/{doctor-fptq8tvn.js → doctor-fmzpg7ag.js} +7 -4
  19. package/dist/engine/shared/print-area.d.ts +77 -1
  20. package/dist/{engine-x07q927h.js → engine-5p8tn9b4.js} +4 -3
  21. package/dist/{engine-0b8bt05n.js → engine-9710cd7x.js} +3 -2
  22. package/dist/{ext-gp624t5c.js → ext-hsdnad5c.js} +21 -14
  23. package/dist/extension-restore-05pae3cp.js +16 -0
  24. package/dist/extension-restore-3r7phztr.js +20 -0
  25. package/dist/{gutterpress-viewer-w7njnkj7.js → gutterpress-viewer-w9jzx6sb.js} +56 -52
  26. package/dist/{index-t7r8g75c.js → index-2d8j0enc.js} +4 -2
  27. package/dist/{cli-sth219v1.js → index-2ppw6q9v.js} +26 -6
  28. package/dist/{index-mr7zs8mv.js → index-3wcr69ba.js} +5579 -10055
  29. package/dist/{cli-5qr3z0qa.js → index-93pqkvty.js} +63 -207
  30. package/dist/{index-mr72vt8n.js → index-apc8w9w8.js} +55 -267
  31. package/dist/index-ew7cyk7c.js +2208 -0
  32. package/dist/index-jsz0s3dy.js +213 -0
  33. package/dist/index-kx03e4m4.js +3244 -0
  34. package/dist/index-r7jcsk6h.js +200 -0
  35. package/dist/index.js +71 -50
  36. package/dist/lib/extension-manifest.d.ts +22 -1
  37. package/dist/lib/markdown/assemble.d.ts +3 -2
  38. package/dist/lib/markdown/plugins.d.ts +20 -0
  39. package/dist/lib/markdown/two-versions.d.ts +32 -0
  40. package/dist/lib/page-size.d.ts +3 -4
  41. package/dist/lib/pdf-inspect.d.ts +12 -0
  42. package/dist/lib/project-scaffold.d.ts +3 -3
  43. package/dist/lib/publish/plugin-provider.d.ts +16 -0
  44. package/dist/lib/publish/run-publish.d.ts +13 -5
  45. package/dist/lib/publish/types.d.ts +34 -2
  46. package/dist/lib/text-diff.d.ts +43 -0
  47. package/dist/lib/two-versions-markers.d.ts +26 -0
  48. package/dist/lib/version-changes.d.ts +87 -0
  49. package/dist/{manifest.schema-n292sb77.json → manifest.schema-nvawq0xv.json} +2 -2
  50. package/dist/{new-dre6aw05.js → new-fqtt022k.js} +23 -16
  51. package/dist/{pdf-inspect-zdmy7qca.js → pdf-inspect-wnw4za71.js} +1 -1
  52. package/dist/plugins-gfvsgnxb.js +24 -0
  53. package/dist/plugins-nfxr831f.js +26 -0
  54. package/dist/{preflight-1k1zkg1p.js → preflight-gr9mscjw.js} +18 -11
  55. package/dist/{preview-dtrksvxf.js → preview-52w9pdps.js} +19 -13
  56. package/dist/{publish-g1ytnrmr.js → publish-esmcyhpb.js} +40 -23
  57. package/dist/render.js +706 -0
  58. package/dist/schema/manifest.types.d.ts +8 -3
  59. package/dist/{source-provider-pzmz0br9.js → source-provider-2r74a7wp.js} +2 -1
  60. package/dist/{source-provider-qbthnmqw.js → source-provider-qbfknhyy.js} +2 -1
  61. package/dist/{validate-yrrq8639.js → validate-vt1d7h6k.js} +16 -10
  62. package/package.json +1 -1
@@ -0,0 +1,200 @@
1
+ // src/lib/plugin-vendor.ts
2
+ import { lstat, readFile, realpath } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { exports as resolveExports } from "resolve.exports";
5
+ var PLUGINS_DIR = "plugins";
6
+ var VENDORED_NPM_DIR = "npm";
7
+ var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
8
+ var EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
9
+ var WINDOWS_RESERVED = /^(con|prn|aux|nul|clock\$|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\..*)?$/i;
10
+ function isValidNpmPackageName(name) {
11
+ if (!name || name.length > 214 || name !== name.toLowerCase())
12
+ return false;
13
+ if (name.startsWith("@")) {
14
+ const parts = name.slice(1).split("/");
15
+ return parts.length === 2 && parts.every((part) => NPM_SEGMENT.test(part));
16
+ }
17
+ return !name.includes("/") && NPM_SEGMENT.test(name);
18
+ }
19
+ function parseNpmPluginSpec(input) {
20
+ const spec = input.trim();
21
+ if (!spec)
22
+ throw new Error("An npm package name is required.");
23
+ let name = spec;
24
+ let selector;
25
+ if (spec.startsWith("@")) {
26
+ const slash = spec.indexOf("/");
27
+ const versionAt = spec.lastIndexOf("@");
28
+ if (slash > 1 && versionAt > slash) {
29
+ name = spec.slice(0, versionAt);
30
+ selector = spec.slice(versionAt + 1);
31
+ }
32
+ } else {
33
+ const versionAt = spec.lastIndexOf("@");
34
+ if (versionAt > 0) {
35
+ name = spec.slice(0, versionAt);
36
+ selector = spec.slice(versionAt + 1);
37
+ }
38
+ }
39
+ if (!isValidNpmPackageName(name)) {
40
+ throw new Error(`"${name}" is not a valid npm package name. Use a name like ` + "markdown-it-highlightjs or @scope/markdown-it-plugin.");
41
+ }
42
+ if (selector !== undefined && !selector) {
43
+ throw new Error(`The package spec "${spec}" is missing a selector after @.`);
44
+ }
45
+ return selector === undefined ? { name } : { name, selector };
46
+ }
47
+ function isExactNpmVersion(version) {
48
+ return EXACT_VERSION.test(version);
49
+ }
50
+ function encodePathPart(value) {
51
+ return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
52
+ }
53
+ function vendoredNpmPluginRoot(projectDir, name, version) {
54
+ return path.join(projectDir, PLUGINS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
55
+ }
56
+ function vendoredNpmPluginPackageDir(installRoot, name) {
57
+ return path.join(installRoot, "node_modules", ...name.split("/"));
58
+ }
59
+ function toPosixPath(value) {
60
+ return value.split(path.sep).join("/");
61
+ }
62
+ function assertWindowsSafeRelativePath(relative) {
63
+ if (!relative || relative.includes("\\") || relative.startsWith("/") || /^[a-zA-Z]:/.test(relative)) {
64
+ throw new Error(`Unsafe package path: ${relative || "(empty)"}`);
65
+ }
66
+ const normalized = relative.normalize("NFKC");
67
+ const parts = normalized.split("/");
68
+ if (parts.some((part) => !part || part === "." || part === "..")) {
69
+ throw new Error(`Unsafe package path: ${relative}`);
70
+ }
71
+ for (const part of parts) {
72
+ if (part.length > 255 || /[\u0000-\u001f<>:"|?*]/.test(part) || part.endsWith(".") || part.endsWith(" ") || WINDOWS_RESERVED.test(part)) {
73
+ throw new Error(`Package path is invalid on Windows: ${relative}`);
74
+ }
75
+ }
76
+ }
77
+ function windowsPathKey(relative) {
78
+ return relative.normalize("NFKC").toLowerCase();
79
+ }
80
+ function isContained(root, candidate) {
81
+ const relative = path.relative(root, candidate);
82
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
83
+ }
84
+ function object(value) {
85
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
86
+ }
87
+ async function readPackageJson(packageDir) {
88
+ const packageJsonPath = path.join(packageDir, "package.json");
89
+ const info = await lstat(packageJsonPath);
90
+ if (!info.isFile() || info.isSymbolicLink()) {
91
+ throw new Error(`Package metadata is not a regular file: ${packageJsonPath}`);
92
+ }
93
+ const parsed = object(JSON.parse(await readFile(packageJsonPath, "utf8")));
94
+ if (!parsed)
95
+ throw new Error(`Package metadata is not an object: ${packageJsonPath}`);
96
+ return parsed;
97
+ }
98
+ function packageResolutionTargets(manifest, subpath, condition) {
99
+ if (manifest.exports !== undefined) {
100
+ try {
101
+ const targets2 = resolveExports(manifest, subpath ? `./${subpath}` : ".", { require: condition === "require" }) ?? [];
102
+ return targets2.map((target) => ({ target, exact: true }));
103
+ } catch {
104
+ return [];
105
+ }
106
+ }
107
+ if (subpath)
108
+ return [{ target: subpath, exact: false }];
109
+ const targets = [
110
+ ...condition === "import" && typeof manifest.module === "string" ? [manifest.module] : [],
111
+ ...typeof manifest.main === "string" ? [manifest.main] : [],
112
+ "index.mjs",
113
+ "index.js",
114
+ "index.cjs"
115
+ ];
116
+ return [...new Set(targets)].map((target) => ({ target, exact: false }));
117
+ }
118
+ function safePackageTarget(target) {
119
+ if (!target || target.includes("\\") || target.includes("*") || target.includes("?") || target.includes("#")) {
120
+ return null;
121
+ }
122
+ const relative = target.startsWith("./") ? target.slice(2) : target;
123
+ if (!relative || path.isAbsolute(relative))
124
+ return null;
125
+ assertWindowsSafeRelativePath(relative);
126
+ if (relative.normalize("NFKC").split("/").some((part) => part.toLowerCase() === "node_modules")) {
127
+ return null;
128
+ }
129
+ return relative;
130
+ }
131
+ async function resolveEntryCandidate(packageDir, resolution) {
132
+ const relative = safePackageTarget(resolution.target);
133
+ if (!relative)
134
+ return null;
135
+ const base = path.resolve(packageDir, ...relative.split("/"));
136
+ if (!isContained(packageDir, base))
137
+ return null;
138
+ const candidates = resolution.exact ? [base] : [
139
+ base,
140
+ `${base}.mjs`,
141
+ `${base}.js`,
142
+ `${base}.cjs`,
143
+ `${base}.json`,
144
+ path.join(base, "index.mjs"),
145
+ path.join(base, "index.js"),
146
+ path.join(base, "index.cjs"),
147
+ path.join(base, "index.json")
148
+ ];
149
+ for (const candidate of candidates) {
150
+ try {
151
+ const info = await lstat(candidate);
152
+ if (!info.isFile() || info.isSymbolicLink())
153
+ continue;
154
+ const extension = path.extname(candidate);
155
+ if (extension && ![".js", ".mjs", ".cjs", ".json"].includes(extension))
156
+ continue;
157
+ const [realRoot, realCandidate] = await Promise.all([realpath(packageDir), realpath(candidate)]);
158
+ if (!isContained(realRoot, realCandidate))
159
+ continue;
160
+ return toPosixPath(path.relative(packageDir, realCandidate));
161
+ } catch {}
162
+ }
163
+ return null;
164
+ }
165
+ async function resolvePackageEntry(packageDir, packageJson) {
166
+ const manifest = packageJson ?? await readPackageJson(packageDir);
167
+ for (const condition of ["import", "require"]) {
168
+ for (const target of packageResolutionTargets(manifest, "", condition)) {
169
+ const resolved = await resolveEntryCandidate(packageDir, target);
170
+ if (resolved)
171
+ return resolved;
172
+ }
173
+ }
174
+ throw new Error("Package has no contained JavaScript entry point.");
175
+ }
176
+ async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
177
+ const requestedProjectRoot = path.resolve(projectDir);
178
+ const requestedInstallRoot = vendoredNpmPluginRoot(requestedProjectRoot, expectedName, expectedVersion);
179
+ let installInfo;
180
+ try {
181
+ installInfo = await lstat(requestedInstallRoot);
182
+ } catch (error) {
183
+ if (error.code === "ENOENT")
184
+ return null;
185
+ throw error;
186
+ }
187
+ if (!installInfo.isDirectory() || installInfo.isSymbolicLink()) {
188
+ throw new Error("Vendor install root is not a normal directory.");
189
+ }
190
+ const [realProjectRoot, installRoot] = await Promise.all([
191
+ realpath(requestedProjectRoot),
192
+ realpath(requestedInstallRoot)
193
+ ]);
194
+ if (!isContained(realProjectRoot, installRoot)) {
195
+ throw new Error("Vendor install root resolves outside the book folder.");
196
+ }
197
+ return installRoot;
198
+ }
199
+
200
+ export { PLUGINS_DIR, VENDORED_NPM_DIR, isValidNpmPackageName, parseNpmPluginSpec, isExactNpmVersion, vendoredNpmPluginRoot, vendoredNpmPluginPackageDir, toPosixPath, assertWindowsSafeRelativePath, windowsPathKey, packageResolutionTargets, safePackageTarget, resolvePackageEntry, resolveVendoredPluginInstallRoot };
package/dist/index.js CHANGED
@@ -4,58 +4,42 @@ import {
4
4
  AUTO_SYNC_MAX_MINUTES,
5
5
  AUTO_SYNC_MIN_MINUTES,
6
6
  AUTO_SYNC_PUSH_INTERVAL_MINUTES,
7
- BUILT_IN_STYLE_SET_IDS,
8
7
  BUILT_IN_TEMPLATE_IDS,
9
- BUNDLED_EXTENSIONS,
10
- DEFAULT_NPM_REGISTRY,
11
- EXTENSIONS_DIR,
12
8
  EXTENSION_KINDS,
13
- EXTENSION_MANIFEST_FILENAME,
14
9
  FileTokenStore,
15
10
  GDRIVE_HOST,
16
11
  GITHUB_HOST,
17
12
  GOOGLE_NOT_CONFIGURED_MESSAGE,
18
13
  GitHubAuthProvider,
19
14
  GoogleAuthProvider,
20
- MANIFEST_FILENAMES,
21
15
  MAX_SEARCH_BYTES,
22
- PRESETS,
23
- PRESET_IDS,
24
16
  PublishSelectionsStore,
25
- RECOMMENDED_EXTENSIONS,
26
17
  REPAIR_SNAPSHOT_MESSAGE,
27
18
  RESERVED_PREFIX,
28
19
  SEARCH_KEYWORDS,
29
20
  SNIPPETS_DIR,
30
21
  SYNC_SNAPSHOT_MESSAGE,
31
- TARGETS,
32
- TARGET_IDS,
33
- addBuiltInStyleSet,
34
- addExtension,
22
+ VERSION_CHANGES_SHOWN,
35
23
  adoptFolder,
36
- assertExtensionContained,
37
24
  autoSnapshotDelayMs,
38
25
  autoSyncDelayMs,
39
26
  checkCss,
40
- checkExtensionUpdates,
41
27
  checkRemoteUpdates,
42
28
  checkToolAvailability,
43
29
  cloneRepository,
30
+ compareVersionFile,
44
31
  connectGenericHost,
45
32
  connectGoogleDrive,
46
33
  connectPublishProvider,
47
34
  defaultConfigDir,
48
35
  deleteSnippet,
49
- describeExtension,
36
+ describeVersions,
50
37
  diagnoseProjectRemote,
51
38
  disconnectPublishCredential,
52
- ensureProjectGitignore,
53
39
  escapeYamlScalar,
54
40
  executeAndReport,
55
41
  executeValidation,
56
- extensionCarries,
57
- extensionEntry,
58
- extensionStyleList,
42
+ exportVersionTree,
59
43
  extractUrlCredential,
60
44
  extractVariables,
61
45
  forgeKindForHost,
@@ -63,18 +47,15 @@ import {
63
47
  getCheckById,
64
48
  getChecks,
65
49
  getSystemDiagnostics,
66
- hasProjectManifest,
67
50
  importExtensionFromFile,
68
51
  importExtensionFromUrl,
69
52
  importTemplateFromFolder,
70
53
  inspectImage,
71
- isBundledExtension,
72
54
  isGitInternalPath,
73
- isPathSpecifier,
74
55
  isSshRemoteUrl,
75
56
  isValidRepositoryName,
76
57
  knownForgeTokenUrl,
77
- listBuiltInStyleSets,
58
+ listBookPublishProviders,
78
59
  listBuiltInTemplates,
79
60
  listCustomTemplates,
80
61
  listGitHubBranches,
@@ -82,49 +63,34 @@ import {
82
63
  listLocationVariants,
83
64
  listMarkerComponents,
84
65
  listMergedSnippets,
85
- listNpmVersions,
86
- listProjectExtensions,
66
+ listOnlineVersions,
87
67
  listProjectStyles,
88
68
  listPublishAccounts,
89
69
  listPublishProviders,
90
70
  listRepoBooks,
91
71
  listSnippets,
92
- loadManifest,
93
- loadManifestWithPath,
94
- log,
95
- npmRegistryUrl,
96
72
  openPath,
97
- parseExtensionSpecifier,
98
73
  parseRemoteOrigin,
99
- pathEscapesFolder,
100
74
  pkceChallengeFromVerifier,
101
75
  publishConnectionStatus,
102
76
  publishCredentialKey,
103
77
  publishProviderFor,
104
- publishTargetFor,
105
- readExtensionCss,
106
- readExtensionMeta,
107
78
  readManifestFields,
108
79
  readPageSetup,
109
80
  readPublishSettings,
110
81
  refreshRemoteCopies,
111
- removeExtension,
112
- reorderExtensions,
113
82
  repairOnlineBackup,
114
83
  reportMissingTools,
115
84
  requireGoogleClientCredentials,
116
85
  resolveActiveStyles,
117
86
  resolveCheckSelectors,
118
- resolveConfig,
119
- resolveExtension,
120
87
  resolveExtensionPrefix,
121
88
  resolveGitHubClientId,
122
89
  resolveGoogleClientId,
123
90
  resolveGoogleClientSecret,
124
91
  resolvePublishFormat,
92
+ resolvePublishProvider,
125
93
  resolvePublishRequest,
126
- restoreFailureMessage,
127
- restorePinnedExtensions,
128
94
  revokeGoogleCredential,
129
95
  ruleRemoteUrls,
130
96
  ruleRiskyProps,
@@ -132,7 +98,6 @@ import {
132
98
  runBuild,
133
99
  runChecks,
134
100
  runPublish,
135
- sameExtension,
136
101
  sanitizeCloneFolderName,
137
102
  saveProjectAsTemplate,
138
103
  saveSnippet,
@@ -141,7 +106,6 @@ import {
141
106
  scorchedEarth,
142
107
  searchNpmExtensions,
143
108
  setActiveStyles,
144
- setExtensionEnabled,
145
109
  setManifestFields,
146
110
  setPageSetup,
147
111
  setPublishProviderConfig,
@@ -151,18 +115,66 @@ import {
151
115
  startPreviewServer,
152
116
  substituteVariables,
153
117
  syncProject,
154
- testRemoteAccess,
118
+ testRemoteAccess
119
+ } from "./index-3wcr69ba.js";
120
+ import {
121
+ BUILT_IN_STYLE_SET_IDS,
122
+ BUNDLED_EXTENSIONS,
123
+ DEFAULT_NPM_REGISTRY,
124
+ EXTENSIONS_DIR,
125
+ MANIFEST_FILENAMES,
126
+ PRESETS,
127
+ PRESET_IDS,
128
+ RECOMMENDED_EXTENSIONS,
129
+ TARGETS,
130
+ TARGET_IDS,
131
+ addBuiltInStyleSet,
132
+ addExtension,
133
+ checkExtensionUpdates,
134
+ describeExtension,
135
+ ensureProjectGitignore,
136
+ hasProjectManifest,
137
+ isBundledExtension,
138
+ isPathSpecifier,
139
+ listBuiltInStyleSets,
140
+ listNpmVersions,
141
+ listProjectExtensions,
142
+ loadManifest,
143
+ loadManifestWithPath,
144
+ npmRegistryUrl,
145
+ parseExtensionSpecifier,
146
+ publishTargetFor,
147
+ readExtensionCss,
148
+ removeExtension,
149
+ reorderExtensions,
150
+ resolveConfig,
151
+ restoreFailureMessage,
152
+ restorePinnedExtensions,
153
+ sameExtension,
154
+ setExtensionEnabled,
155
155
  updateExtensions,
156
156
  validateProjectExtensions
157
- } from "./index-mr7zs8mv.js";
157
+ } from "./index-ew7cyk7c.js";
158
+ import {
159
+ EXTENSION_MANIFEST_FILENAME,
160
+ assertExtensionContained,
161
+ extensionCarries,
162
+ extensionEntry,
163
+ extensionStyleList,
164
+ log,
165
+ pathEscapesFolder,
166
+ readExtensionMeta,
167
+ resolveExtension
168
+ } from "./index-kx03e4m4.js";
158
169
  import"./index-ht1bcqdd.js";
159
170
  import {
160
171
  BUILD_DIAGNOSTIC_CODES
161
- } from "./index-t7r8g75c.js";
172
+ } from "./index-2d8j0enc.js";
173
+ import"./index-apc8w9w8.js";
162
174
  import {
163
175
  BuildError
164
- } from "./index-mr72vt8n.js";
165
- import"./index-sth219v1.js";
176
+ } from "./index-jsz0s3dy.js";
177
+ import"./index-2ppw6q9v.js";
166
178
  import {
167
179
  DEFAULT_PRINT_OPTS,
168
180
  assertMilestone,
@@ -171,7 +183,6 @@ import {
171
183
  import {
172
184
  AUTO_SNAPSHOT_MESSAGE,
173
185
  HISTORY_PAGE_LIMIT,
174
- PLUGINS_DIR,
175
186
  RESTORE_BACKUP_MESSAGE,
176
187
  SWITCH_BRANCH_SNAPSHOT_MESSAGE,
177
188
  countUnversionedChanges,
@@ -182,7 +193,10 @@ import {
182
193
  restoreVersionWithBackup,
183
194
  switchBranch,
184
195
  whenGitIdle
185
- } from "./index-qf7y4d85.js";
196
+ } from "./index-93pqkvty.js";
197
+ import {
198
+ PLUGINS_DIR
199
+ } from "./index-r7jcsk6h.js";
186
200
  import {
187
201
  capabilitiesFor,
188
202
  detectProjectSource,
@@ -227,6 +241,7 @@ export {
227
241
  restorePinnedExtensions,
228
242
  restoreFailureMessage,
229
243
  resolvePublishRequest,
244
+ resolvePublishProvider,
230
245
  resolvePublishFormat,
231
246
  resolveLogger,
232
247
  resolveGoogleClientSecret,
@@ -271,6 +286,7 @@ export {
271
286
  listPublishAccounts,
272
287
  listProjectStyles,
273
288
  listProjectExtensions,
289
+ listOnlineVersions,
274
290
  listNpmVersions,
275
291
  listMergedSnippets,
276
292
  listMarkerComponents,
@@ -281,6 +297,7 @@ export {
281
297
  listCustomTemplates,
282
298
  listBuiltInTemplates,
283
299
  listBuiltInStyleSets,
300
+ listBookPublishProviders,
284
301
  knownForgeTokenUrl,
285
302
  isValidRepositoryName,
286
303
  isSshRemoteUrl,
@@ -304,6 +321,7 @@ export {
304
321
  extensionStyleList,
305
322
  extensionEntry,
306
323
  extensionCarries,
324
+ exportVersionTree,
307
325
  executeValidation,
308
326
  executeAndReport,
309
327
  escapeYamlScalar,
@@ -311,6 +329,7 @@ export {
311
329
  disconnectPublishCredential,
312
330
  diagnoseProjectRemote,
313
331
  detectProjectSource,
332
+ describeVersions,
314
333
  describeExtension,
315
334
  deleteSnippet,
316
335
  defaultConfigDir,
@@ -318,6 +337,7 @@ export {
318
337
  connectPublishProvider,
319
338
  connectGoogleDrive,
320
339
  connectGenericHost,
340
+ compareVersionFile,
321
341
  cloneRepository,
322
342
  checkToolAvailability,
323
343
  checkRemoteUpdates,
@@ -331,6 +351,7 @@ export {
331
351
  adoptFolder,
332
352
  addExtension,
333
353
  addBuiltInStyleSet,
354
+ VERSION_CHANGES_SHOWN,
334
355
  TARGET_IDS,
335
356
  TARGETS,
336
357
  SYNC_SNAPSHOT_MESSAGE,
@@ -70,6 +70,15 @@ export interface ExtensionMetadata {
70
70
  * does the merge.
71
71
  */
72
72
  snippets?: string;
73
+ /**
74
+ * `gutterpress.publish: true` — EXPERIMENTAL (#381): the package is a
75
+ * publishing plugin. Its entry (npm `main`/`exports`, or a folder's `main`)
76
+ * exports a named `publish` function and contributes NO markdown-it plugin,
77
+ * so the markdown loader skips it without importing it. A boolean, not a
78
+ * path: it lets core recognise a publisher from package.json alone, without
79
+ * executing its code. Anything other than literal `true` reads as absent.
80
+ */
81
+ publish?: boolean;
73
82
  /**
74
83
  * npm `engines.gutterpress` — the Gutterpress versions the extension works
75
84
  * with, as a semver range (e.g. `">=0.11.20-alpha.3"`). Checked when the
@@ -98,7 +107,8 @@ export declare function extensionStyleList(meta: ExtensionMetadata): string[];
98
107
  * The markdown-it entry of a FOLDER extension: an explicit
99
108
  * `gutterpress.markdown` when the package's `main` is not the plugin,
100
109
  * otherwise npm's own `main`. `undefined` for a folder that carries no
101
- * markdown behaviour at all (a look).
110
+ * markdown behaviour at all (a look, or a publishing plugin — whose `main` is
111
+ * its publish module, not a markdown-it plugin).
102
112
  */
103
113
  export declare function extensionEntry(meta: ExtensionMetadata): string | undefined;
104
114
  /** What an extension declares, by field — the desktop's one-list filter. */
@@ -107,8 +117,19 @@ export interface ExtensionCarries {
107
117
  styles: boolean;
108
118
  snippets: boolean;
109
119
  components: boolean;
120
+ /** A publishing plugin (experimental, #381). */
121
+ publish: boolean;
110
122
  }
111
123
  export declare function extensionCarries(meta: ExtensionMetadata): ExtensionCarries;
124
+ /**
125
+ * A publishing plugin (experimental, #381) is a publisher and nothing else:
126
+ * its entry is never imported while a book renders, so a markdown entry,
127
+ * styles, components or snippets it also declared would either load (a
128
+ * folder) or be dropped without a word (a vendored copy). One rule, checked
129
+ * here, keeps both load paths and the publish path agreeing. Not a listing
130
+ * guard — `readExtensionMeta` stays tolerant.
131
+ */
132
+ export declare function assertPublishOnly(meta: ExtensionMetadata, ref: string): void;
112
133
  /**
113
134
  * True when a declared relative path escapes its own folder (absolute, or a
114
135
  * `..` segment) — the traversal shape every containment check in this
@@ -132,8 +132,9 @@ export interface AssembleBookHtmlOptions {
132
132
  * preset id and printer spec — written as `<meta name="gp-print-area">`
133
133
  * (engine/shared/print-area.ts) into EVERY assembled book, preview, build
134
134
  * and publish alike: the preview's print overlays judge the ink against it,
135
- * and both engines place pinned art on its art-safe line. Metadata only:
136
- * nothing renders from it but what reads it.
135
+ * and both engines place pinned art on its art-safe line. Metadata only,
136
+ * with one exception: its `width` x `height` is also the page size the book
137
+ * prints at when no stylesheet sets one — see {@link defaultPageSizeCss}.
137
138
  */
138
139
  printArea?: PrintAreaSource;
139
140
  plugins?: LoadedPlugin[];
@@ -1,4 +1,5 @@
1
1
  import type { ResolvedExtensionConfig } from "../../schema/manifest.types";
2
+ import type { PublishPluginFunction } from "../publish/types";
2
3
  import type { LoadedPlugin } from "./renderer";
3
4
  export type { GutterpressComponent, GutterpressComponentBlock, GutterpressComponentProblem, GutterpressMarkerDeclaration, GutterpressMarkerLabel, GutterpressMarkerTable, GutterpressPlugin, GutterpressPluginMetadata, GutterpressPluginExport, LoadedPlugin, } from "./renderer";
4
5
  export { applyPlugins, collectPluginCss, collectPluginStylePaths, collectPluginStyleGroups } from "./renderer";
@@ -7,6 +8,25 @@ import { type PluginStyleGroup } from "./renderer";
7
8
  /** Test-only: reset the path-plugin cache between test cases. */
8
9
  export declare function __resetPathPluginCacheForTests(): void;
9
10
  export declare function loadPlugin(config: ResolvedExtensionConfig, baseDir: string): Promise<LoadedPlugin>;
11
+ /** Where a publishing plugin's package sits, as discovery found it. */
12
+ export interface PublishPluginLocation {
13
+ /** The package folder: a `path:` folder, or the vendored copy's package dir. */
14
+ dir: string;
15
+ /** The vendored install root of an npm entry; `null` for a folder. */
16
+ installRoot: string | null;
17
+ /** The manifest spelling, for messages. */
18
+ ref: string;
19
+ }
20
+ /**
21
+ * EXPERIMENTAL (#381): import a publishing plugin's entry and return its
22
+ * named `publish` export. Only the publish path calls this — never a build or
23
+ * preview (see the skip in {@link loadPlugin}). The package is the one
24
+ * discovery located, so there is no second lookup and no fall-through to a
25
+ * same-named package elsewhere: a vendored copy loads through
26
+ * {@link importVendoredEntry}, a folder's `main` through the mtime cache, so
27
+ * the long-lived desktop host sees edits.
28
+ */
29
+ export declare function loadPublishFunction({ dir, installRoot, ref }: PublishPluginLocation): Promise<PublishPluginFunction>;
10
30
  /**
11
31
  * Load all plugins from the resolved configuration.
12
32
  *
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `two_versions` — renders a passage the converge merge left with TWO
3
+ * versions as two labelled boxes instead of broken markdown.
4
+ *
5
+ * After a sync that found a passage edited both here and online, the file
6
+ * holds both versions inside the marker family `two-versions-markers.ts`
7
+ * spells out. Those lines are valid markdown — `=======` is a setext
8
+ * underline, `>>>>>>>` seven nested blockquotes — so until this rule the
9
+ * preview showed the author's paragraph as a chapter-sized heading, listed
10
+ * `<<<<<<< your version` in the outline, and gave no way to read either
11
+ * version as it would print. The `source.sync.merge-markers` check still
12
+ * reports every block as an error, so a build never ships one; this rule
13
+ * only decides what the LIVE PREVIEW shows while the author chooses.
14
+ *
15
+ * The output is plain HTML + CSS: a `div.gp-two-versions` holding two
16
+ * `div.gp-version`s (`-yours`, `-online`), each labelled through
17
+ * `data-label` and each parsed as ordinary block markdown, so a heading or
18
+ * a list inside a version renders as one. No viewer or print code reads
19
+ * these classes; `TWO_VERSIONS_CSS` is the whole styling. Matching is as
20
+ * exact as the check's: the three fixed lines, at the start of a line, in
21
+ * order; anything else (a bare `=======`, a half-deleted family) is left
22
+ * to markdown as before.
23
+ */
24
+ import type MarkdownIt from "markdown-it";
25
+ /** The styling for the block above — injected by assemble.ts with MARKER_CSS. */
26
+ export declare const TWO_VERSIONS_CSS = "\n.gp-two-versions {\n margin: 1em 0;\n border: 2px dashed #b7791f;\n border-radius: 6px;\n break-inside: avoid;\n}\n.gp-two-versions > .gp-version {\n padding: 0.5em 0.9em;\n}\n.gp-two-versions > .gp-version + .gp-version {\n border-top: 2px dashed #b7791f;\n}\n.gp-two-versions > .gp-version > .gp-version-text {\n margin: 0;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n font-size: 0.85em;\n}\n.gp-two-versions > .gp-version::before {\n content: attr(data-label);\n display: block;\n margin-bottom: 0.35em;\n font-family: system-ui, sans-serif;\n font-size: 0.7em;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: #b7791f;\n}\n";
27
+ /**
28
+ * markdown-it plugin. Before `html_block` so the `<` opener is ours first;
29
+ * listed as a paragraph terminator so a block that starts right under a
30
+ * line of prose (as the merge writes it) still opens.
31
+ */
32
+ export default function gpTwoVersions(md: MarkdownIt): void;
@@ -1,5 +1,7 @@
1
1
  import type { Document } from "yaml";
2
2
  import { type PresetId } from "./presets.ts";
3
+ import { formatPageSize, isPositivePoints } from "../engine/shared/print-area.ts";
4
+ export { formatPageSize, isPositivePoints };
3
5
  /** Page bounds written to the manifest's `page:` block (points; 72pt = 1in). */
4
6
  export interface CustomPageOptions {
5
7
  /** Trim width in points (72pt = 1in). */
@@ -28,12 +30,11 @@ export interface PageSetup {
28
30
  bounds: (PageSizePoints & {
29
31
  tolerance: number;
30
32
  }) | null;
31
- /** What the book's stylesheets print at, or null when no plain `@page` sets a size. */
33
+ /** What the book's stylesheets print at, or null when no plain `@page` sets a size (the book then prints at `bounds`). */
32
34
  css: (PageSizePoints & {
33
35
  file: string;
34
36
  }) | null;
35
37
  }
36
- export declare function isPositivePoints(value: unknown): value is number;
37
38
  /**
38
39
  * Write the manifest's `page:` bounds. `tolerance` defaults to whatever the
39
40
  * block already carries, so re-sizing a book never silently resets a
@@ -43,8 +44,6 @@ export declare function isPositivePoints(value: unknown): value is number;
43
44
  export declare function writePageBounds(doc: Document, page: CustomPageOptions): void;
44
45
  /** The size (points) a choice means: `custom` brings its own, the others are the preset's. */
45
46
  export declare function pageSizeForChoice(choice: PageSizeChoice): PageSizePoints;
46
- /** The `size` value written for a page: `8.5in 11in`, `595pt 842pt`. */
47
- export declare function formatPageSize(page: PageSizePoints): string;
48
47
  /** The size the LAST plain `@page { size }` in the stylesheet sets (cascade: last wins), or null. */
49
48
  export declare function readPageSizeFromCss(css: string): PageSizePoints | null;
50
49
  /**
@@ -122,6 +122,18 @@ export interface ImageRef {
122
122
  */
123
123
  x: number;
124
124
  y: number;
125
+ /**
126
+ * The drawn rect in points, y up from the sheet's bottom: the unit square
127
+ * under the CTM as its axis-aligned bounds, so it holds for an image drawn
128
+ * flipped or rotated where `x`/`y`/`placedW`/`placedH` do not. The
129
+ * `pdf.print.safe-margins` check judges it against the art rule.
130
+ */
131
+ bounds: {
132
+ x: number;
133
+ y: number;
134
+ w: number;
135
+ h: number;
136
+ };
125
137
  /** 1-based page number. */
126
138
  page: number;
127
139
  }
@@ -64,9 +64,9 @@ export interface CreateProjectOptions {
64
64
  /**
65
65
  * Page geometry written into the generated manifest (points; 72pt = 1in).
66
66
  * REQUIRED when `preset` is `"custom"` (it has no built-in trim); allowed
67
- * with any preset to override its trim. These are the validation bounds
68
- * the built PDF is checked against — the actual trim comes from the
69
- * stylesheet's `@page` rule, and the two should match.
67
+ * with any preset to override its trim. This is the size the book prints
68
+ * at, unless a stylesheet sets its own plain `@page { size }` (#415), and
69
+ * the size the built PDF is checked against — so the two should match.
70
70
  */
71
71
  customPage?: CustomPageOptions;
72
72
  /** Version-history mode for the new project. Defaults to `"local-git"`. */