gutterpress 0.11.6-alpha.2 → 0.11.6-alpha.4

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 (34) hide show
  1. package/dist/api/index.d.ts +3 -3
  2. package/dist/api/index.js +10 -6
  3. package/dist/{audit-fdba6axw.js → audit-qd5q0k1m.js} +5 -5
  4. package/dist/{build-2gjr2rj6.js → build-38mz8d2q.js} +5 -5
  5. package/dist/cli-4tx0szk7.js +1251 -0
  6. package/dist/{cli-sekhwex4.js → cli-5vdgncmm.js} +2 -2
  7. package/dist/{cli-ersw5g7j.js → cli-a569p7v6.js} +1 -1
  8. package/dist/{cli-7k2v2sya.js → cli-q4e99n20.js} +409 -984
  9. package/dist/{cli-wmvvkwcc.js → cli-qt466d4e.js} +1 -1
  10. package/dist/cli.js +14 -14
  11. package/dist/{doctor-vgarhrsg.js → doctor-a98dkqnp.js} +2 -2
  12. package/dist/{engine-w5ed5hmb.js → engine-15dfcy85.js} +2 -2
  13. package/dist/{engine-cbc17ne4.js → engine-amxqsfka.js} +1 -1
  14. package/dist/{ext-hb5k5gga.js → ext-5wgrtygx.js} +7 -7
  15. package/dist/{gutterpress-viewer-k0xrt5hw.js → gutterpress-viewer-swz6bwfa.js} +25 -3
  16. package/dist/index-7mqst9tg.js +1302 -0
  17. package/dist/{index-nmykzwb4.js → index-ash0pvb9.js} +495 -1069
  18. package/dist/{index-6n15e1ze.js → index-gf1shk3y.js} +1 -1
  19. package/dist/{index-12pvhbyz.js → index-yeej2vhs.js} +2 -2
  20. package/dist/index.js +11 -7
  21. package/dist/lib/project-scaffold.d.ts +20 -0
  22. package/dist/lib/remote-auth/sync-messages.d.ts +7 -7
  23. package/dist/lib/source-provider.d.ts +24 -0
  24. package/dist/{lint-0pfqbs5r.js → lint-btmms4m9.js} +5 -5
  25. package/dist/{new-nxxx2mv2.js → new-xt38pdjk.js} +7 -7
  26. package/dist/{preflight-g7xjme17.js → preflight-fydrrjpz.js} +5 -5
  27. package/dist/{preview-j59b5hg3.js → preview-655bdb9g.js} +6 -6
  28. package/dist/{publish-mzct8ggw.js → publish-gz6re318.js} +6 -6
  29. package/dist/{source-provider-54a52bpd.js → source-provider-gy3cn7kw.js} +3 -1
  30. package/dist/{source-provider-a458mmec.js → source-provider-m5wes47h.js} +3 -1
  31. package/dist/{validate-z2drbha6.js → validate-e3vvngx0.js} +5 -5
  32. package/package.json +1 -1
  33. package/dist/cli-c9h2bk2k.js +0 -645
  34. package/dist/index-c5ev4r1y.js +0 -696
@@ -18,20 +18,39 @@ import {
18
18
  resolveChromiumExecutable,
19
19
  run,
20
20
  spawnCapture
21
- } from "./index-6n15e1ze.js";
21
+ } from "./index-gf1shk3y.js";
22
22
  import {
23
+ PLUGINS_DIR,
24
+ VENDORED_NPM_DIR,
25
+ VENDOR_RECEIPT_FILE,
26
+ VENDOR_RECEIPT_VERSION,
27
+ assertWindowsSafeRelativePath,
28
+ computeVendorTreeDigest,
23
29
  errorLogData,
24
30
  gitFs,
25
31
  gitScopeFor,
26
32
  hasPendingChanges,
27
33
  hasUncommittedChanges,
34
+ isExactNpmVersion,
35
+ isValidNpmPackageName,
28
36
  listWorkdirChanges,
37
+ npmRegistryUrl,
38
+ packageEntryFormat,
39
+ packageResolutionTargets,
40
+ parseNpmPluginSpec,
29
41
  resolveGitAuthor,
30
42
  resolveLogger,
43
+ resolvePackageEntry,
44
+ resolveVendoredPluginInstallRoot,
31
45
  snapshotStagingMarkerPath,
32
46
  snapshotWorkingTreeUnlocked,
47
+ toPosixPath,
48
+ vendoredNpmPluginPackageDir,
49
+ vendoredNpmPluginRoot,
50
+ verifyVendoredPlugin,
51
+ windowsPathKey,
33
52
  withRepoLock
34
- } from "./index-c5ev4r1y.js";
53
+ } from "./index-7mqst9tg.js";
35
54
  import {
36
55
  detectProjectSource,
37
56
  repoSubPath,
@@ -42,611 +61,18 @@ import {
42
61
  } from "./index-37x76zdn.js";
43
62
 
44
63
  // src/lib/build-runner.ts
45
- import path9 from "node:path";
64
+ import path8 from "node:path";
46
65
  import fs2 from "node:fs";
47
66
  import fsp2 from "node:fs/promises";
48
67
  import os2 from "node:os";
49
68
  import { randomBytes } from "node:crypto";
50
69
 
51
70
  // src/lib/manifest.ts
52
- import { readFile as readFile2 } from "node:fs/promises";
71
+ import { readFile } from "node:fs/promises";
53
72
  import { existsSync, statSync } from "node:fs";
54
73
  import { resolve, dirname } from "node:path";
55
74
  import { parse as parseYaml, YAMLParseError } from "yaml";
56
75
 
57
- // src/lib/plugin-vendor.ts
58
- import { createHash } from "node:crypto";
59
- import { createReadStream } from "node:fs";
60
- import { lstat, readFile, readdir, realpath } from "node:fs/promises";
61
- import path from "node:path";
62
- import { exports as resolveExports } from "resolve.exports";
63
- import { satisfies, validRange } from "semver";
64
-
65
- // src/lib/npm-registry.ts
66
- var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
67
- var ENV_OVERRIDE = "GUTTERPRESS_NPM_REGISTRY";
68
- function npmRegistryUrl() {
69
- const configured = process.env[ENV_OVERRIDE]?.trim();
70
- if (!configured)
71
- return DEFAULT_NPM_REGISTRY;
72
- let scheme;
73
- try {
74
- scheme = new URL(configured).protocol;
75
- } catch {
76
- throw new Error(`Invalid ${ENV_OVERRIDE} URL "${configured}".`);
77
- }
78
- if (scheme !== "http:" && scheme !== "https:") {
79
- throw new Error(`${ENV_OVERRIDE} must be an http(s) URL — got "${scheme}".`);
80
- }
81
- return configured.replace(/\/+$/, "");
82
- }
83
-
84
- // src/lib/plugin-vendor.ts
85
- var PLUGINS_DIR = "plugins";
86
- var VENDORED_NPM_DIR = "npm";
87
- var VENDOR_RECEIPT_FILE = ".gutterpress-install.json";
88
- var VENDOR_RECEIPT_VERSION = 2;
89
- var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
90
- 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-]+)*)?$/;
91
- var WINDOWS_RESERVED = /^(con|prn|aux|nul|clock\$|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\..*)?$/i;
92
- var MAX_VERIFIED_FILES = 50000;
93
- var MAX_VERIFIED_BYTES = 512 * 1024 * 1024;
94
- function isValidNpmPackageName(name) {
95
- if (!name || name.length > 214 || name !== name.toLowerCase())
96
- return false;
97
- if (name.startsWith("@")) {
98
- const parts = name.slice(1).split("/");
99
- return parts.length === 2 && parts.every((part) => NPM_SEGMENT.test(part));
100
- }
101
- return !name.includes("/") && NPM_SEGMENT.test(name);
102
- }
103
- function parseNpmPluginSpec(input) {
104
- const spec = input.trim();
105
- if (!spec)
106
- throw new Error("An npm package name is required.");
107
- let name = spec;
108
- let selector;
109
- if (spec.startsWith("@")) {
110
- const slash = spec.indexOf("/");
111
- const versionAt = spec.lastIndexOf("@");
112
- if (slash > 1 && versionAt > slash) {
113
- name = spec.slice(0, versionAt);
114
- selector = spec.slice(versionAt + 1);
115
- }
116
- } else {
117
- const versionAt = spec.lastIndexOf("@");
118
- if (versionAt > 0) {
119
- name = spec.slice(0, versionAt);
120
- selector = spec.slice(versionAt + 1);
121
- }
122
- }
123
- if (!isValidNpmPackageName(name)) {
124
- throw new Error(`"${name}" is not a valid npm package name. Use a name like ` + "markdown-it-highlightjs or @scope/markdown-it-plugin.");
125
- }
126
- if (selector !== undefined && !selector) {
127
- throw new Error(`The package spec "${spec}" is missing a selector after @.`);
128
- }
129
- return selector === undefined ? { name } : { name, selector };
130
- }
131
- function isExactNpmVersion(version) {
132
- return EXACT_VERSION.test(version);
133
- }
134
- function encodePathPart(value) {
135
- return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
136
- }
137
- function vendoredNpmPluginRoot(projectDir, name, version) {
138
- return path.join(projectDir, PLUGINS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
139
- }
140
- function vendoredNpmPluginPackageDir(installRoot, name) {
141
- return path.join(installRoot, "node_modules", ...name.split("/"));
142
- }
143
- function toPosixPath(value) {
144
- return value.split(path.sep).join("/");
145
- }
146
- function packageEntryFormat(manifest, relativeEntry) {
147
- const extension = path.posix.extname(relativeEntry);
148
- return extension === ".cjs" || extension === ".json" || extension !== ".mjs" && manifest.type !== "module" ? "commonjs" : "module";
149
- }
150
- function assertWindowsSafeRelativePath(relative) {
151
- if (!relative || relative.includes("\\") || relative.startsWith("/") || /^[a-zA-Z]:/.test(relative)) {
152
- throw new Error(`Unsafe package path: ${relative || "(empty)"}`);
153
- }
154
- const normalized = relative.normalize("NFKC");
155
- const parts = normalized.split("/");
156
- if (parts.some((part) => !part || part === "." || part === "..")) {
157
- throw new Error(`Unsafe package path: ${relative}`);
158
- }
159
- for (const part of parts) {
160
- if (part.length > 255 || /[\u0000-\u001f<>:"|?*]/.test(part) || part.endsWith(".") || part.endsWith(" ") || WINDOWS_RESERVED.test(part)) {
161
- throw new Error(`Package path is invalid on Windows: ${relative}`);
162
- }
163
- }
164
- }
165
- function windowsPathKey(relative) {
166
- return relative.normalize("NFKC").toLowerCase();
167
- }
168
- function safeReceiptPath(relative, label) {
169
- if (typeof relative !== "string")
170
- throw new Error(`${label} must be a string.`);
171
- assertWindowsSafeRelativePath(relative);
172
- return relative;
173
- }
174
- function isContained(root, candidate) {
175
- const relative = path.relative(root, candidate);
176
- return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
177
- }
178
- function object(value) {
179
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
180
- }
181
- function stringMap(value) {
182
- const map = object(value);
183
- if (!map)
184
- return {};
185
- const result = {};
186
- for (const [name, selector] of Object.entries(map)) {
187
- if (typeof selector === "string" && selector.trim())
188
- result[name] = selector.trim();
189
- }
190
- return result;
191
- }
192
- function dependencyDeclarations(manifest) {
193
- const required = stringMap(manifest.dependencies);
194
- const optional = stringMap(manifest.optionalDependencies);
195
- for (const name of Object.keys(optional))
196
- delete required[name];
197
- const peers = stringMap(manifest.peerDependencies);
198
- const peerMeta = object(manifest.peerDependenciesMeta) ?? {};
199
- const optionalPeers = {};
200
- for (const [name, selector] of Object.entries(peers)) {
201
- if (name in required || name in optional)
202
- continue;
203
- if (object(peerMeta[name])?.optional === true)
204
- optionalPeers[name] = selector;
205
- else
206
- required[name] = selector;
207
- }
208
- return { required, optional, optionalPeers };
209
- }
210
- function unsupportedRegistrySelector(selector) {
211
- return /^(?:file:|git\+|https?:|ssh:|github:|workspace:|link:|npm:)/i.test(selector);
212
- }
213
- async function readPackageJson(packageDir) {
214
- const packageJsonPath = path.join(packageDir, "package.json");
215
- const info = await lstat(packageJsonPath);
216
- if (!info.isFile() || info.isSymbolicLink()) {
217
- throw new Error(`Package metadata is not a regular file: ${packageJsonPath}`);
218
- }
219
- const parsed = object(JSON.parse(await readFile(packageJsonPath, "utf8")));
220
- if (!parsed)
221
- throw new Error(`Package metadata is not an object: ${packageJsonPath}`);
222
- return parsed;
223
- }
224
- async function verifyReceiptEntry(installRoot, packagePath, entry, label) {
225
- if (entry === undefined)
226
- return;
227
- const relative = safeReceiptPath(entry, label);
228
- if (!relative.startsWith(`${packagePath}/`)) {
229
- throw new Error(`${label} is outside its package.`);
230
- }
231
- const packageDir = path.resolve(installRoot, ...packagePath.split("/"));
232
- const entryPath = path.resolve(installRoot, ...relative.split("/"));
233
- const [realPackageDir, realEntry] = await Promise.all([
234
- realpath(packageDir),
235
- realpath(entryPath)
236
- ]);
237
- if (!isContained(realPackageDir, realEntry))
238
- throw new Error(`${label} escapes its package.`);
239
- const info = await lstat(realEntry);
240
- if (!info.isFile() || info.isSymbolicLink())
241
- throw new Error(`${label} is not a regular file.`);
242
- return realEntry;
243
- }
244
- function packageResolutionTargets(manifest, subpath, condition) {
245
- if (manifest.exports !== undefined) {
246
- try {
247
- const targets2 = resolveExports(manifest, subpath ? `./${subpath}` : ".", { require: condition === "require" }) ?? [];
248
- return targets2.map((target) => ({ target, exact: true }));
249
- } catch {
250
- return [];
251
- }
252
- }
253
- if (subpath)
254
- return [{ target: subpath, exact: false }];
255
- const targets = [
256
- ...condition === "import" && typeof manifest.module === "string" ? [manifest.module] : [],
257
- ...typeof manifest.main === "string" ? [manifest.main] : [],
258
- "index.mjs",
259
- "index.js",
260
- "index.cjs"
261
- ];
262
- return [...new Set(targets)].map((target) => ({ target, exact: false }));
263
- }
264
- function safePackageTarget(target) {
265
- if (!target || target.includes("\\") || target.includes("*") || target.includes("?") || target.includes("#")) {
266
- return null;
267
- }
268
- const relative = target.startsWith("./") ? target.slice(2) : target;
269
- if (!relative || path.isAbsolute(relative))
270
- return null;
271
- assertWindowsSafeRelativePath(relative);
272
- if (relative.normalize("NFKC").split("/").some((part) => part.toLowerCase() === "node_modules")) {
273
- return null;
274
- }
275
- return relative;
276
- }
277
- async function resolveEntryCandidate(packageDir, resolution) {
278
- const relative = safePackageTarget(resolution.target);
279
- if (!relative)
280
- return null;
281
- const base = path.resolve(packageDir, ...relative.split("/"));
282
- if (!isContained(packageDir, base))
283
- return null;
284
- const candidates = resolution.exact ? [base] : [
285
- base,
286
- `${base}.mjs`,
287
- `${base}.js`,
288
- `${base}.cjs`,
289
- `${base}.json`,
290
- path.join(base, "index.mjs"),
291
- path.join(base, "index.js"),
292
- path.join(base, "index.cjs"),
293
- path.join(base, "index.json")
294
- ];
295
- for (const candidate of candidates) {
296
- try {
297
- const info = await lstat(candidate);
298
- if (!info.isFile() || info.isSymbolicLink())
299
- continue;
300
- const extension = path.extname(candidate);
301
- if (extension && ![".js", ".mjs", ".cjs", ".json"].includes(extension))
302
- continue;
303
- const [realRoot, realCandidate] = await Promise.all([realpath(packageDir), realpath(candidate)]);
304
- if (!isContained(realRoot, realCandidate))
305
- continue;
306
- return toPosixPath(path.relative(packageDir, realCandidate));
307
- } catch {}
308
- }
309
- return null;
310
- }
311
- async function resolvePackageEntry(packageDir, packageJson, condition = "import") {
312
- const manifest = packageJson ?? await readPackageJson(packageDir);
313
- for (const target of packageResolutionTargets(manifest, "", condition)) {
314
- const resolved = await resolveEntryCandidate(packageDir, target);
315
- if (resolved)
316
- return resolved;
317
- }
318
- throw new Error("Package has no contained JavaScript entry point for ESM import.");
319
- }
320
- async function listTreeEntries(root) {
321
- const out = [];
322
- async function visit(dir) {
323
- const entries = await readdir(dir, { withFileTypes: true });
324
- entries.sort((a, b) => a.name.localeCompare(b.name));
325
- for (const entry of entries) {
326
- const absolute = path.join(dir, entry.name);
327
- const relative = toPosixPath(path.relative(root, absolute));
328
- if (relative === VENDOR_RECEIPT_FILE)
329
- continue;
330
- if (entry.isSymbolicLink())
331
- throw new Error(`Vendor tree contains a symbolic link: ${relative}`);
332
- if (entry.isDirectory()) {
333
- out.push({ relative, directory: true });
334
- await visit(absolute);
335
- } else if (entry.isFile())
336
- out.push({ relative, directory: false });
337
- else
338
- throw new Error(`Vendor tree contains an unsupported filesystem entry: ${relative}`);
339
- }
340
- }
341
- await visit(root);
342
- return out;
343
- }
344
- async function computeVendorTreeDigest(root) {
345
- const entries = await listTreeEntries(root);
346
- const files = entries.filter((entry) => !entry.directory);
347
- if (files.length > MAX_VERIFIED_FILES) {
348
- throw new Error(`Vendor tree contains more than ${MAX_VERIFIED_FILES} files.`);
349
- }
350
- const seen = new Set;
351
- const hash = createHash("sha256");
352
- let bytes = 0;
353
- for (const entry of entries) {
354
- const { relative } = entry;
355
- assertWindowsSafeRelativePath(relative);
356
- const key = windowsPathKey(relative);
357
- if (seen.has(key))
358
- throw new Error(`Vendor tree has a Windows-colliding path: ${relative}`);
359
- seen.add(key);
360
- if (entry.directory)
361
- continue;
362
- const absolute = path.join(root, ...relative.split("/"));
363
- const info = await lstat(absolute);
364
- if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1) {
365
- throw new Error(`Vendor tree file is not an independent regular file: ${relative}`);
366
- }
367
- bytes += info.size;
368
- if (bytes > MAX_VERIFIED_BYTES) {
369
- throw new Error(`Vendor tree exceeds ${Math.round(MAX_VERIFIED_BYTES / 1024 / 1024)}MB.`);
370
- }
371
- hash.update(`${Buffer.byteLength(relative)}:${relative}:${info.size}\x00`);
372
- for await (const chunk of createReadStream(absolute))
373
- hash.update(chunk);
374
- }
375
- return { digest: hash.digest("hex"), files: files.length, bytes };
376
- }
377
- async function assertVendorPackageOwnership(root, packagePaths) {
378
- const packages = packagePaths.map((relative) => ({
379
- relative,
380
- key: windowsPathKey(relative),
381
- parts: relative.split("/").length
382
- })).sort((a, b) => b.key.length - a.key.length);
383
- for (const entry of await listTreeEntries(root)) {
384
- const actualParts = entry.relative.split("/");
385
- for (const part of actualParts) {
386
- if (part.normalize("NFKC").toLowerCase() === "node_modules" && part !== "node_modules") {
387
- throw new Error(`Vendor tree contains a non-canonical node_modules path: ${entry.relative}`);
388
- }
389
- }
390
- const key = windowsPathKey(entry.relative);
391
- const structural = packages.some((pkg) => pkg.key === key || pkg.key.startsWith(`${key}/`));
392
- if (entry.directory && structural)
393
- continue;
394
- const owner = packages.find((pkg) => key.startsWith(`${pkg.key}/`));
395
- if (!owner)
396
- throw new Error(`Vendor tree path is not owned by a receipt package: ${entry.relative}`);
397
- const packageRelative = actualParts.slice(owner.parts);
398
- const nodeModulesAt = packageRelative.findIndex((part) => part.normalize("NFKC").toLowerCase() === "node_modules");
399
- if (nodeModulesAt < 0)
400
- continue;
401
- const generatedEmptyDirectory = entry.directory && nodeModulesAt === 0 && (packageRelative.length === 1 || packageRelative.length === 2 && packageRelative[1].startsWith("@"));
402
- if (!generatedEmptyDirectory) {
403
- throw new Error(`Package contains bundled node_modules content: ${entry.relative}`);
404
- }
405
- }
406
- }
407
- async function recomputeReceiptEntry(packageDir, packagePath, manifest, condition) {
408
- try {
409
- const entry = await resolvePackageEntry(packageDir, manifest, condition);
410
- return `${packagePath}/${entry}`;
411
- } catch (error) {
412
- if (error instanceof Error && error.message.includes("no contained JavaScript entry point")) {
413
- return;
414
- }
415
- throw error;
416
- }
417
- }
418
- async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
419
- const requestedProjectRoot = path.resolve(projectDir);
420
- const requestedInstallRoot = vendoredNpmPluginRoot(requestedProjectRoot, expectedName, expectedVersion);
421
- let installInfo;
422
- try {
423
- installInfo = await lstat(requestedInstallRoot);
424
- } catch (error) {
425
- if (error.code === "ENOENT")
426
- return null;
427
- throw error;
428
- }
429
- if (!installInfo.isDirectory() || installInfo.isSymbolicLink()) {
430
- throw new Error("Vendor install root is not a normal directory.");
431
- }
432
- const [realProjectRoot, installRoot] = await Promise.all([
433
- realpath(requestedProjectRoot),
434
- realpath(requestedInstallRoot)
435
- ]);
436
- if (!isContained(realProjectRoot, installRoot)) {
437
- throw new Error("Vendor install root resolves outside the project.");
438
- }
439
- return installRoot;
440
- }
441
- function reconcileReceiptGraph(receipt, packages, manifests) {
442
- if (!Array.isArray(receipt.skipped))
443
- throw new Error("Vendor receipt skipped list is invalid.");
444
- const declarations = new Map;
445
- for (const [packagePath, manifest] of manifests) {
446
- declarations.set(packagePath, dependencyDeclarations(manifest));
447
- }
448
- const skippedByEdge = new Map;
449
- for (const skipped of receipt.skipped) {
450
- if (!packages.has(skipped.from) || !isValidNpmPackageName(skipped.name) || typeof skipped.selector !== "string" || !["optional", "optional-peer"].includes(skipped.kind) || typeof skipped.reason !== "string") {
451
- throw new Error("Vendor receipt contains an invalid skipped dependency.");
452
- }
453
- const declared = declarations.get(skipped.from);
454
- const expectedSelector = skipped.kind === "optional" ? declared.optional[skipped.name] : declared.optionalPeers[skipped.name];
455
- if (expectedSelector !== skipped.selector) {
456
- throw new Error(`Skipped dependency is not declared as ${skipped.kind}: ${skipped.name}`);
457
- }
458
- const key = `${skipped.from}\x00${skipped.name}`;
459
- if (skippedByEdge.has(key))
460
- throw new Error(`Duplicate skipped dependency: ${skipped.name}`);
461
- skippedByEdge.set(key, skipped);
462
- }
463
- for (const pkg of receipt.packages) {
464
- if (!object(pkg.dependencies))
465
- throw new Error(`Invalid dependency map for ${pkg.name}.`);
466
- const declared = declarations.get(pkg.path);
467
- for (const [dependency, target] of Object.entries(pkg.dependencies)) {
468
- const targetPackage = packages.get(target);
469
- if (!isValidNpmPackageName(dependency) || !targetPackage || targetPackage.name !== dependency) {
470
- throw new Error(`Broken dependency edge ${pkg.name} -> ${dependency}.`);
471
- }
472
- const selector = declared.required[dependency] ?? declared.optional[dependency];
473
- if (!selector)
474
- throw new Error(`Undeclared dependency edge ${pkg.name} -> ${dependency}.`);
475
- if (unsupportedRegistrySelector(selector)) {
476
- throw new Error(`Unsupported selector on dependency edge ${pkg.name} -> ${dependency}.`);
477
- }
478
- const range = validRange(selector);
479
- if (range && !satisfies(targetPackage.version, range)) {
480
- throw new Error(`Dependency edge ${pkg.name} -> ${dependency}@${targetPackage.version} does not satisfy ${selector}.`);
481
- }
482
- if (skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
483
- throw new Error(`Dependency is both installed and skipped: ${pkg.name} -> ${dependency}.`);
484
- }
485
- }
486
- for (const dependency of Object.keys(declared.required)) {
487
- if (!(dependency in pkg.dependencies)) {
488
- throw new Error(`Missing required dependency edge ${pkg.name} -> ${dependency}.`);
489
- }
490
- }
491
- for (const dependency of Object.keys(declared.optional)) {
492
- if (!(dependency in pkg.dependencies) && !skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
493
- throw new Error(`Optional dependency is neither installed nor skipped: ${pkg.name} -> ${dependency}.`);
494
- }
495
- }
496
- for (const dependency of Object.keys(declared.optionalPeers)) {
497
- if (!skippedByEdge.has(`${pkg.path}\x00${dependency}`)) {
498
- throw new Error(`Optional peer dependency is not recorded as skipped: ${pkg.name} -> ${dependency}.`);
499
- }
500
- }
501
- }
502
- }
503
- async function verifyVendoredPlugin(projectDir, expectedName, expectedVersion) {
504
- try {
505
- const installRoot = await resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion);
506
- if (!installRoot)
507
- return null;
508
- const receiptPath = path.join(installRoot, VENDOR_RECEIPT_FILE);
509
- let raw;
510
- try {
511
- const info = await lstat(receiptPath);
512
- if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.size > 1024 * 1024) {
513
- throw new Error("Vendor receipt is not a small regular file.");
514
- }
515
- raw = await readFile(receiptPath, "utf8");
516
- } catch (error) {
517
- if (error.code === "ENOENT")
518
- return null;
519
- throw error;
520
- }
521
- const receipt = JSON.parse(raw);
522
- if (receipt.schemaVersion !== VENDOR_RECEIPT_VERSION) {
523
- throw new Error(`Unsupported vendor receipt schema ${String(receipt.schemaVersion)}.`);
524
- }
525
- if (receipt.root?.name !== expectedName || receipt.root?.version !== expectedVersion) {
526
- throw new Error("Vendor receipt identity does not match the manifest entry.");
527
- }
528
- if (!Array.isArray(receipt.packages) || receipt.packages.length === 0) {
529
- throw new Error("Vendor receipt has no package graph.");
530
- }
531
- const tree = await computeVendorTreeDigest(installRoot);
532
- if (receipt.tree?.algorithm !== "sha256" || receipt.tree.digest !== tree.digest || receipt.tree.files !== tree.files || receipt.tree.bytes !== tree.bytes) {
533
- throw new Error("Vendor tree hash does not match its receipt.");
534
- }
535
- const packages = new Map;
536
- const manifests = new Map;
537
- const verifiedPackages = [];
538
- const pathKeys = new Set;
539
- for (const pkg of receipt.packages) {
540
- const relative = safeReceiptPath(pkg.path, "Package receipt path");
541
- const key = windowsPathKey(relative);
542
- if (pathKeys.has(key))
543
- throw new Error(`Duplicate package receipt path: ${relative}`);
544
- pathKeys.add(key);
545
- if (!isValidNpmPackageName(pkg.name) || !isExactNpmVersion(pkg.version)) {
546
- throw new Error(`Invalid package identity in receipt: ${pkg.name}@${pkg.version}`);
547
- }
548
- const expectedSuffix = `node_modules/${pkg.name}`;
549
- if (relative !== expectedSuffix && !relative.endsWith(`/${expectedSuffix}`)) {
550
- throw new Error(`Package receipt path does not match ${pkg.name}: ${relative}`);
551
- }
552
- if (typeof pkg.integrity !== "string" || !/^(?:sha512|sha384|sha256|sha1)-(?:hex-)?[A-Za-z0-9+/=]+$/.test(pkg.integrity) || typeof pkg.legacySha1 !== "boolean" || pkg.legacySha1 !== pkg.integrity.startsWith("sha1-")) {
553
- throw new Error(`Package receipt is missing provenance: ${pkg.name}@${pkg.version}`);
554
- }
555
- const tarball = new URL(pkg.tarball);
556
- if (tarball.origin !== new URL(npmRegistryUrl()).origin || tarball.username || tarball.password) {
557
- throw new Error(`Package receipt has an invalid tarball URL: ${pkg.name}@${pkg.version}`);
558
- }
559
- const packageDir = path.resolve(installRoot, ...relative.split("/"));
560
- if (!isContained(installRoot, packageDir))
561
- throw new Error(`Package escapes vendor root: ${relative}`);
562
- const realPackageDir2 = await realpath(packageDir);
563
- if (!isContained(installRoot, realPackageDir2)) {
564
- throw new Error(`Package resolves outside the vendor root: ${relative}`);
565
- }
566
- const manifest = await readPackageJson(packageDir);
567
- if (manifest.name !== pkg.name || manifest.version !== pkg.version) {
568
- throw new Error(`Installed package identity changed: ${pkg.name}@${pkg.version}`);
569
- }
570
- const [expectedEntry, expectedRequireEntry] = await Promise.all([
571
- recomputeReceiptEntry(packageDir, relative, manifest, "import"),
572
- recomputeReceiptEntry(packageDir, relative, manifest, "require")
573
- ]);
574
- if (pkg.entry !== expectedEntry || pkg.requireEntry !== expectedRequireEntry) {
575
- throw new Error(`Recorded package entries do not match package.json: ${pkg.name}@${pkg.version}`);
576
- }
577
- packages.set(relative, pkg);
578
- manifests.set(relative, manifest);
579
- verifiedPackages.push({
580
- path: relative,
581
- name: pkg.name,
582
- packageDir: realPackageDir2,
583
- manifest,
584
- entryPath: await verifyReceiptEntry(installRoot, relative, pkg.entry, "Package import entry"),
585
- requireEntryPath: await verifyReceiptEntry(installRoot, relative, pkg.requireEntry, "Package require entry"),
586
- dependencies: pkg.dependencies
587
- });
588
- }
589
- await assertVendorPackageOwnership(installRoot, receipt.packages.map((pkg) => pkg.path));
590
- reconcileReceiptGraph(receipt, packages, manifests);
591
- const rootPackagePath = safeReceiptPath(receipt.root.packagePath, "Root package path");
592
- const rootPackage = packages.get(rootPackagePath);
593
- if (!rootPackage || rootPackage.name !== expectedName || rootPackage.version !== expectedVersion) {
594
- throw new Error("Vendor receipt root package is missing.");
595
- }
596
- const reachablePackages = new Set([rootPackagePath]);
597
- const pendingPackages = [rootPackagePath];
598
- while (pendingPackages.length > 0) {
599
- const current = packages.get(pendingPackages.pop());
600
- for (const target of Object.values(current.dependencies)) {
601
- if (reachablePackages.has(target))
602
- continue;
603
- reachablePackages.add(target);
604
- pendingPackages.push(target);
605
- }
606
- }
607
- if (reachablePackages.size !== receipt.packages.length) {
608
- throw new Error("Vendor receipt contains a package that is not reachable from its root.");
609
- }
610
- const rootManifest = manifests.get(rootPackagePath);
611
- if (receipt.root.format !== "module" && receipt.root.format !== "commonjs") {
612
- throw new Error("Vendor receipt root format is invalid.");
613
- }
614
- const entryRelative = safeReceiptPath(receipt.root.entry, "Plugin entry path");
615
- if (!entryRelative.startsWith(`${rootPackagePath}/`)) {
616
- throw new Error("Plugin entry is outside its package.");
617
- }
618
- const expectedRootEntry = receipt.root.format === "commonjs" ? rootPackage.requireEntry ?? rootPackage.entry : rootPackage.entry;
619
- if (entryRelative !== expectedRootEntry) {
620
- throw new Error("Plugin entry does not match the package graph entry.");
621
- }
622
- if (packageEntryFormat(rootManifest, entryRelative) !== receipt.root.format) {
623
- throw new Error("Plugin entry format does not match its package metadata.");
624
- }
625
- const entryPath = path.resolve(installRoot, ...entryRelative.split("/"));
626
- const rootPackageDir = path.resolve(installRoot, ...rootPackagePath.split("/"));
627
- const [realPackageDir, realEntry] = await Promise.all([
628
- realpath(rootPackageDir),
629
- realpath(entryPath)
630
- ]);
631
- if (!isContained(installRoot, realEntry) || !isContained(realPackageDir, realEntry)) {
632
- throw new Error("Plugin entry resolves outside the verified vendor tree.");
633
- }
634
- const entryInfo = await lstat(realEntry);
635
- if (!entryInfo.isFile() || entryInfo.isSymbolicLink()) {
636
- throw new Error("Plugin entry is not a regular file.");
637
- }
638
- return {
639
- installRoot,
640
- entryPath: realEntry,
641
- format: receipt.root.format,
642
- packages: verifiedPackages,
643
- receipt
644
- };
645
- } catch (error) {
646
- throw new Error(`Vendored plugin "${expectedName}@${expectedVersion}" failed verification: ` + `${error instanceof Error ? error.message : String(error)} Reinstall it before building.`, { cause: error });
647
- }
648
- }
649
-
650
76
  // src/lib/extension-specifier.ts
651
77
  var BUNDLED_EXTENSIONS = [
652
78
  "markdown-it-mark",
@@ -1027,7 +453,7 @@ async function loadManifestWithPath(pathOrDir, opts) {
1027
453
  const candidates = pathOrDir ? [resolve(pathOrDir), ...MANIFEST_FILENAMES.map((name) => resolve(pathOrDir, name))] : MANIFEST_FILENAMES.map((name) => resolve(name));
1028
454
  for (const p of candidates) {
1029
455
  if (existsSync(p) && statSync(p).isFile()) {
1030
- const raw = await readFile2(p, "utf8");
456
+ const raw = await readFile(p, "utf8");
1031
457
  try {
1032
458
  const manifest = parseYaml(raw) ?? {};
1033
459
  return { manifest, manifestDir: dirname(p), manifestPath: p };
@@ -1215,7 +641,7 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
1215
641
  }
1216
642
 
1217
643
  // src/lib/markdown/index.ts
1218
- import { readdir as readdir3, readFile as readFile4, writeFile, mkdir } from "node:fs/promises";
644
+ import { readdir as readdir2, readFile as readFile3, writeFile, mkdir } from "node:fs/promises";
1219
645
 
1220
646
  // src/lib/remote-auth/sync-messages.ts
1221
647
  var MSG_UP_TO_DATE = "Everything is in sync.";
@@ -1224,13 +650,13 @@ var MSG_SYNCED = "Your changes are online.";
1224
650
  var MSG_SYNCED_MERGED = "Your changes are online, combined with changes from the online copy.";
1225
651
  var MSG_OFFLINE = "Your changes are saved on this computer. gutterpress couldn't reach the online repository — try syncing again when you're back online.";
1226
652
  var MSG_AUTH = "The online repository didn't accept the saved connection. Reconnect and try again.";
1227
- var MSG_INSECURE_TRANSPORT = "This project's online address isn't secure, so the saved connection wasn't sent — connections are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to sync.";
653
+ var MSG_INSECURE_TRANSPORT = "This book's online address isn't secure, so the saved connection wasn't sent — connections are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to sync.";
1228
654
  var MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer — try Sync again in a moment.";
1229
- var MSG_HISTORY_UNREADABLE = "This project's version history can't be read, so syncing can't continue. Your writing is safe — every file is still here on this computer. To get history and syncing working again, download a fresh copy of the project from online, then move any recent changes into it.";
1230
- var MSG_UNRELATED = "This project and the online copy have no history in common, so they can't be combined. Either the online address points at a different project, or this project's own history was lost and started over. Your writing is safe — every file is still here on this computer. Check the online address; if it is correct, download a fresh copy from online and move any recent changes into it.";
1231
- var MSG_NO_REMOTE = "This project isn't connected to an online repository yet.";
1232
- var MSG_SSH_REMOTE = "This project's online address uses SSH (git@…), which gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
1233
- var MSG_NO_BRANCH = "This project's version history isn't on a named branch, so it can't be synced right now.";
655
+ var MSG_HISTORY_UNREADABLE = "This book's version history can't be read, so syncing can't continue. Your writing is safe — every file is still here on this computer. To get history and syncing working again, download a fresh copy of the book from online, then move any recent changes into it.";
656
+ var MSG_UNRELATED = "This book and the online copy have no history in common, so they can't be combined. Either the online address points at a different book, or this book's own history was lost and started over. Your writing is safe — every file is still here on this computer. Check the online address; if it is correct, download a fresh copy from online and move any recent changes into it.";
657
+ var MSG_NO_REMOTE = "This book isn't connected to an online repository yet.";
658
+ var MSG_SSH_REMOTE = "This book's online address uses SSH (git@…), which gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
659
+ var MSG_NO_BRANCH = "This book's version history isn't on a named branch, so it can't be synced right now.";
1234
660
  var SYNC_SNAPSHOT_MESSAGE = "Automatic backup of your work";
1235
661
  var SYNC_LATE_EDIT_MESSAGE = "Saved the edit you made while syncing";
1236
662
  function onlineSiblingPath(filepath) {
@@ -3224,8 +2650,8 @@ ${bodyContent}
3224
2650
 
3225
2651
  // src/lib/style-resolver.ts
3226
2652
  import { existsSync as existsSync2 } from "node:fs";
3227
- import { readdir as readdir2 } from "node:fs/promises";
3228
- import path2 from "node:path";
2653
+ import { readdir } from "node:fs/promises";
2654
+ import path from "node:path";
3229
2655
  var STYLES_SUBDIR = "styles";
3230
2656
  var FALLBACK_PRIORITY = [
3231
2657
  "styles/book.css",
@@ -3235,21 +2661,21 @@ var FALLBACK_PRIORITY = [
3235
2661
  "css/main.css"
3236
2662
  ];
3237
2663
  function relDisplay(projectDir, absPath) {
3238
- return path2.relative(projectDir, absPath).split(path2.sep).join("/");
2664
+ return path.relative(projectDir, absPath).split(path.sep).join("/");
3239
2665
  }
3240
2666
  async function dirEntries(dir) {
3241
2667
  try {
3242
- return await readdir2(dir, { withFileTypes: true });
2668
+ return await readdir(dir, { withFileTypes: true });
3243
2669
  } catch {
3244
2670
  return [];
3245
2671
  }
3246
2672
  }
3247
2673
  function cssFilesFrom(dir, entries) {
3248
- return entries.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".css")).map((e) => path2.join(dir, e.name));
2674
+ return entries.filter((e) => e.isFile() && e.name.toLowerCase().endsWith(".css")).map((e) => path.join(dir, e.name));
3249
2675
  }
3250
2676
  async function discoverCssFiles(projectDir) {
3251
- const stylesDir = path2.join(projectDir, STYLES_SUBDIR);
3252
- const cssDir = path2.join(projectDir, "css");
2677
+ const stylesDir = path.join(projectDir, STYLES_SUBDIR);
2678
+ const cssDir = path.join(projectDir, "css");
3253
2679
  const [rootEntries, stylesEntries, cssEntries] = await Promise.all([
3254
2680
  dirEntries(projectDir),
3255
2681
  dirEntries(stylesDir),
@@ -3272,7 +2698,7 @@ async function resolveActiveStyles(projectDir, manifestStyles) {
3272
2698
  if (manifest.length > 0)
3273
2699
  return manifest;
3274
2700
  for (const rel of FALLBACK_PRIORITY) {
3275
- if (existsSync2(path2.join(projectDir, rel)))
2701
+ if (existsSync2(path.join(projectDir, rel)))
3276
2702
  return [rel];
3277
2703
  }
3278
2704
  const discovered = await discoverCssFiles(projectDir);
@@ -3286,14 +2712,14 @@ var SHARED_STYLE_ROOTS = ["shared", "."];
3286
2712
  async function discoverSharedCssFiles(repoRoot) {
3287
2713
  const found = new Set;
3288
2714
  for (const rel of SHARED_STYLE_ROOTS) {
3289
- const base = path2.resolve(repoRoot, rel);
2715
+ const base = path.resolve(repoRoot, rel);
3290
2716
  for (const abs of await discoverCssFiles(base))
3291
2717
  found.add(abs);
3292
2718
  }
3293
2719
  return [...found];
3294
2720
  }
3295
2721
  async function listProjectStyles(projectDir, opts = {}) {
3296
- const repoRoot = opts.repoRoot && path2.resolve(opts.repoRoot) !== path2.resolve(projectDir) ? path2.resolve(opts.repoRoot) : null;
2722
+ const repoRoot = opts.repoRoot && path.resolve(opts.repoRoot) !== path.resolve(projectDir) ? path.resolve(opts.repoRoot) : null;
3297
2723
  const [{ manifest }, ownCss, sharedCss] = await Promise.all([
3298
2724
  loadManifestWithPath(projectDir),
3299
2725
  discoverCssFiles(projectDir),
@@ -3304,7 +2730,7 @@ async function listProjectStyles(projectDir, opts = {}) {
3304
2730
  const out = [];
3305
2731
  const seen = new Set;
3306
2732
  for (const rel of activeRels) {
3307
- const abs = path2.resolve(projectDir, rel);
2733
+ const abs = path.resolve(projectDir, rel);
3308
2734
  if (seen.has(abs))
3309
2735
  continue;
3310
2736
  seen.add(abs);
@@ -3316,9 +2742,9 @@ async function listProjectStyles(projectDir, opts = {}) {
3316
2742
  }
3317
2743
 
3318
2744
  // src/lib/asset-inline.ts
3319
- import { createHash as createHash2 } from "node:crypto";
3320
- import { readFile as readFile3 } from "node:fs/promises";
3321
- import path3 from "node:path";
2745
+ import { createHash } from "node:crypto";
2746
+ import { readFile as readFile2 } from "node:fs/promises";
2747
+ import path2 from "node:path";
3322
2748
  import postcss from "postcss";
3323
2749
  var FONT_EXTS = new Set([".woff2", ".woff", ".ttf", ".otf"]);
3324
2750
  var HASHED_ASSET_DIR = "assets";
@@ -3359,21 +2785,21 @@ function decodeRef(ref) {
3359
2785
  }
3360
2786
  }
3361
2787
  function toPosix(p) {
3362
- return p.split(path3.sep).join("/");
2788
+ return p.split(path2.sep).join("/");
3363
2789
  }
3364
2790
  function escapesProjectRoot(projectDir, absPath) {
3365
- const rel = path3.relative(projectDir, absPath);
3366
- return rel === ".." || rel.startsWith(`..${path3.sep}`) || path3.isAbsolute(rel);
2791
+ const rel = path2.relative(projectDir, absPath);
2792
+ return rel === ".." || rel.startsWith(`..${path2.sep}`) || path2.isAbsolute(rel);
3367
2793
  }
3368
2794
  function proseImageRefError(ref, projectDir) {
3369
2795
  const cleaned = stripUrlSuffix(decodeRef(ref));
3370
- if (/^file:/i.test(cleaned) || path3.isAbsolute(cleaned)) {
3371
- return `Image reference must be relative to the project: ${ref}
3372
- ` + ` Copy the file into your project folder and reference it from there.`;
2796
+ if (/^file:/i.test(cleaned) || path2.isAbsolute(cleaned)) {
2797
+ return `Image reference must be relative to the book folder: ${ref}
2798
+ ` + ` Copy the file into your book folder and reference it from there.`;
3373
2799
  }
3374
- if (escapesProjectRoot(projectDir, path3.resolve(projectDir, cleaned))) {
3375
- return `Image reference points outside the project: ${ref}
3376
- ` + ` Copy the file into your project folder and reference it from there.`;
2800
+ if (escapesProjectRoot(projectDir, path2.resolve(projectDir, cleaned))) {
2801
+ return `Image reference points outside the book folder: ${ref}
2802
+ ` + ` Copy the file into your book folder and reference it from there.`;
3377
2803
  }
3378
2804
  return null;
3379
2805
  }
@@ -3382,7 +2808,7 @@ function mimeFor(ext) {
3382
2808
  }
3383
2809
  async function readOrThrow(absPath, kind, referencedFrom) {
3384
2810
  try {
3385
- return await readFile3(absPath);
2811
+ return await readFile2(absPath);
3386
2812
  } catch {
3387
2813
  throw new BuildError(`Missing ${kind}: ${absPath}
3388
2814
  referenced from ${referencedFrom}
@@ -3393,7 +2819,7 @@ function dataUri(bytes, ext) {
3393
2819
  return `data:${mimeFor(ext)};base64,${bytes.toString("base64")}`;
3394
2820
  }
3395
2821
  function contentHash(bytes) {
3396
- return createHash2("sha256").update(bytes).digest("hex").slice(0, 16);
2822
+ return createHash("sha256").update(bytes).digest("hex").slice(0, 16);
3397
2823
  }
3398
2824
  async function rewriteUrls(value, rewrite) {
3399
2825
  const matches = [...value.matchAll(URL_TOKEN_RE)];
@@ -3441,12 +2867,12 @@ function importTarget(atRule) {
3441
2867
  return target;
3442
2868
  }
3443
2869
  async function inlineOne(cssPath, copies, warnings, seen) {
3444
- const abs = path3.resolve(cssPath);
2870
+ const abs = path2.resolve(cssPath);
3445
2871
  if (seen.has(abs))
3446
2872
  return "";
3447
2873
  seen.add(abs);
3448
- const source = await readOrThrow(abs, "stylesheet", "the project manifest");
3449
- const cssDir = path3.dirname(abs);
2874
+ const source = await readOrThrow(abs, "stylesheet", "the book manifest");
2875
+ const cssDir = path2.dirname(abs);
3450
2876
  let root;
3451
2877
  try {
3452
2878
  root = postcss.parse(source.toString("utf-8"), { from: abs });
@@ -3468,8 +2894,8 @@ async function inlineOne(cssPath, copies, warnings, seen) {
3468
2894
  return null;
3469
2895
  }
3470
2896
  const relPath = stripUrlSuffix(decodeRef(raw));
3471
- const absAsset = path3.resolve(cssDir, relPath);
3472
- const ext = path3.extname(absAsset).toLowerCase();
2897
+ const absAsset = path2.resolve(cssDir, relPath);
2898
+ const ext = path2.extname(absAsset).toLowerCase();
3473
2899
  if (FONT_EXTS.has(ext)) {
3474
2900
  const bytes2 = await readOrThrow(absAsset, "font file", abs);
3475
2901
  return dataUri(bytes2, ext);
@@ -3487,7 +2913,7 @@ async function inlineOne(cssPath, copies, warnings, seen) {
3487
2913
  imports.push({ node, target });
3488
2914
  });
3489
2915
  for (const { node, target } of imports) {
3490
- const importedAbs = path3.resolve(cssDir, stripUrlSuffix(decodeRef(target)));
2916
+ const importedAbs = path2.resolve(cssDir, stripUrlSuffix(decodeRef(target)));
3491
2917
  const inlined = await inlineOne(importedAbs, copies, warnings, seen);
3492
2918
  node.replaceWith(wrapImportConditions(node, postcss.parse(inlined, { from: importedAbs })));
3493
2919
  }
@@ -3499,10 +2925,10 @@ async function inlineStyles(projectDir, stylePaths) {
3499
2925
  const seen = new Set;
3500
2926
  const parts = [];
3501
2927
  for (const rel of stylePaths) {
3502
- const abs = path3.resolve(projectDir, rel);
2928
+ const abs = path2.resolve(projectDir, rel);
3503
2929
  const css = await inlineOne(abs, copies, warnings, seen);
3504
2930
  if (css.trim().length > 0) {
3505
- parts.push(`/* ${toPosix(path3.relative(projectDir, abs))} */
2931
+ parts.push(`/* ${toPosix(path2.relative(projectDir, abs))} */
3506
2932
  ${css.trim()}`);
3507
2933
  }
3508
2934
  }
@@ -3518,14 +2944,14 @@ async function collectStyleDependencies(projectDir, stylePaths) {
3518
2944
  const found = new Set;
3519
2945
  const visited = new Set;
3520
2946
  async function walk(cssPath) {
3521
- const abs = path3.resolve(cssPath);
2947
+ const abs = path2.resolve(cssPath);
3522
2948
  if (visited.has(abs))
3523
2949
  return;
3524
2950
  visited.add(abs);
3525
2951
  found.add(abs);
3526
2952
  let source;
3527
2953
  try {
3528
- source = await readFile3(abs, "utf-8");
2954
+ source = await readFile2(abs, "utf-8");
3529
2955
  } catch {
3530
2956
  return;
3531
2957
  }
@@ -3535,14 +2961,14 @@ async function collectStyleDependencies(projectDir, stylePaths) {
3535
2961
  } catch {
3536
2962
  return;
3537
2963
  }
3538
- const cssDir = path3.dirname(abs);
2964
+ const cssDir = path2.dirname(abs);
3539
2965
  root.walkDecls((decl) => {
3540
2966
  if (!decl.value.includes("url("))
3541
2967
  return;
3542
2968
  for (const raw of extractCssUrls(decl.value)) {
3543
2969
  if (!raw || isNonFilesystemRef(raw))
3544
2970
  continue;
3545
- found.add(path3.resolve(cssDir, stripUrlSuffix(decodeRef(raw))));
2971
+ found.add(path2.resolve(cssDir, stripUrlSuffix(decodeRef(raw))));
3546
2972
  }
3547
2973
  });
3548
2974
  const imports = [];
@@ -3552,11 +2978,11 @@ async function collectStyleDependencies(projectDir, stylePaths) {
3552
2978
  imports.push(target);
3553
2979
  });
3554
2980
  for (const target of imports) {
3555
- await walk(path3.resolve(cssDir, stripUrlSuffix(decodeRef(target))));
2981
+ await walk(path2.resolve(cssDir, stripUrlSuffix(decodeRef(target))));
3556
2982
  }
3557
2983
  }
3558
2984
  for (const rel of stylePaths) {
3559
- await walk(path3.resolve(projectDir, rel));
2985
+ await walk(path2.resolve(projectDir, rel));
3560
2986
  }
3561
2987
  return [...found];
3562
2988
  }
@@ -3573,8 +2999,8 @@ async function planImageCopies(projectDir, refs) {
3573
2999
  continue;
3574
3000
  }
3575
3001
  const cleaned = stripUrlSuffix(decodeRef(ref));
3576
- const abs = path3.resolve(projectDir, cleaned);
3577
- const rel = path3.relative(projectDir, abs);
3002
+ const abs = path2.resolve(projectDir, cleaned);
3003
+ const rel = path2.relative(projectDir, abs);
3578
3004
  const dest = toPosix(rel);
3579
3005
  copies.set(dest, { from: abs, to: dest });
3580
3006
  destinations.set(ref, dest);
@@ -3595,18 +3021,18 @@ async function inlineShapeUrls(html, baseDir) {
3595
3021
  if (!raw || isNonFilesystemRef(raw))
3596
3022
  continue;
3597
3023
  const cleaned = stripUrlSuffix(decodeRef(raw));
3598
- if (path3.isAbsolute(cleaned))
3024
+ if (path2.isAbsolute(cleaned))
3599
3025
  continue;
3600
- const abs = path3.resolve(baseDir, cleaned);
3026
+ const abs = path2.resolve(baseDir, cleaned);
3601
3027
  if (escapesProjectRoot(baseDir, abs))
3602
3028
  continue;
3603
3029
  let bytes;
3604
3030
  try {
3605
- bytes = await readFile3(abs);
3031
+ bytes = await readFile2(abs);
3606
3032
  } catch {
3607
3033
  continue;
3608
3034
  }
3609
- const mime = mimeFor(path3.extname(abs).toLowerCase());
3035
+ const mime = mimeFor(path2.extname(abs).toLowerCase());
3610
3036
  const dataUri2 = `data:${mime};base64,${bytes.toString("base64")}`;
3611
3037
  replacements.set(m[0], `--gp-shape:url(&quot;${dataUri2}&quot;)`);
3612
3038
  }
@@ -3623,7 +3049,7 @@ async function inlineShapeUrls(html, baseDir) {
3623
3049
  async function resolveActiveMarkdownFiles(inputDir, configuredFiles) {
3624
3050
  if (configuredFiles && configuredFiles.length > 0)
3625
3051
  return configuredFiles;
3626
- return (await readdir3(inputDir)).filter((f) => f.endsWith(".md") && !isOnlineSibling(f)).sort();
3052
+ return (await readdir2(inputDir)).filter((f) => f.endsWith(".md") && !isOnlineSibling(f)).sort();
3627
3053
  }
3628
3054
  async function renderChapters(inputDir, opts = {}) {
3629
3055
  const styles = await resolveActiveStyles(inputDir, opts.styles);
@@ -3662,7 +3088,7 @@ ${css.trim()}
3662
3088
  }
3663
3089
  return assembleBookHtml({
3664
3090
  files,
3665
- readText: (relPath) => readFile4(join(inputDir, canonicalChapterId(relPath)), "utf-8"),
3091
+ readText: (relPath) => readFile3(join(inputDir, canonicalChapterId(relPath)), "utf-8"),
3666
3092
  projectCss: inlined.css,
3667
3093
  preloadImages: cssAssetCopies.map((c) => c.to),
3668
3094
  title: opts.title,
@@ -3693,9 +3119,9 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
3693
3119
  }
3694
3120
 
3695
3121
  // src/lib/markdown/plugins.ts
3696
- import { createHash as createHash3 } from "node:crypto";
3122
+ import { createHash as createHash2 } from "node:crypto";
3697
3123
  import { existsSync as existsSync4, lstatSync, realpathSync, rmSync, statSync as statSync2, unlinkSync } from "node:fs";
3698
- import { cp, link, mkdir as mkdir2, mkdtemp, readFile as readFile6, rm, unlink, writeFile as writeFile2 } from "node:fs/promises";
3124
+ import { cp, link, mkdir as mkdir2, mkdtemp, readFile as readFile5, rm, unlink, writeFile as writeFile2 } from "node:fs/promises";
3699
3125
  import { tmpdir } from "node:os";
3700
3126
  import { resolve as resolve3, join as join2, dirname as dirname2, basename, extname, relative, isAbsolute } from "node:path";
3701
3127
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -3720,24 +3146,24 @@ function resolveDeclaredStyles(rawStyles, baseDir, subject) {
3720
3146
  }
3721
3147
 
3722
3148
  // src/lib/extension-manifest.ts
3723
- import { readFile as readFile5 } from "node:fs/promises";
3724
- import path4 from "node:path";
3149
+ import { readFile as readFile4 } from "node:fs/promises";
3150
+ import path3 from "node:path";
3725
3151
  var EXTENSION_MANIFEST_FILENAME = "package.json";
3726
3152
  var GUTTERPRESS_KEY = "gutterpress";
3727
- function object2(value) {
3153
+ function object(value) {
3728
3154
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
3729
3155
  }
3730
3156
  function str(value) {
3731
3157
  return typeof value === "string" ? value : undefined;
3732
3158
  }
3733
3159
  function authorName(value) {
3734
- return str(value) ?? str(object2(value)?.name);
3160
+ return str(value) ?? str(object(value)?.name);
3735
3161
  }
3736
3162
  function metaFromPackageJson(raw) {
3737
- const pkg = object2(raw);
3163
+ const pkg = object(raw);
3738
3164
  if (!pkg)
3739
3165
  return {};
3740
- const gp = object2(pkg[GUTTERPRESS_KEY]) ?? {};
3166
+ const gp = object(pkg[GUTTERPRESS_KEY]) ?? {};
3741
3167
  const keywords = Array.isArray(pkg.keywords) ? pkg.keywords.filter((k) => typeof k === "string") : undefined;
3742
3168
  return {
3743
3169
  ...str(pkg.name) ? { name: str(pkg.name) } : {},
@@ -3756,7 +3182,7 @@ function metaFromPackageJson(raw) {
3756
3182
  }
3757
3183
  async function readExtensionMeta(dir) {
3758
3184
  try {
3759
- return metaFromPackageJson(JSON.parse(await readFile5(path4.join(dir, EXTENSION_MANIFEST_FILENAME), "utf8")));
3185
+ return metaFromPackageJson(JSON.parse(await readFile4(path3.join(dir, EXTENSION_MANIFEST_FILENAME), "utf8")));
3760
3186
  } catch {
3761
3187
  return {};
3762
3188
  }
@@ -3779,7 +3205,7 @@ function extensionCarries(meta) {
3779
3205
  };
3780
3206
  }
3781
3207
  function pathEscapesFolder(rel) {
3782
- return path4.isAbsolute(rel) || rel.split(/[\\/]/).includes("..");
3208
+ return path3.isAbsolute(rel) || rel.split(/[\\/]/).includes("..");
3783
3209
  }
3784
3210
  function assertExtensionContained(meta) {
3785
3211
  if (meta.engineStyles !== undefined) {
@@ -4050,9 +3476,9 @@ function isReceiptBoundRequireCall(node) {
4050
3476
  return true;
4051
3477
  if (callee.type !== "MemberExpression")
4052
3478
  return false;
4053
- const object3 = callee.object;
3479
+ const object2 = callee.object;
4054
3480
  const property = memberName(callee);
4055
- return Boolean(object3 && (object3.type === "Identifier" && object3.name === "module" && property === "require" || object3.type === "Identifier" && object3.name === "require" && property === "resolve"));
3481
+ return Boolean(object2 && (object2.type === "Identifier" && object2.name === "module" && property === "require" || object2.type === "Identifier" && object2.name === "require" && property === "resolve"));
4056
3482
  }
4057
3483
  function rewriteCommonJsRequires(source, absolute, pkg, tree) {
4058
3484
  const ast = parseJavaScript(source, {
@@ -4103,7 +3529,7 @@ async function rewriteReachableModuleImports(entryPath, tree) {
4103
3529
  const pkg = packageOwningFile(tree, absolute);
4104
3530
  if (!pkg)
4105
3531
  throw new Error(`Reachable plugin module is outside the verified package tree: ${absolute}`);
4106
- const source = await readFile6(absolute, "utf8");
3532
+ const source = await readFile5(absolute, "utf8");
4107
3533
  const [imports] = parseModuleImports(source, absolute);
4108
3534
  const replacements = [];
4109
3535
  const targets = new Set;
@@ -4210,7 +3636,7 @@ async function createIsolatedVendorTree(verified, address) {
4210
3636
  }
4211
3637
  }
4212
3638
  async function isolateVendoredPlugin(verified) {
4213
- const address = createHash3("sha256").update(verified.receipt.tree.digest).update("\x00").update(JSON.stringify(verified.receipt)).digest("hex");
3639
+ const address = createHash2("sha256").update(verified.receipt.tree.digest).update("\x00").update(JSON.stringify(verified.receipt)).digest("hex");
4214
3640
  let pending = isolatedVendorTrees.get(address);
4215
3641
  if (!pending) {
4216
3642
  pending = createIsolatedVendorTree(verified, address).catch((error2) => {
@@ -4614,7 +4040,7 @@ async function loadPluginsWithCss(configs, baseDir, onError) {
4614
4040
  }
4615
4041
 
4616
4042
  // src/lib/output-paths.ts
4617
- import path5 from "node:path";
4043
+ import path4 from "node:path";
4618
4044
 
4619
4045
  // src/lib/slug.ts
4620
4046
  function slugify(name, fallback = "") {
@@ -4632,7 +4058,7 @@ function bookSlug(title) {
4632
4058
  return slugify(title ?? "", "book");
4633
4059
  }
4634
4060
  function resolveOutputDir(manifestDir, title) {
4635
- return path5.resolve(manifestDir, DIST_DIRNAME, bookSlug(title));
4061
+ return path4.resolve(manifestDir, DIST_DIRNAME, bookSlug(title));
4636
4062
  }
4637
4063
  function artifactName(title, format) {
4638
4064
  return `${bookSlug(title)}-${format}.pdf`;
@@ -4640,7 +4066,7 @@ function artifactName(title, format) {
4640
4066
 
4641
4067
  // src/lib/ghostscript.ts
4642
4068
  import { existsSync as existsSync5 } from "node:fs";
4643
- import { readFile as readFile7, writeFile as writeFile3, unlink as unlink2, rename, readdir as readdir4 } from "node:fs/promises";
4069
+ import { readFile as readFile6, writeFile as writeFile3, unlink as unlink2, rename, readdir as readdir3 } from "node:fs/promises";
4644
4070
  import { join as join3, basename as basename2, resolve as resolve4 } from "node:path";
4645
4071
  var WINDOWS_GHOSTSCRIPT_NAMES = ["gswin64c", "gswin32c", "gs"];
4646
4072
  var POSIX_GHOSTSCRIPT_NAMES = ["gs"];
@@ -4667,7 +4093,7 @@ async function findWindowsGhostscriptInstall(env) {
4667
4093
  const installRoot = join3(root, "gs");
4668
4094
  let versions;
4669
4095
  try {
4670
- versions = (await readdir4(installRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^gs\d/i.test(entry.name)).sort((a, b) => b.name.localeCompare(a.name, undefined, { numeric: true }));
4096
+ versions = (await readdir3(installRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^gs\d/i.test(entry.name)).sort((a, b) => b.name.localeCompare(a.name, undefined, { numeric: true }));
4671
4097
  } catch {
4672
4098
  continue;
4673
4099
  }
@@ -4742,7 +4168,7 @@ async function stripAnnotations(pdfPath, stagingDir) {
4742
4168
  await unlink2(tmp).catch(() => {});
4743
4169
  }
4744
4170
  const { PDFDocument, PDFName } = await import("pdf-lib");
4745
- const bytes = await readFile7(pdfPath);
4171
+ const bytes = await readFile6(pdfPath);
4746
4172
  const doc = await PDFDocument.load(bytes, { updateMetadata: false });
4747
4173
  const annots = PDFName.of("Annots");
4748
4174
  for (const page of doc.getPages())
@@ -4817,7 +4243,7 @@ ${docInfoConformance}/DOCINFO pdfmark
4817
4243
  }
4818
4244
  async function stampCreator(pdfPath) {
4819
4245
  const { PDFDocument } = await import("pdf-lib");
4820
- const bytes = await readFile7(pdfPath);
4246
+ const bytes = await readFile6(pdfPath);
4821
4247
  const doc = await PDFDocument.load(bytes, { updateMetadata: false });
4822
4248
  doc.setCreator("gutterpress");
4823
4249
  const out = await doc.save();
@@ -4868,14 +4294,14 @@ async function convertToPdfxCmyk(inputPdf, outPdf, config) {
4868
4294
 
4869
4295
  // src/lib/build-fingerprint.ts
4870
4296
  import * as fs from "node:fs";
4871
- import { mkdir as mkdir3, readFile as readFile8, writeFile as writeFile4 } from "node:fs/promises";
4872
- import { createHash as createHash4 } from "node:crypto";
4873
- import path6 from "node:path";
4297
+ import { mkdir as mkdir3, readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
4298
+ import { createHash as createHash3 } from "node:crypto";
4299
+ import path5 from "node:path";
4874
4300
  import git from "isomorphic-git";
4875
4301
  // package.json
4876
4302
  var package_default = {
4877
4303
  name: "gutterpress",
4878
- version: "0.11.6-alpha.2",
4304
+ version: "0.11.6-alpha.4",
4879
4305
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
4880
4306
  author: "itlackey",
4881
4307
  license: "MPL-2.0",
@@ -5032,7 +4458,7 @@ async function getGitRevision(sourceDir) {
5032
4458
  const candidateDirs = [sourceDir, process.cwd()].filter((v) => Boolean(v));
5033
4459
  const seen = new Set;
5034
4460
  for (const dir of candidateDirs) {
5035
- const abs = path6.resolve(dir);
4461
+ const abs = path5.resolve(dir);
5036
4462
  if (seen.has(abs))
5037
4463
  continue;
5038
4464
  seen.add(abs);
@@ -5068,10 +4494,10 @@ async function getEngineBundleHash() {
5068
4494
  getAssetPath("engine/gutterpress-agent.js")
5069
4495
  ]);
5070
4496
  const [viewerBytes, agentBytes] = await Promise.all([
5071
- readFile8(viewerPath),
5072
- readFile8(agentPath)
4497
+ readFile7(viewerPath),
4498
+ readFile7(agentPath)
5073
4499
  ]);
5074
- const hash = createHash4("sha256");
4500
+ const hash = createHash3("sha256");
5075
4501
  hash.update(viewerBytes);
5076
4502
  hash.update(agentBytes);
5077
4503
  return hash.digest("hex").slice(0, 12);
@@ -5117,8 +4543,8 @@ function sanitizeArgs(args) {
5117
4543
  return out;
5118
4544
  }
5119
4545
  async function writeBuildFingerprint(input) {
5120
- const outputDir = path6.resolve(input.outputDir);
5121
- const outPath = path6.join(outputDir, FINGERPRINT_FILENAME);
4546
+ const outputDir = path5.resolve(input.outputDir);
4547
+ const outPath = path5.join(outputDir, FINGERPRINT_FILENAME);
5122
4548
  const [tools, sourceRevision] = await Promise.all([
5123
4549
  getToolVersions(),
5124
4550
  getGitRevision(input.sourceDir)
@@ -5134,7 +4560,7 @@ async function writeBuildFingerprint(input) {
5134
4560
  iccPath: input.pdfx.iccPath,
5135
4561
  stripAnnotations: input.pdfx.stripAnnotations
5136
4562
  },
5137
- outputDir: input.recordedOutputDir ? path6.resolve(input.recordedOutputDir) : outputDir
4563
+ outputDir: input.recordedOutputDir ? path5.resolve(input.recordedOutputDir) : outputDir
5138
4564
  },
5139
4565
  sourceRevision,
5140
4566
  tools
@@ -5149,7 +4575,7 @@ import { existsSync as existsSync10 } from "node:fs";
5149
4575
  import { dirname as dirname4, join as join4, resolve as resolve8 } from "node:path";
5150
4576
 
5151
4577
  // src/lib/markdownlint-fix.ts
5152
- import { readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
4578
+ import { readFile as readFile9, writeFile as writeFile5 } from "node:fs/promises";
5153
4579
  import { applyFixes } from "markdownlint";
5154
4580
  import { lint as lint2 } from "markdownlint/sync";
5155
4581
 
@@ -5170,7 +4596,7 @@ function findConfigFile(inputDir, names, explicit) {
5170
4596
  }
5171
4597
 
5172
4598
  // src/checks/source/markdownlint.ts
5173
- import { readFile as readFile9 } from "node:fs/promises";
4599
+ import { readFile as readFile8 } from "node:fs/promises";
5174
4600
  import { lint } from "markdownlint/sync";
5175
4601
  import { parse as parseYaml2 } from "yaml";
5176
4602
 
@@ -5252,7 +4678,7 @@ function stripJsonComments(s) {
5252
4678
  return s.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
5253
4679
  }
5254
4680
  async function loadConfig(configPath) {
5255
- const raw = await readFile9(configPath, "utf8");
4681
+ const raw = await readFile8(configPath, "utf8");
5256
4682
  let parsed;
5257
4683
  if (configPath.endsWith(".json")) {
5258
4684
  parsed = JSON.parse(raw);
@@ -5329,7 +4755,7 @@ async function uncommittedAmong(inputDir, files) {
5329
4755
  async function applyMarkdownlintFixes(ctx) {
5330
4756
  const sourceConfig = ctx.config.validate.source;
5331
4757
  if (sourceConfig.markdownlint === false) {
5332
- log.warn("--fix: source.markdownlint is disabled for this project — no files were fixed.");
4758
+ log.warn("--fix: source.markdownlint is disabled for this book — no files were fixed.");
5333
4759
  return [];
5334
4760
  }
5335
4761
  const files = ctx.markdownFiles;
@@ -5349,7 +4775,7 @@ async function applyMarkdownlintFixes(ctx) {
5349
4775
  for (const [file, violations] of Object.entries(results)) {
5350
4776
  if (!Array.isArray(violations) || violations.length === 0)
5351
4777
  continue;
5352
- const before = await readFile10(file, "utf8");
4778
+ const before = await readFile9(file, "utf8");
5353
4779
  const after = applyFixes(before, violations);
5354
4780
  if (after !== before)
5355
4781
  pending.push({ file, content: after });
@@ -5474,7 +4900,7 @@ function inspectionFailed(checkId, message, extra = {}) {
5474
4900
  }
5475
4901
 
5476
4902
  // src/lib/pdf-inspect.ts
5477
- import { stat, readFile as readFile11 } from "node:fs/promises";
4903
+ import { stat, readFile as readFile10 } from "node:fs/promises";
5478
4904
  import { getDocumentProxy } from "unpdf";
5479
4905
  import * as pdfjs from "unpdf/pdfjs";
5480
4906
  function getOps(mod) {
@@ -5510,17 +4936,17 @@ function evictLru() {
5510
4936
  destroyEntryAfterGrace(evicted);
5511
4937
  }
5512
4938
  }
5513
- async function loadPdf(path7) {
4939
+ async function loadPdf(path6) {
5514
4940
  let st;
5515
4941
  try {
5516
- st = await stat(path7);
4942
+ st = await stat(path6);
5517
4943
  } catch {
5518
4944
  return null;
5519
4945
  }
5520
- const hit = docCache.get(path7);
4946
+ const hit = docCache.get(path6);
5521
4947
  if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) {
5522
- docCache.delete(path7);
5523
- docCache.set(path7, hit);
4948
+ docCache.delete(path6);
4949
+ docCache.set(path6, hit);
5524
4950
  try {
5525
4951
  return await hit.doc;
5526
4952
  } catch {
@@ -5529,15 +4955,15 @@ async function loadPdf(path7) {
5529
4955
  }
5530
4956
  if (hit)
5531
4957
  destroyEntry(hit);
5532
- const docPromise = readFile11(path7).then((buf) => getDocumentProxy(new Uint8Array(buf)));
5533
- docCache.delete(path7);
5534
- docCache.set(path7, { mtimeMs: st.mtimeMs, size: st.size, doc: docPromise });
4958
+ const docPromise = readFile10(path6).then((buf) => getDocumentProxy(new Uint8Array(buf)));
4959
+ docCache.delete(path6);
4960
+ docCache.set(path6, { mtimeMs: st.mtimeMs, size: st.size, doc: docPromise });
5535
4961
  evictLru();
5536
4962
  try {
5537
4963
  return await docPromise;
5538
4964
  } catch {
5539
- if (docCache.get(path7)?.doc === docPromise)
5540
- docCache.delete(path7);
4965
+ if (docCache.get(path6)?.doc === docPromise)
4966
+ docCache.delete(path6);
5541
4967
  return null;
5542
4968
  }
5543
4969
  }
@@ -5988,9 +5414,9 @@ var check4 = {
5988
5414
  registerCheck(check4);
5989
5415
 
5990
5416
  // src/lib/pdf-parse.ts
5991
- import { readFile as readFile12 } from "node:fs/promises";
5417
+ import { readFile as readFile11 } from "node:fs/promises";
5992
5418
  async function readPdfBytes(pdfPath) {
5993
- return readFile12(pdfPath, "latin1");
5419
+ return readFile11(pdfPath, "latin1");
5994
5420
  }
5995
5421
  function parseInkCov(out) {
5996
5422
  const lines = out.split(/\r?\n/).filter((l) => l.includes("CMYK"));
@@ -6562,7 +5988,7 @@ registerCheck(check16);
6562
5988
 
6563
5989
  // src/checks/source/htmlhint.ts
6564
5990
  import { existsSync as existsSync7 } from "node:fs";
6565
- import { readFile as readFile13 } from "node:fs/promises";
5991
+ import { readFile as readFile12 } from "node:fs/promises";
6566
5992
  import htmlhint from "htmlhint";
6567
5993
  var { HTMLHint } = htmlhint;
6568
5994
  var CONFIG_NAMES2 = [".htmlhintrc"];
@@ -6582,8 +6008,8 @@ var check17 = {
6582
6008
  const resolvedConfig = findConfigFile(ctx.inputDir, CONFIG_NAMES2, configPath);
6583
6009
  if (!resolvedConfig && !configPath)
6584
6010
  return [];
6585
- const ruleset = resolvedConfig ? JSON.parse(await readFile13(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
6586
- const html = await readFile13(ctx.htmlPath, "utf8");
6011
+ const ruleset = resolvedConfig ? JSON.parse(await readFile12(resolvedConfig, "utf8")) : HTMLHint.defaultRuleset;
6012
+ const html = await readFile12(ctx.htmlPath, "utf8");
6587
6013
  const messages = HTMLHint.verify(html, ruleset);
6588
6014
  return messages.map((m) => ({
6589
6015
  checkId: check17.id,
@@ -6598,7 +6024,7 @@ var check17 = {
6598
6024
  registerCheck(check17);
6599
6025
 
6600
6026
  // src/checks/source/stylelint.ts
6601
- import { readFile as readFile14 } from "node:fs/promises";
6027
+ import { readFile as readFile13 } from "node:fs/promises";
6602
6028
 
6603
6029
  // src/lib/printsafe.ts
6604
6030
  import postcss2 from "postcss";
@@ -6874,7 +6300,7 @@ var check18 = {
6874
6300
  for (const file of files) {
6875
6301
  let css;
6876
6302
  try {
6877
- css = await readFile14(file, "utf8");
6303
+ css = await readFile13(file, "utf8");
6878
6304
  } catch (err) {
6879
6305
  results.push({
6880
6306
  checkId: check18.id,
@@ -6901,12 +6327,12 @@ var check18 = {
6901
6327
  registerCheck(check18);
6902
6328
 
6903
6329
  // src/checks/source/css-ownership.ts
6904
- import { readFile as readFile15 } from "node:fs/promises";
6330
+ import { readFile as readFile14 } from "node:fs/promises";
6905
6331
  import postcss3 from "postcss";
6906
6332
  import { parse as parseYaml3 } from "yaml";
6907
6333
  var CONFIG_NAMES3 = [".gutterpress/css-contract.yaml", ".gutterpress/css-contract.yml"];
6908
6334
  async function loadContract(configPath) {
6909
- const raw = await readFile15(configPath, "utf8");
6335
+ const raw = await readFile14(configPath, "utf8");
6910
6336
  const parsed = parseYaml3(raw);
6911
6337
  if (parsed && typeof parsed === "object")
6912
6338
  return parsed;
@@ -7057,7 +6483,7 @@ var check19 = {
7057
6483
  continue;
7058
6484
  let css;
7059
6485
  try {
7060
- css = await readFile15(absFile, "utf8");
6486
+ css = await readFile14(absFile, "utf8");
7061
6487
  } catch {
7062
6488
  continue;
7063
6489
  }
@@ -7103,7 +6529,7 @@ import { existsSync as existsSync8 } from "node:fs";
7103
6529
  import { dirname as dirname3, resolve as resolve6 } from "node:path";
7104
6530
 
7105
6531
  // src/checks/source/local-ref-parser.ts
7106
- import { readFile as readFile16 } from "node:fs/promises";
6532
+ import { readFile as readFile15 } from "node:fs/promises";
7107
6533
  function newlineOffsets(content) {
7108
6534
  const offsets = [];
7109
6535
  for (let i = 0;i < content.length; i++)
@@ -7172,7 +6598,7 @@ async function* renderedLocalRefs(ctx, checkId, results) {
7172
6598
  for (const file of files) {
7173
6599
  let refs;
7174
6600
  try {
7175
- refs = collectRenderedLocalRefs(await readFile16(file, "utf8"));
6601
+ refs = collectRenderedLocalRefs(await readFile15(file, "utf8"));
7176
6602
  } catch {
7177
6603
  results.push(inspectionFailed(checkId, `Could not read source file: ${file}`, { file }));
7178
6604
  continue;
@@ -7234,15 +6660,15 @@ function localRefExists(ref, kind, sourceFile, projectRoot) {
7234
6660
  registerCheck(check20);
7235
6661
 
7236
6662
  // src/lib/build-staging.ts
7237
- import path7 from "node:path";
6663
+ import path6 from "node:path";
7238
6664
  import os from "node:os";
7239
6665
  import fsp from "node:fs/promises";
7240
6666
 
7241
6667
  // src/lib/missing-asset-placeholder.ts
7242
- import { createHash as createHash5 } from "node:crypto";
6668
+ import { createHash as createHash4 } from "node:crypto";
7243
6669
  import { deflateSync } from "node:zlib";
7244
6670
  function placeholderOutputPath(missingOutputPath) {
7245
- const hash = createHash5("sha256").update(missingOutputPath).digest("hex").slice(0, 16);
6671
+ const hash = createHash4("sha256").update(missingOutputPath).digest("hex").slice(0, 16);
7246
6672
  return `assets/gutterpress-missing/${hash}.png`;
7247
6673
  }
7248
6674
  function decodeHtmlAttribute(value) {
@@ -7419,8 +6845,8 @@ function placeholderPng(width = 640, height = 480, cell = 32) {
7419
6845
 
7420
6846
  // src/lib/build-staging.ts
7421
6847
  async function shipViewerHtml(htmlFile, outDir) {
7422
- await fsp.mkdir(path7.join(outDir, "engine"), { recursive: true });
7423
- await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"), path7.join(outDir, "engine/gutterpress-viewer.js"));
6848
+ await fsp.mkdir(path6.join(outDir, "engine"), { recursive: true });
6849
+ await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"), path6.join(outDir, "engine/gutterpress-viewer.js"));
7424
6850
  const tag = ` <script src="engine/gutterpress-viewer.js"></script>
7425
6851
  `;
7426
6852
  const html = await fsp.readFile(htmlFile, "utf-8");
@@ -7452,18 +6878,18 @@ async function stageBookAssets(options) {
7452
6878
  return { missing: [...missingPlaceholders.keys()].sort() };
7453
6879
  }
7454
6880
  async function copyReferencedAssets(copies, outDir) {
7455
- const dirs = new Set(copies.map((c) => path7.dirname(path7.resolve(outDir, c.to))));
6881
+ const dirs = new Set(copies.map((c) => path6.dirname(path6.resolve(outDir, c.to))));
7456
6882
  await Promise.all([...dirs].map((d) => fsp.mkdir(d, { recursive: true })));
7457
6883
  const missing = new Map;
7458
6884
  await Promise.all(copies.map(async (c) => {
7459
- const dest = path7.resolve(outDir, c.to);
6885
+ const dest = path6.resolve(outDir, c.to);
7460
6886
  try {
7461
6887
  await fsp.copyFile(c.from, dest);
7462
6888
  } catch (err) {
7463
6889
  if (err?.code === "ENOENT") {
7464
6890
  const placeholder = placeholderOutputPath(c.to);
7465
- const placeholderDest = path7.resolve(outDir, placeholder);
7466
- await fsp.mkdir(path7.dirname(placeholderDest), { recursive: true });
6891
+ const placeholderDest = path6.resolve(outDir, placeholder);
6892
+ await fsp.mkdir(path6.dirname(placeholderDest), { recursive: true });
7467
6893
  await fsp.writeFile(placeholderDest, placeholderPng());
7468
6894
  missing.set(c.to, placeholder);
7469
6895
  return;
@@ -7493,7 +6919,7 @@ function dropRelativeLinkHrefs(html) {
7493
6919
  });
7494
6920
  }
7495
6921
  async function createStageRoot() {
7496
- return fsp.mkdtemp(path7.join(os.tmpdir(), "gutterpress-stage-"));
6922
+ return fsp.mkdtemp(path6.join(os.tmpdir(), "gutterpress-stage-"));
7497
6923
  }
7498
6924
 
7499
6925
  // src/checks/source/dangling-links.ts
@@ -7523,7 +6949,7 @@ var check21 = {
7523
6949
  registerCheck(check21);
7524
6950
 
7525
6951
  // src/checks/source/accessibility-alt-text.ts
7526
- import { readFile as readFile17 } from "node:fs/promises";
6952
+ import { readFile as readFile16 } from "node:fs/promises";
7527
6953
  var check22 = {
7528
6954
  id: "source.accessibility.alt-text",
7529
6955
  name: "Image Alt Text",
@@ -7537,7 +6963,7 @@ var check22 = {
7537
6963
  const results = [];
7538
6964
  for (const file of files) {
7539
6965
  try {
7540
- const content = await readFile17(file, "utf8");
6966
+ const content = await readFile16(file, "utf8");
7541
6967
  const lines = content.split(`
7542
6968
  `);
7543
6969
  let inFence = false;
@@ -7581,7 +7007,7 @@ function extractImageAlts(line) {
7581
7007
  registerCheck(check22);
7582
7008
 
7583
7009
  // src/checks/source/accessibility-heading-order.ts
7584
- import { readFile as readFile18 } from "node:fs/promises";
7010
+ import { readFile as readFile17 } from "node:fs/promises";
7585
7011
  var check23 = {
7586
7012
  id: "source.accessibility.heading-order",
7587
7013
  name: "Heading Order",
@@ -7595,7 +7021,7 @@ var check23 = {
7595
7021
  const results = [];
7596
7022
  for (const file of files) {
7597
7023
  try {
7598
- const content = await readFile18(file, "utf8");
7024
+ const content = await readFile17(file, "utf8");
7599
7025
  const lines = content.split(`
7600
7026
  `);
7601
7027
  let inFence = false;
@@ -7631,7 +7057,7 @@ var check23 = {
7631
7057
  registerCheck(check23);
7632
7058
 
7633
7059
  // src/checks/source/layout-markers.ts
7634
- import { readFile as readFile19 } from "node:fs/promises";
7060
+ import { readFile as readFile18 } from "node:fs/promises";
7635
7061
  var check24 = {
7636
7062
  id: "source.markdown.layout-markers",
7637
7063
  name: "Layout Markers",
@@ -7649,7 +7075,7 @@ var check24 = {
7649
7075
  const md = createMarkdownRenderer(plugins);
7650
7076
  for (const file of files) {
7651
7077
  try {
7652
- const content = await readFile19(file, "utf8");
7078
+ const content = await readFile18(file, "utf8");
7653
7079
  const env = {};
7654
7080
  md.render(content, env);
7655
7081
  for (const w of env.layoutWarnings ?? []) {
@@ -7671,8 +7097,8 @@ var check24 = {
7671
7097
  registerCheck(check24);
7672
7098
 
7673
7099
  // src/checks/source/merge-markers.ts
7674
- import { readFile as readFile20 } from "node:fs/promises";
7675
- import path8 from "node:path";
7100
+ import { readFile as readFile19 } from "node:fs/promises";
7101
+ import path7 from "node:path";
7676
7102
 
7677
7103
  // src/checks/asset/extensions.ts
7678
7104
  var RASTER_INSPECTABLE_EXTS = [
@@ -7713,7 +7139,7 @@ var check25 = {
7713
7139
  for (const file of textFiles) {
7714
7140
  let content;
7715
7141
  try {
7716
- content = await readFile20(file, "utf8");
7142
+ content = await readFile19(file, "utf8");
7717
7143
  } catch {
7718
7144
  results.push(inspectionFailed(check25.id, `Could not read source file: ${file}`, { file }));
7719
7145
  continue;
@@ -7756,11 +7182,11 @@ var check25 = {
7756
7182
  ignore: [...ASSET_SCAN_IGNORE_GLOBS]
7757
7183
  });
7758
7184
  for (const sibling of siblings.sort()) {
7759
- const rel = path8.relative(ctx.inputDir, sibling).split(path8.sep).join("/");
7185
+ const rel = path7.relative(ctx.inputDir, sibling).split(path7.sep).join("/");
7760
7186
  results.push(finding(check25.id, {
7761
7187
  severity: "warning",
7762
7188
  code: "kept-both-versions",
7763
- message: `Two versions of ${withoutOnlineTag(rel)} are in your project — keep the one you want, then delete the other.`,
7189
+ message: `Two versions of ${withoutOnlineTag(rel)} are in your book — keep the one you want, then delete the other.`,
7764
7190
  file: sibling
7765
7191
  }));
7766
7192
  }
@@ -7776,27 +7202,27 @@ registerCheck(check25);
7776
7202
  import { stat as stat3 } from "node:fs/promises";
7777
7203
 
7778
7204
  // src/lib/image-inspect.ts
7779
- import { stat as stat2, readFile as readFile21 } from "node:fs/promises";
7205
+ import { stat as stat2, readFile as readFile20 } from "node:fs/promises";
7780
7206
  var DEFAULT_DPI = 72;
7781
7207
  var cache = new Map;
7782
- async function inspectImage(path9) {
7208
+ async function inspectImage(path8) {
7783
7209
  let st;
7784
7210
  try {
7785
- st = await stat2(path9);
7211
+ st = await stat2(path8);
7786
7212
  } catch {
7787
7213
  return null;
7788
7214
  }
7789
- const hit = cache.get(path9);
7215
+ const hit = cache.get(path8);
7790
7216
  if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
7791
7217
  return hit.info;
7792
7218
  let info2 = null;
7793
7219
  try {
7794
- const buf = await readFile21(path9);
7220
+ const buf = await readFile20(path8);
7795
7221
  info2 = parseImage(buf);
7796
7222
  } catch {
7797
7223
  info2 = null;
7798
7224
  }
7799
- cache.set(path9, { mtimeMs: st.mtimeMs, size: st.size, info: info2 });
7225
+ cache.set(path8, { mtimeMs: st.mtimeMs, size: st.size, info: info2 });
7800
7226
  return info2;
7801
7227
  }
7802
7228
  function parseImage(b) {
@@ -8351,7 +7777,7 @@ var check33 = {
8351
7777
  registerCheck(check33);
8352
7778
 
8353
7779
  // src/checks/heuristic/section-density.ts
8354
- import { readFile as readFile22 } from "node:fs/promises";
7780
+ import { readFile as readFile21 } from "node:fs/promises";
8355
7781
  var check34 = {
8356
7782
  id: "heuristic.chunking.section-density",
8357
7783
  name: "Section Density",
@@ -8368,7 +7794,7 @@ var check34 = {
8368
7794
  const results = [];
8369
7795
  for (const file of files) {
8370
7796
  try {
8371
- const content = await readFile22(file, "utf8");
7797
+ const content = await readFile21(file, "utf8");
8372
7798
  const lines = content.split(`
8373
7799
  `);
8374
7800
  let currentSectionStart = 0;
@@ -9017,21 +8443,21 @@ function splitOutPath(outArg, format) {
9017
8443
  if (typeof outArg !== "string" || outArg.length === 0) {
9018
8444
  return { outDir: undefined, pdfFileOverride: null };
9019
8445
  }
9020
- const resolved = path9.resolve(outArg);
8446
+ const resolved = path8.resolve(outArg);
9021
8447
  if ((format === "pdf" || format === "pdfx") && resolved.toLowerCase().endsWith(".pdf")) {
9022
- return { outDir: path9.dirname(resolved), pdfFileOverride: resolved };
8448
+ return { outDir: path8.dirname(resolved), pdfFileOverride: resolved };
9023
8449
  }
9024
8450
  return { outDir: resolved, pdfFileOverride: null };
9025
8451
  }
9026
8452
  function layoutWarningKey(file, line, message) {
9027
- return `${path9.resolve(file)}\x00${line ?? 0}\x00${message}`;
8453
+ return `${path8.resolve(file)}\x00${line ?? 0}\x00${message}`;
9028
8454
  }
9029
8455
  async function resolveBuildContext(opts) {
9030
8456
  const { format } = opts;
9031
- const inputDir = path9.resolve(opts.inputDir);
8457
+ const inputDir = path8.resolve(opts.inputDir);
9032
8458
  const { manifest, manifestDir, manifestPath } = await loadManifestWithPath(opts.manifestPath ?? inputDir, { explicit: opts.manifestPath !== undefined });
9033
8459
  if (manifestPath === null) {
9034
- throw new UsageError(`No project manifest found in ${inputDir}. Looked for ${MANIFEST_FILENAMES.join(" or ")}. ` + "Run from your project folder or pass that folder with `gutterpress build <project-dir>`. " + "For a custom manifest filename, pass `--manifest <path>`.");
8460
+ throw new UsageError(`No book manifest found in ${inputDir}. Looked for ${MANIFEST_FILENAMES.join(" or ")}. ` + "Run from your book folder or pass that folder with `gutterpress build <book-dir>`. " + "For a custom manifest filename, pass `--manifest <path>`.");
9035
8461
  }
9036
8462
  const pdfxConfigOverride = format === "pdfx" ? {
9037
8463
  flavor: opts.pdfxFlavor,
@@ -9042,10 +8468,10 @@ async function resolveBuildContext(opts) {
9042
8468
  title: opts.title,
9043
8469
  pdfx: pdfxConfigOverride
9044
8470
  }, manifest);
9045
- const target = opts.pdfFileOverride ? { kind: "file", file: path9.resolve(opts.pdfFileOverride) } : opts.outDir ? { kind: "directory", dir: opts.outDir } : { kind: "project", dir: resolveOutputDir(manifestDir, config.title) };
9046
- const outDir = target.kind === "file" ? path9.dirname(target.file) : target.dir;
8471
+ const target = opts.pdfFileOverride ? { kind: "file", file: path8.resolve(opts.pdfFileOverride) } : opts.outDir ? { kind: "directory", dir: opts.outDir } : { kind: "project", dir: resolveOutputDir(manifestDir, config.title) };
8472
+ const outDir = target.kind === "file" ? path8.dirname(target.file) : target.dir;
9047
8473
  const gates = computeGates(format, opts, config);
9048
- const workDir = target.kind === "project" ? path9.join(path9.dirname(target.dir), `.${path9.basename(target.dir)}-build-${randomBytes(6).toString("hex")}`) : path9.join(os2.tmpdir(), `gutterpress-build-${randomBytes(6).toString("hex")}`);
8474
+ const workDir = target.kind === "project" ? path8.join(path8.dirname(target.dir), `.${path8.basename(target.dir)}-build-${randomBytes(6).toString("hex")}`) : path8.join(os2.tmpdir(), `gutterpress-build-${randomBytes(6).toString("hex")}`);
9049
8475
  return {
9050
8476
  opts,
9051
8477
  format,
@@ -9115,7 +8541,7 @@ async function renderBook(ctx) {
9115
8541
  pluginStyles,
9116
8542
  onChapterWarnings: (file, warnings) => {
9117
8543
  for (const w of warnings) {
9118
- const key = layoutWarningKey(path9.resolve(renderDir, file), w.line, w.message);
8544
+ const key = layoutWarningKey(path8.resolve(renderDir, file), w.line, w.message);
9119
8545
  if (!ctx.prevalidatedLayoutWarningKeys.has(key)) {
9120
8546
  log.warn(` ${file}, line ${w.line}: ${w.message}`);
9121
8547
  }
@@ -9154,9 +8580,9 @@ async function renderBook(ctx) {
9154
8580
  return htmlFile;
9155
8581
  }
9156
8582
  async function resolveIccProfile(icc, manifestDir, explicitIccPath) {
9157
- const iccCandidates = path9.isAbsolute(icc) ? [icc] : [path9.resolve(manifestDir, icc), path9.resolve(icc)];
8583
+ const iccCandidates = path8.isAbsolute(icc) ? [icc] : [path8.resolve(manifestDir, icc), path8.resolve(icc)];
9158
8584
  let effectiveIccPath = iccCandidates.find((candidate) => fs2.existsSync(candidate)) ?? null;
9159
- if (!effectiveIccPath && !explicitIccPath && path9.basename(icc) === "CGATS21_CRPC1.icc") {
8585
+ if (!effectiveIccPath && !explicitIccPath && path8.basename(icc) === "CGATS21_CRPC1.icc") {
9160
8586
  effectiveIccPath = await getAssetPath("profiles/CGATS21_CRPC1.icc");
9161
8587
  }
9162
8588
  if (!effectiveIccPath) {
@@ -9172,13 +8598,13 @@ async function finalizeBuild(ctx, fingerprint, wroteMessage, paths, artifactName
9172
8598
  });
9173
8599
  await publishBuild(ctx, artifactName2);
9174
8600
  const delivered = ctx.target.kind === "project" || ctx.target.kind === "directory" && ctx.format === "html";
9175
- const fingerprintPath = delivered ? path9.join(ctx.outDir, path9.basename(workFingerprint)) : null;
8601
+ const fingerprintPath = delivered ? path8.join(ctx.outDir, path8.basename(workFingerprint)) : null;
9176
8602
  log.success(wroteMessage);
9177
8603
  if (fingerprintPath)
9178
8604
  log.info(`Fingerprint: ${fingerprintPath}`);
9179
8605
  return {
9180
8606
  outDir: ctx.outDir,
9181
- htmlPath: delivered ? path9.join(ctx.outDir, path9.relative(ctx.workDir, paths.htmlPath)) : null,
8607
+ htmlPath: delivered ? path8.join(ctx.outDir, path8.relative(ctx.workDir, paths.htmlPath)) : null,
9182
8608
  pdfPath: paths.pdfPath,
9183
8609
  fingerprintPath,
9184
8610
  diagnostics
@@ -9189,7 +8615,7 @@ async function publishToDirectory(workDir, targetDir, format, artifactName2) {
9189
8615
  if (format !== "html") {
9190
8616
  if (!artifactName2)
9191
8617
  throw new BuildError("No artifact to write for this format", 1);
9192
- await fsp2.copyFile(path9.join(workDir, artifactName2), path9.join(targetDir, artifactName2));
8618
+ await fsp2.copyFile(path8.join(workDir, artifactName2), path8.join(targetDir, artifactName2));
9193
8619
  return;
9194
8620
  }
9195
8621
  await fsp2.cp(workDir, targetDir, { recursive: true, force: true });
@@ -9199,8 +8625,8 @@ async function publishBuild(ctx, artifactName2) {
9199
8625
  if (target.kind === "file") {
9200
8626
  if (!artifactName2)
9201
8627
  throw new BuildError("No artifact to write for this format", 1);
9202
- await fsp2.mkdir(path9.dirname(target.file), { recursive: true });
9203
- await fsp2.copyFile(path9.join(workDir, artifactName2), target.file);
8628
+ await fsp2.mkdir(path8.dirname(target.file), { recursive: true });
8629
+ await fsp2.copyFile(path8.join(workDir, artifactName2), target.file);
9204
8630
  return;
9205
8631
  }
9206
8632
  if (target.kind === "directory") {
@@ -9208,12 +8634,12 @@ async function publishBuild(ctx, artifactName2) {
9208
8634
  return;
9209
8635
  }
9210
8636
  for (const name of await listPdfs(target.dir)) {
9211
- if (!fs2.existsSync(path9.join(workDir, name))) {
9212
- await fsp2.rename(path9.join(target.dir, name), path9.join(workDir, name));
8637
+ if (!fs2.existsSync(path8.join(workDir, name))) {
8638
+ await fsp2.rename(path8.join(target.dir, name), path8.join(workDir, name));
9213
8639
  }
9214
8640
  }
9215
8641
  await fsp2.rm(target.dir, { recursive: true, force: true });
9216
- await fsp2.mkdir(path9.dirname(target.dir), { recursive: true });
8642
+ await fsp2.mkdir(path8.dirname(target.dir), { recursive: true });
9217
8643
  await fsp2.rename(workDir, target.dir);
9218
8644
  }
9219
8645
  async function listPdfs(dir) {
@@ -9229,7 +8655,7 @@ class HtmlOutput {
9229
8655
  const { workDir, outDir, inputDir, config, opts } = ctx;
9230
8656
  log.info("Shipping self-contained HTML + viewer bundle (native engine)");
9231
8657
  await shipViewerHtml(htmlFile, workDir);
9232
- await fsp2.writeFile(path9.join(workDir, "index.html"), `<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=book.html"><title>Gutterpress</title></head><body></body></html>
8658
+ await fsp2.writeFile(path8.join(workDir, "index.html"), `<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=book.html"><title>Gutterpress</title></head><body></body></html>
9233
8659
  `, "utf-8");
9234
8660
  return finalizeBuild(ctx, {
9235
8661
  command: "build",
@@ -9242,7 +8668,7 @@ class HtmlOutput {
9242
8668
  iccPath: null,
9243
8669
  stripAnnotations: null
9244
8670
  }
9245
- }, `Wrote: ${path9.join(outDir, BOOK_HTML)}`, { htmlPath: htmlFile, pdfPath: null });
8671
+ }, `Wrote: ${path8.join(outDir, BOOK_HTML)}`, { htmlPath: htmlFile, pdfPath: null });
9246
8672
  }
9247
8673
  }
9248
8674
 
@@ -9251,14 +8677,14 @@ class PdfOutput {
9251
8677
  const { workDir, outDir, inputDir, config, opts, format, manifestDir, gates } = ctx;
9252
8678
  const pdfxMode = format === "pdfx" ? opts.pdfxFlavor ?? config.pdfx.flavor : undefined;
9253
8679
  const pdfName = artifactName(config.title, pdfxMode ? "pdfx" : "pdf");
9254
- const pdfFile = path9.join(workDir, pdfName);
9255
- const reportedPdf = ctx.target.kind === "file" ? ctx.target.file : path9.join(outDir, pdfName);
8680
+ const pdfFile = path8.join(workDir, pdfName);
8681
+ const reportedPdf = ctx.target.kind === "file" ? ctx.target.file : path8.join(outDir, pdfName);
9256
8682
  const stage = await createStageRoot();
9257
8683
  try {
9258
- const rawPdf = pdfxMode ? path9.join(stage, "raw.pdf") : path9.resolve(pdfFile);
9259
- await fsp2.mkdir(path9.dirname(path9.resolve(pdfFile)), { recursive: true });
8684
+ const rawPdf = pdfxMode ? path8.join(stage, "raw.pdf") : path8.resolve(pdfFile);
8685
+ await fsp2.mkdir(path8.dirname(path8.resolve(pdfFile)), { recursive: true });
9260
8686
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
9261
- const { buildNativePdf } = await import("./engine-w5ed5hmb.js");
8687
+ const { buildNativePdf } = await import("./engine-15dfcy85.js");
9262
8688
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
9263
8689
  title: config.title,
9264
8690
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -9289,7 +8715,7 @@ class PdfOutput {
9289
8715
  await stripAnnotations(rawPdf, stage);
9290
8716
  }
9291
8717
  log.info(`Converting to CMYK PDF/X (${pdfxMode})`);
9292
- await convertToPdfxCmyk(rawPdf, path9.resolve(pdfFile), {
8718
+ await convertToPdfxCmyk(rawPdf, path8.resolve(pdfFile), {
9293
8719
  iccPath: effectiveIccPath,
9294
8720
  pdfx: pdfxMode,
9295
8721
  title: config.title,
@@ -9349,10 +8775,10 @@ async function runBuild(opts) {
9349
8775
  }
9350
8776
  }
9351
8777
  // src/preview/lifecycle.ts
9352
- import path11 from "path";
8778
+ import path10 from "path";
9353
8779
  import { tmpdir as tmpdir2 } from "os";
9354
8780
  import { randomBytes as randomBytes2 } from "crypto";
9355
- import { readdir as readdir5, readFile as readFile23, writeFile as writeFile6 } from "node:fs/promises";
8781
+ import { readdir as readdir4, readFile as readFile22, writeFile as writeFile6 } from "node:fs/promises";
9356
8782
 
9357
8783
  // src/utils/file-utils.ts
9358
8784
  import { promises as fs3 } from "fs";
@@ -9374,7 +8800,7 @@ async function mkdir4(dirPath) {
9374
8800
  // src/preview/file-watcher.ts
9375
8801
  import { watch } from "chokidar";
9376
8802
  import fsp3 from "node:fs/promises";
9377
- import path10 from "path";
8803
+ import path9 from "path";
9378
8804
  var EMPTY_BOOK_HTML = `<!doctype html>
9379
8805
  <html lang="en">
9380
8806
  <head>
@@ -9427,7 +8853,7 @@ function injectPreviewScripts(html, pageIsolateChapters) {
9427
8853
  async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
9428
8854
  if (!inputPath) {
9429
8855
  cssAssets.clear();
9430
- await fsp3.writeFile(path10.join(tempDir, BOOK_HTML_FILENAME), EMPTY_BOOK_HTML, "utf-8");
8856
+ await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), EMPTY_BOOK_HTML, "utf-8");
9431
8857
  return;
9432
8858
  }
9433
8859
  const nextAssets = new Map;
@@ -9442,7 +8868,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
9442
8868
  cssAssets.clear();
9443
8869
  for (const [to, from] of nextAssets)
9444
8870
  cssAssets.set(to, from);
9445
- await fsp3.writeFile(path10.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, false), "utf-8");
8871
+ await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, false), "utf-8");
9446
8872
  }
9447
8873
  async function renderChapterPreviewHtml(inputPath, file, config) {
9448
8874
  const html = await renderPreviewBook(inputPath, config, {
@@ -9454,12 +8880,12 @@ async function renderChapterPreviewHtml(inputPath, file, config) {
9454
8880
  function describeChanges(changes, inputResolved) {
9455
8881
  const files = [];
9456
8882
  for (const [changedPath, event] of changes) {
9457
- const relative2 = path10.relative(inputResolved, path10.resolve(changedPath));
9458
- if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative2))
8883
+ const relative2 = path9.relative(inputResolved, path9.resolve(changedPath));
8884
+ if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${path9.sep}`) || path9.isAbsolute(relative2))
9459
8885
  continue;
9460
8886
  files.push({
9461
8887
  relativePath: canonicalChapterId(relative2),
9462
- ext: path10.extname(changedPath).toLowerCase(),
8888
+ ext: path9.extname(changedPath).toLowerCase(),
9463
8889
  event
9464
8890
  });
9465
8891
  }
@@ -9511,23 +8937,23 @@ function isIgnoredWatchPath(candidatePath, projectRoot) {
9511
8937
  return isDotPathUnderRoot(candidatePath, projectRoot) || isGeneratedProjectPath(relative2);
9512
8938
  }
9513
8939
  function isExternalWatchCandidate(candidatePath, targets) {
9514
- const candidate = path10.resolve(candidatePath);
8940
+ const candidate = path9.resolve(candidatePath);
9515
8941
  for (const rawTarget of targets) {
9516
- const target = path10.resolve(rawTarget);
9517
- const rel = path10.relative(candidate, target);
9518
- if (rel === "" || rel !== ".." && !rel.startsWith(`..${path10.sep}`) && !path10.isAbsolute(rel))
8942
+ const target = path9.resolve(rawTarget);
8943
+ const rel = path9.relative(candidate, target);
8944
+ if (rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel))
9519
8945
  return true;
9520
8946
  }
9521
8947
  return false;
9522
8948
  }
9523
8949
  async function nearestExistingDirectory(start) {
9524
- let current = path10.resolve(start);
8950
+ let current = path9.resolve(start);
9525
8951
  while (true) {
9526
8952
  try {
9527
8953
  if ((await fsp3.stat(current)).isDirectory())
9528
8954
  return current;
9529
8955
  } catch {}
9530
- const parent = path10.dirname(current);
8956
+ const parent = path9.dirname(current);
9531
8957
  if (parent === current)
9532
8958
  return current;
9533
8959
  current = parent;
@@ -9536,7 +8962,7 @@ async function nearestExistingDirectory(start) {
9536
8962
  async function externalWatchRoots(targets) {
9537
8963
  const roots = new Set;
9538
8964
  for (const target of targets) {
9539
- roots.add(await nearestExistingDirectory(path10.dirname(path10.resolve(target))));
8965
+ roots.add(await nearestExistingDirectory(path9.dirname(path9.resolve(target))));
9540
8966
  }
9541
8967
  return [...roots];
9542
8968
  }
@@ -9550,19 +8976,19 @@ async function extensionFolderFiles(dir) {
9550
8976
  const meta = await readExtensionMeta(dir);
9551
8977
  const entry = extensionEntry(meta);
9552
8978
  return [
9553
- path10.join(dir, EXTENSION_MANIFEST_FILENAME),
9554
- ...entry ? [path10.resolve(dir, entry)] : [],
8979
+ path9.join(dir, EXTENSION_MANIFEST_FILENAME),
8980
+ ...entry ? [path9.resolve(dir, entry)] : [],
9555
8981
  ...await collectStyleDependencies(dir, extensionStyleList(meta))
9556
8982
  ];
9557
8983
  }
9558
8984
  async function externalWatchTargets(projectDir, config) {
9559
- const root = path10.resolve(projectDir);
8985
+ const root = path9.resolve(projectDir);
9560
8986
  let canonicalRoot = root;
9561
8987
  try {
9562
8988
  canonicalRoot = await fsp3.realpath(root);
9563
8989
  } catch {}
9564
8990
  const styles = await resolveActiveStyles(root, config.styles);
9565
- const extensionPaths = (config.extensions ?? []).map((p) => p.path).filter((p) => !!p).map((p) => path10.resolve(root, p));
8991
+ const extensionPaths = (config.extensions ?? []).map((p) => p.path).filter((p) => !!p).map((p) => path9.resolve(root, p));
9566
8992
  const candidates = [
9567
8993
  ...await collectStyleDependencies(root, styles),
9568
8994
  ...extensionPaths,
@@ -9570,17 +8996,17 @@ async function externalWatchTargets(projectDir, config) {
9570
8996
  ];
9571
8997
  const external = new Set;
9572
8998
  for (const abs of candidates) {
9573
- const authoredPath = path10.resolve(abs);
9574
- const authoredRel = path10.relative(root, authoredPath);
9575
- if (authoredRel !== "" && (authoredRel === ".." || authoredRel.startsWith(`..${path10.sep}`) || path10.isAbsolute(authoredRel))) {
8999
+ const authoredPath = path9.resolve(abs);
9000
+ const authoredRel = path9.relative(root, authoredPath);
9001
+ if (authoredRel !== "" && (authoredRel === ".." || authoredRel.startsWith(`..${path9.sep}`) || path9.isAbsolute(authoredRel))) {
9576
9002
  external.add(authoredPath);
9577
9003
  }
9578
9004
  let canonicalPath = authoredPath;
9579
9005
  try {
9580
9006
  canonicalPath = await fsp3.realpath(canonicalPath);
9581
9007
  } catch {}
9582
- const rel = path10.relative(canonicalRoot, canonicalPath);
9583
- if (rel === "" || rel !== ".." && !rel.startsWith(`..${path10.sep}`) && !path10.isAbsolute(rel))
9008
+ const rel = path9.relative(canonicalRoot, canonicalPath);
9009
+ if (rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel))
9584
9010
  continue;
9585
9011
  external.add(canonicalPath);
9586
9012
  }
@@ -9607,7 +9033,7 @@ async function addAndAwaitWatch(watcher, roots) {
9607
9033
  }
9608
9034
  }
9609
9035
  function createFileWatcher(state) {
9610
- const inputResolved = path10.resolve(state.currentInputPath);
9036
+ const inputResolved = path9.resolve(state.currentInputPath);
9611
9037
  const watcher = watch(inputResolved, {
9612
9038
  persistent: true,
9613
9039
  ignoreInitial: true,
@@ -9647,7 +9073,7 @@ function createFileWatcher(state) {
9647
9073
  async function recordChange(event, filePath) {
9648
9074
  if (closed)
9649
9075
  return;
9650
- const target = path10.resolve(filePath);
9076
+ const target = path9.resolve(filePath);
9651
9077
  const settled = settledWrites.get(target);
9652
9078
  if (settled && event !== "unlink" && Date.now() <= settled.expiresAt) {
9653
9079
  const diskContent = await fsp3.readFile(target, "utf8").catch(() => null);
@@ -9668,7 +9094,7 @@ function createFileWatcher(state) {
9668
9094
  recordChange(event, filePath);
9669
9095
  });
9670
9096
  externalWatcher.on("all", (event, filePath) => {
9671
- const target = path10.resolve(filePath);
9097
+ const target = path9.resolve(filePath);
9672
9098
  if (!desiredExternals.has(target))
9673
9099
  return;
9674
9100
  if (event === "add" && suppressInitialExternalAdds.delete(target))
@@ -9676,8 +9102,8 @@ function createFileWatcher(state) {
9676
9102
  recordChange(event, target);
9677
9103
  });
9678
9104
  function isWatchedSource(target) {
9679
- const rel = path10.relative(inputResolved, target);
9680
- const inProject = rel === "" || rel !== ".." && !rel.startsWith(`..${path10.sep}`) && !path10.isAbsolute(rel);
9105
+ const rel = path9.relative(inputResolved, target);
9106
+ const inProject = rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel);
9681
9107
  if (inProject)
9682
9108
  return !isIgnoredWatchPath(target, inputResolved);
9683
9109
  return desiredExternals.has(target);
@@ -9702,7 +9128,7 @@ function createFileWatcher(state) {
9702
9128
  state.notifySettledWrite = (filePath, writtenContent) => {
9703
9129
  if (closed)
9704
9130
  return;
9705
- const target = path10.resolve(filePath);
9131
+ const target = path9.resolve(filePath);
9706
9132
  if (isWatchedSource(target)) {
9707
9133
  acceptSettledWrite(target, writtenContent);
9708
9134
  return;
@@ -9723,7 +9149,7 @@ function createFileWatcher(state) {
9723
9149
  return;
9724
9150
  let nextTargets;
9725
9151
  try {
9726
- nextTargets = new Set((await externalWatchTargets(inputResolved, state.config)).map((target) => path10.resolve(target)));
9152
+ nextTargets = new Set((await externalWatchTargets(inputResolved, state.config)).map((target) => path9.resolve(target)));
9727
9153
  } catch (err) {
9728
9154
  debug(`Could not resolve external watch targets: ${err}`);
9729
9155
  return;
@@ -9855,7 +9281,7 @@ async function stopFileWatcher(state) {
9855
9281
  }
9856
9282
 
9857
9283
  // src/preview/lifecycle.ts
9858
- var TEMP_DIR_BASE = path11.join(tmpdir2(), "gutterpress-preview");
9284
+ var TEMP_DIR_BASE = path10.join(tmpdir2(), "gutterpress-preview");
9859
9285
  var PID_FILE_NAME = ".gutterpress.pid";
9860
9286
  function isProcessAlive(pid) {
9861
9287
  if (!Number.isFinite(pid) || pid <= 0)
@@ -9870,15 +9296,15 @@ function isProcessAlive(pid) {
9870
9296
  async function cleanupOrphanTempDirs() {
9871
9297
  let entries;
9872
9298
  try {
9873
- entries = await readdir5(TEMP_DIR_BASE);
9299
+ entries = await readdir4(TEMP_DIR_BASE);
9874
9300
  } catch {
9875
9301
  return;
9876
9302
  }
9877
9303
  for (const entry of entries) {
9878
- const dir = path11.join(TEMP_DIR_BASE, entry);
9879
- const pidFile = path11.join(dir, PID_FILE_NAME);
9304
+ const dir = path10.join(TEMP_DIR_BASE, entry);
9305
+ const pidFile = path10.join(dir, PID_FILE_NAME);
9880
9306
  try {
9881
- const raw = await readFile23(pidFile, "utf8");
9307
+ const raw = await readFile22(pidFile, "utf8");
9882
9308
  const pid = parseInt(raw.trim(), 10);
9883
9309
  if (!isProcessAlive(pid)) {
9884
9310
  debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
@@ -9890,10 +9316,10 @@ async function cleanupOrphanTempDirs() {
9890
9316
  async function initializePreviewDirectories() {
9891
9317
  await cleanupOrphanTempDirs();
9892
9318
  const tempDirSuffix = randomBytes2(8).toString("hex");
9893
- const tempDir = path11.join(TEMP_DIR_BASE, tempDirSuffix);
9319
+ const tempDir = path10.join(TEMP_DIR_BASE, tempDirSuffix);
9894
9320
  await mkdir4(tempDir);
9895
9321
  debug(`Created temporary directory: ${tempDir}`);
9896
- await writeFile6(path11.join(tempDir, PID_FILE_NAME), `${process.pid}
9322
+ await writeFile6(path10.join(tempDir, PID_FILE_NAME), `${process.pid}
9897
9323
  `, "utf8");
9898
9324
  return tempDir;
9899
9325
  }
@@ -9983,9 +9409,9 @@ function createServerState(inputPath, tempDir, config, options) {
9983
9409
  // src/preview/http-server.ts
9984
9410
  import http from "node:http";
9985
9411
  import net from "node:net";
9986
- import { createHash as createHash6, randomUUID } from "node:crypto";
9987
- import { readFile as readFile24, stat as stat4 } from "node:fs/promises";
9988
- import path13 from "path";
9412
+ import { createHash as createHash5, randomUUID } from "node:crypto";
9413
+ import { readFile as readFile23, stat as stat4 } from "node:fs/promises";
9414
+ import path12 from "path";
9989
9415
  import { WebSocket, WebSocketServer } from "ws";
9990
9416
 
9991
9417
  // src/lib/open-path.ts
@@ -10017,7 +9443,7 @@ function openPath(filePath) {
10017
9443
  }
10018
9444
 
10019
9445
  // src/lib/static-serve.ts
10020
- import path12 from "node:path";
9446
+ import path11 from "node:path";
10021
9447
  var STATIC_MIME = {
10022
9448
  ".html": "text/html; charset=utf-8",
10023
9449
  ".htm": "text/html; charset=utf-8",
@@ -10039,9 +9465,9 @@ var STATIC_MIME = {
10039
9465
  ".otf": "font/otf"
10040
9466
  };
10041
9467
  function resolveWithinRoot(relPath, root) {
10042
- const resolvedRoot = path12.resolve(root);
10043
- const candidate = path12.resolve(resolvedRoot, "." + (relPath.startsWith("/") ? relPath : "/" + relPath));
10044
- if (candidate !== resolvedRoot && !candidate.startsWith(resolvedRoot + path12.sep)) {
9468
+ const resolvedRoot = path11.resolve(root);
9469
+ const candidate = path11.resolve(resolvedRoot, "." + (relPath.startsWith("/") ? relPath : "/" + relPath));
9470
+ if (candidate !== resolvedRoot && !candidate.startsWith(resolvedRoot + path11.sep)) {
10045
9471
  return null;
10046
9472
  }
10047
9473
  return candidate;
@@ -10294,7 +9720,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
10294
9720
  try {
10295
9721
  const s = await stat4(filePath);
10296
9722
  if (s.isDirectory())
10297
- filePath = path13.join(filePath, "index.html");
9723
+ filePath = path12.join(filePath, "index.html");
10298
9724
  } catch {
10299
9725
  res.writeHead(404);
10300
9726
  res.end("Not Found");
@@ -10302,7 +9728,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
10302
9728
  }
10303
9729
  let data;
10304
9730
  try {
10305
- data = await readFile24(filePath);
9731
+ data = await readFile23(filePath);
10306
9732
  } catch {
10307
9733
  res.writeHead(404);
10308
9734
  res.end("Not Found");
@@ -10315,7 +9741,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
10315
9741
  res.end(withHmr);
10316
9742
  return;
10317
9743
  }
10318
- const ct = STATIC_MIME[path13.extname(filePath).toLowerCase()] ?? "application/octet-stream";
9744
+ const ct = STATIC_MIME[path12.extname(filePath).toLowerCase()] ?? "application/octet-stream";
10319
9745
  res.writeHead(200, { "Content-Type": ct, "Cache-Control": cacheControl, ...extraHeaders });
10320
9746
  res.end(data);
10321
9747
  }
@@ -10325,7 +9751,7 @@ var EMBEDDED_CACHE_CONTROL = "public, no-cache";
10325
9751
  var EMBEDDED_ETAG = `"gutterpress-${PACKAGE_VERSION}"`;
10326
9752
  var PROJECT_ASSET_CACHE_CONTROL = "private, no-cache";
10327
9753
  async function serveRevalidatedStatic(req, absPath, res, validators) {
10328
- const extension = path13.extname(absPath).toLowerCase();
9754
+ const extension = path12.extname(absPath).toLowerCase();
10329
9755
  if (extension === ".html" || extension === ".htm") {
10330
9756
  await serveStatic(absPath, res);
10331
9757
  return;
@@ -10352,12 +9778,12 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
10352
9778
  let data;
10353
9779
  if (!validator || validator.stamp !== stamp) {
10354
9780
  try {
10355
- data = await readFile24(absPath);
9781
+ data = await readFile23(absPath);
10356
9782
  } catch {
10357
9783
  await serveStatic(absPath, res);
10358
9784
  return;
10359
9785
  }
10360
- const digest = createHash6("sha256").update(data).digest("base64url");
9786
+ const digest = createHash5("sha256").update(data).digest("base64url");
10361
9787
  validator = { stamp, etag: `"gutterpress-file-${digest}"` };
10362
9788
  validators.set(absPath, validator);
10363
9789
  }
@@ -10491,7 +9917,7 @@ async function createPreviewServer(state, port) {
10491
9917
  const file = rawFile ? canonicalChapterId(rawFile) : "";
10492
9918
  const revision = Number(url.searchParams.get("revision"));
10493
9919
  const configuredFiles = state.config.source?.files;
10494
- if (!file || !state.currentInputPath || !incrementalPreviewEnabled() || !Number.isSafeInteger(revision) || revision !== reloadRevision || path13.extname(file).toLowerCase() !== ".md" || hasDotSegment(file) || !resolveWithinRoot(file, state.currentInputPath) || Array.isArray(configuredFiles) && configuredFiles.length > 0 && !configuredFiles.some((configured) => canonicalChapterId(configured) === file)) {
9920
+ if (!file || !state.currentInputPath || !incrementalPreviewEnabled() || !Number.isSafeInteger(revision) || revision !== reloadRevision || path12.extname(file).toLowerCase() !== ".md" || hasDotSegment(file) || !resolveWithinRoot(file, state.currentInputPath) || Array.isArray(configuredFiles) && configuredFiles.length > 0 && !configuredFiles.some((configured) => canonicalChapterId(configured) === file)) {
10495
9921
  res.writeHead(400);
10496
9922
  res.end("Bad Request");
10497
9923
  return;
@@ -10693,9 +10119,9 @@ async function startPreviewServer(options) {
10693
10119
  import { platform, arch, release } from "node:os";
10694
10120
 
10695
10121
  // src/lib/remote-auth/token-store.ts
10696
- import { chmod, mkdir as mkdir5, readFile as readFile25, rename as rename2, writeFile as writeFile7 } from "node:fs/promises";
10122
+ import { chmod, mkdir as mkdir5, readFile as readFile24, rename as rename2, writeFile as writeFile7 } from "node:fs/promises";
10697
10123
  import os3 from "node:os";
10698
- import path14 from "node:path";
10124
+ import path13 from "node:path";
10699
10125
  function credentialHostKey(hostOrUrl) {
10700
10126
  const trimmed = String(hostOrUrl ?? "").trim();
10701
10127
  if (!trimmed)
@@ -10722,22 +10148,22 @@ function defaultConfigDir() {
10722
10148
  if (override)
10723
10149
  return override;
10724
10150
  if (process.platform === "win32" && process.env.APPDATA) {
10725
- return path14.join(process.env.APPDATA, "gutterpress");
10151
+ return path13.join(process.env.APPDATA, "gutterpress");
10726
10152
  }
10727
10153
  const xdg = process.env.XDG_CONFIG_HOME?.trim();
10728
- return path14.join(xdg || path14.join(os3.homedir(), ".config"), "gutterpress");
10154
+ return path13.join(xdg || path13.join(os3.homedir(), ".config"), "gutterpress");
10729
10155
  }
10730
10156
 
10731
10157
  class FileTokenStore {
10732
10158
  filePath;
10733
10159
  queue = Promise.resolve();
10734
10160
  constructor(filePath) {
10735
- this.filePath = filePath ?? path14.join(defaultConfigDir(), "credentials.json");
10161
+ this.filePath = filePath ?? path13.join(defaultConfigDir(), "credentials.json");
10736
10162
  }
10737
10163
  async read() {
10738
10164
  let raw;
10739
10165
  try {
10740
- raw = await readFile25(this.filePath, "utf8");
10166
+ raw = await readFile24(this.filePath, "utf8");
10741
10167
  } catch (e) {
10742
10168
  if (e?.code === "ENOENT") {
10743
10169
  return { version: 1, credentials: {} };
@@ -10757,7 +10183,7 @@ class FileTokenStore {
10757
10183
  return { version: 1, credentials: {} };
10758
10184
  }
10759
10185
  async write(data) {
10760
- await mkdir5(path14.dirname(this.filePath), { recursive: true });
10186
+ await mkdir5(path13.dirname(this.filePath), { recursive: true });
10761
10187
  const tmpPath = `${this.filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
10762
10188
  await writeFile7(tmpPath, JSON.stringify(data, null, 2), {
10763
10189
  encoding: "utf8",
@@ -10906,18 +10332,18 @@ async function getSystemDiagnostics() {
10906
10332
  installHint: INSTALL_HINTS3[t.hintKey]
10907
10333
  };
10908
10334
  }
10909
- const [path15, available] = await Promise.all([
10335
+ const [path14, available] = await Promise.all([
10910
10336
  findTool(t.bin),
10911
10337
  isToolAvailable(t.bin)
10912
10338
  ]);
10913
- const found = available || !!path15;
10339
+ const found = available || !!path14;
10914
10340
  const version = found ? await getVersion(t.bin) : undefined;
10915
10341
  return {
10916
10342
  id: t.id,
10917
10343
  name: t.name,
10918
10344
  bin: t.bin,
10919
10345
  found,
10920
- path: path15,
10346
+ path: path14,
10921
10347
  version,
10922
10348
  usedBy: t.usedBy,
10923
10349
  installHint: INSTALL_HINTS3[t.hintKey]
@@ -10937,25 +10363,25 @@ async function getSystemDiagnostics() {
10937
10363
  };
10938
10364
  }
10939
10365
  // src/lib/project-scaffold.ts
10940
- import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir6, readFile as readFile27, stat as stat5, writeFile as writeFile8 } from "node:fs/promises";
10366
+ import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir5, readFile as readFile26, stat as stat5, writeFile as writeFile8 } from "node:fs/promises";
10941
10367
  import { constants as FS, existsSync as existsSync12 } from "node:fs";
10942
- import path16 from "node:path";
10368
+ import path15 from "node:path";
10943
10369
 
10944
10370
  // src/lib/manifest-doc.ts
10945
10371
  import { existsSync as existsSync11 } from "node:fs";
10946
10372
  import { randomUUID as randomUUID2 } from "node:crypto";
10947
- import { mkdir as mkdir6, open, readFile as readFile26, rename as rename3, rm as rm2 } from "node:fs/promises";
10948
- import path15 from "node:path";
10373
+ import { mkdir as mkdir6, open, readFile as readFile25, rename as rename3, rm as rm2 } from "node:fs/promises";
10374
+ import path14 from "node:path";
10949
10375
  import { parseDocument, isSeq, YAMLSeq } from "yaml";
10950
10376
  function resolveManifestPath(projectDir) {
10951
- const existing = MANIFEST_FILENAMES.find((name) => existsSync11(path15.join(projectDir, name)));
10952
- return path15.join(projectDir, existing ?? MANIFEST_FILENAMES[0]);
10377
+ const existing = MANIFEST_FILENAMES.find((name) => existsSync11(path14.join(projectDir, name)));
10378
+ return path14.join(projectDir, existing ?? MANIFEST_FILENAMES[0]);
10953
10379
  }
10954
10380
  async function loadManifestDoc(projectDir) {
10955
10381
  const file = resolveManifestPath(projectDir);
10956
10382
  let text = "";
10957
10383
  try {
10958
- text = await readFile26(file, "utf8");
10384
+ text = await readFile25(file, "utf8");
10959
10385
  } catch (error2) {
10960
10386
  if (error2.code !== "ENOENT")
10961
10387
  throw error2;
@@ -10963,9 +10389,9 @@ async function loadManifestDoc(projectDir) {
10963
10389
  return { doc: parseDocument(text), file };
10964
10390
  }
10965
10391
  async function writeManifestDoc(file, doc) {
10966
- const dir = path15.dirname(file);
10392
+ const dir = path14.dirname(file);
10967
10393
  await mkdir6(dir, { recursive: true });
10968
- const temporary = path15.join(dir, `.${path15.basename(file)}.tmp-${process.pid}-${randomUUID2()}`);
10394
+ const temporary = path14.join(dir, `.${path14.basename(file)}.tmp-${process.pid}-${randomUUID2()}`);
10969
10395
  try {
10970
10396
  const handle = await open(temporary, "wx", 438);
10971
10397
  try {
@@ -11066,7 +10492,7 @@ function resolveScaffoldTargets(requested, preset) {
11066
10492
  async function scaffoldProject(options) {
11067
10493
  const name = (options.name ?? "").trim();
11068
10494
  if (!name) {
11069
- throw new CreateProjectErrorImpl("invalid-name", "A project name is required.");
10495
+ throw new CreateProjectErrorImpl("invalid-name", "A book name is required.");
11070
10496
  }
11071
10497
  const slug = slugifyProjectName(name);
11072
10498
  const folderName = (options.folderName ?? slug).trim();
@@ -11074,7 +10500,7 @@ async function scaffoldProject(options) {
11074
10500
  throw new CreateProjectErrorImpl("invalid-name", `Could not derive a valid folder name from "${name}".`);
11075
10501
  }
11076
10502
  const parentDir = options.parentDir;
11077
- if (!parentDir || !path16.isAbsolute(parentDir)) {
10503
+ if (!parentDir || !path15.isAbsolute(parentDir)) {
11078
10504
  throw new CreateProjectErrorImpl("parent-not-writable", "The save location must be an absolute path.");
11079
10505
  }
11080
10506
  try {
@@ -11082,7 +10508,7 @@ async function scaffoldProject(options) {
11082
10508
  } catch {
11083
10509
  throw new CreateProjectErrorImpl("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
11084
10510
  }
11085
- const projectDir = path16.join(parentDir, folderName);
10511
+ const projectDir = path15.join(parentDir, folderName);
11086
10512
  let targetExists = true;
11087
10513
  try {
11088
10514
  await access(projectDir, FS.F_OK);
@@ -11099,37 +10525,37 @@ async function scaffoldProject(options) {
11099
10525
  try {
11100
10526
  await mkdir7(projectDir, { recursive: true });
11101
10527
  if (customTemplateDir) {
11102
- const entries = await readdir6(customTemplateDir, { withFileTypes: true });
10528
+ const entries = await readdir5(customTemplateDir, { withFileTypes: true });
11103
10529
  for (const entry of entries) {
11104
10530
  if (entry.name === ".gutterpress-template.json")
11105
10531
  continue;
11106
- await cp2(path16.join(customTemplateDir, entry.name), path16.join(projectDir, entry.name), { recursive: true });
10532
+ await cp2(path15.join(customTemplateDir, entry.name), path15.join(projectDir, entry.name), { recursive: true });
11107
10533
  }
11108
- await mkdir7(path16.join(projectDir, "assets"), { recursive: true });
10534
+ await mkdir7(path15.join(projectDir, "assets"), { recursive: true });
11109
10535
  } else {
11110
10536
  const tplManifest = await getAssetPath(`templates/${template}/manifest.yaml`);
11111
10537
  const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
11112
- await mkdir7(path16.join(projectDir, "assets"), { recursive: true });
11113
- await copyFile(tplManifest, path16.join(projectDir, MANIFEST_FILENAMES[0]));
11114
- await copyFile(tplChapter, path16.join(projectDir, "chapter-01.md"));
11115
- await mkdir7(path16.join(projectDir, "styles"), { recursive: true });
11116
- await writeFile8(path16.join(projectDir, "styles", "book.css"), starterOverridesCss(), "utf8");
10538
+ await mkdir7(path15.join(projectDir, "assets"), { recursive: true });
10539
+ await copyFile(tplManifest, path15.join(projectDir, MANIFEST_FILENAMES[0]));
10540
+ await copyFile(tplChapter, path15.join(projectDir, "chapter-01.md"));
10541
+ await mkdir7(path15.join(projectDir, "styles"), { recursive: true });
10542
+ await writeFile8(path15.join(projectDir, "styles", "book.css"), starterOverridesCss(), "utf8");
11117
10543
  }
11118
10544
  } catch (e) {
11119
- throw new CreateProjectErrorImpl("scaffold-io", `Could not create the project files: ${e instanceof Error ? e.message : String(e)}`);
10545
+ throw new CreateProjectErrorImpl("scaffold-io", `Could not create the book files: ${e instanceof Error ? e.message : String(e)}`);
11120
10546
  }
11121
10547
  try {
11122
10548
  await ensureGitignoreHasDist(projectDir);
11123
10549
  } catch (e) {
11124
10550
  throw new CreateProjectErrorImpl("scaffold-io", `Could not write .gitignore: ${e instanceof Error ? e.message : String(e)}`);
11125
10551
  }
11126
- const manifestPath = MANIFEST_FILENAMES.map((name2) => path16.join(projectDir, name2)).find(existsSync12) ?? path16.join(projectDir, MANIFEST_FILENAMES[0]);
10552
+ const manifestPath = MANIFEST_FILENAMES.map((name2) => path15.join(projectDir, name2)).find(existsSync12) ?? path15.join(projectDir, MANIFEST_FILENAMES[0]);
11127
10553
  const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
11128
10554
  const substitutions = {
11129
10555
  "{{TITLE}}": escapeYamlScalar(name),
11130
10556
  "{{AUTHOR}}": escapeYamlScalar(author)
11131
10557
  };
11132
- let openFile = path16.join(projectDir, "chapter-01.md");
10558
+ let openFile = path15.join(projectDir, "chapter-01.md");
11133
10559
  try {
11134
10560
  await fillTemplateFile(manifestPath, substitutions);
11135
10561
  if (preset) {
@@ -11150,17 +10576,17 @@ async function scaffoldProject(options) {
11150
10576
  }
11151
10577
  const firstSource = await firstSourceFile(manifestPath);
11152
10578
  if (firstSource)
11153
- openFile = path16.join(projectDir, firstSource);
10579
+ openFile = path15.join(projectDir, firstSource);
11154
10580
  await fillTemplateFile(openFile, { "{{TITLE}}": name }).catch(() => {});
11155
10581
  } catch (e) {
11156
- throw new CreateProjectErrorImpl("scaffold-io", `Could not finalise the project files: ${e instanceof Error ? e.message : String(e)}`);
10582
+ throw new CreateProjectErrorImpl("scaffold-io", `Could not finalize the book files: ${e instanceof Error ? e.message : String(e)}`);
11157
10583
  }
11158
10584
  const requested = options.versionHistory ?? "local-git";
11159
10585
  let versionHistory = "none";
11160
10586
  let versionHistoryError;
11161
10587
  if (requested === "local-git") {
11162
10588
  try {
11163
- const { providerFor } = await import("./source-provider-a458mmec.js");
10589
+ const { providerFor } = await import("./source-provider-m5wes47h.js");
11164
10590
  const provider = providerFor({ type: "local-folder", path: projectDir });
11165
10591
  await provider.initVersionHistory({
11166
10592
  projectDir,
@@ -11172,7 +10598,7 @@ async function scaffoldProject(options) {
11172
10598
  versionHistory = "none";
11173
10599
  const { findEnclosingRepoDir } = await import("./project-source-ekcyp63q.js");
11174
10600
  const enclosingRepoDir = await findEnclosingRepoDir(projectDir);
11175
- versionHistoryError = enclosingRepoDir ? `This book was added to ${path16.basename(enclosingRepoDir)}'s shared version history; save a snapshot to record it.` : e instanceof Error ? e.message : String(e);
10601
+ versionHistoryError = enclosingRepoDir ? `This book was added to ${path15.basename(enclosingRepoDir)}'s shared version history; save a snapshot to record it.` : e instanceof Error ? e.message : String(e);
11176
10602
  }
11177
10603
  }
11178
10604
  const result = {
@@ -11192,7 +10618,7 @@ function prettifyFolderName(base) {
11192
10618
  }
11193
10619
  async function adoptFolder(options) {
11194
10620
  const dir = options.dir;
11195
- if (typeof dir !== "string" || !path16.isAbsolute(dir)) {
10621
+ if (typeof dir !== "string" || !path15.isAbsolute(dir)) {
11196
10622
  throw new CreateProjectErrorImpl("scaffold-io", "An absolute folder path is required.");
11197
10623
  }
11198
10624
  let st;
@@ -11204,24 +10630,24 @@ async function adoptFolder(options) {
11204
10630
  if (!st.isDirectory()) {
11205
10631
  throw new CreateProjectErrorImpl("scaffold-io", `Not a folder: ${dir}`);
11206
10632
  }
11207
- if (MANIFEST_FILENAMES.some((name) => existsSync12(path16.join(dir, name)))) {
11208
- throw new CreateProjectErrorImpl("target-exists", "This folder is already a Gutterpress project.");
10633
+ if (MANIFEST_FILENAMES.some((name) => existsSync12(path15.join(dir, name)))) {
10634
+ throw new CreateProjectErrorImpl("target-exists", "This folder is already a Gutterpress book.");
11209
10635
  }
11210
10636
  const template = options.template ?? "book";
11211
- const title = (options.title ?? "").trim() || prettifyFolderName(path16.basename(dir));
10637
+ const title = (options.title ?? "").trim() || prettifyFolderName(path15.basename(dir));
11212
10638
  const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
11213
10639
  let mdFiles;
11214
10640
  try {
11215
- const entries = await readdir6(dir, { withFileTypes: true });
10641
+ const entries = await readdir5(dir, { withFileTypes: true });
11216
10642
  mdFiles = entries.filter((e) => e.isFile() && /\.(md|markdown)$/i.test(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
11217
10643
  if (mdFiles.length === 0) {
11218
10644
  const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
11219
- await copyFile(tplChapter, path16.join(dir, "chapter-01.md"));
11220
- await fillTemplateFile(path16.join(dir, "chapter-01.md"), { "{{TITLE}}": title }).catch(() => {});
10645
+ await copyFile(tplChapter, path15.join(dir, "chapter-01.md"));
10646
+ await fillTemplateFile(path15.join(dir, "chapter-01.md"), { "{{TITLE}}": title }).catch(() => {});
11221
10647
  mdFiles = ["chapter-01.md"];
11222
10648
  }
11223
- await mkdir7(path16.join(dir, "styles"), { recursive: true });
11224
- const bookCssPath = path16.join(dir, "styles", "book.css");
10649
+ await mkdir7(path15.join(dir, "styles"), { recursive: true });
10650
+ const bookCssPath = path15.join(dir, "styles", "book.css");
11225
10651
  if (!existsSync12(bookCssPath)) {
11226
10652
  await writeFile8(bookCssPath, starterOverridesCss(), "utf8");
11227
10653
  }
@@ -11239,8 +10665,8 @@ ${filesYaml}
11239
10665
  styles:
11240
10666
  - styles/book.css
11241
10667
  `;
11242
- await writeFile8(path16.join(dir, MANIFEST_FILENAMES[0]), manifest, "utf8");
11243
- await mkdir7(path16.join(dir, "assets"), { recursive: true });
10668
+ await writeFile8(path15.join(dir, MANIFEST_FILENAMES[0]), manifest, "utf8");
10669
+ await mkdir7(path15.join(dir, "assets"), { recursive: true });
11244
10670
  await ensureGitignoreHasDist(dir);
11245
10671
  } catch (e) {
11246
10672
  if (e instanceof CreateProjectErrorImpl)
@@ -11252,7 +10678,7 @@ styles:
11252
10678
  let versionHistoryError;
11253
10679
  if (requested === "local-git") {
11254
10680
  try {
11255
- const { providerFor } = await import("./source-provider-a458mmec.js");
10681
+ const { providerFor } = await import("./source-provider-m5wes47h.js");
11256
10682
  const provider = providerFor({ type: "local-folder", path: dir });
11257
10683
  await provider.initVersionHistory({
11258
10684
  projectDir: dir,
@@ -11267,8 +10693,8 @@ styles:
11267
10693
  }
11268
10694
  const result = {
11269
10695
  projectDir: dir,
11270
- manifestPath: path16.join(dir, MANIFEST_FILENAMES[0]),
11271
- openFile: path16.join(dir, mdFiles[0]),
10696
+ manifestPath: path15.join(dir, MANIFEST_FILENAMES[0]),
10697
+ openFile: path15.join(dir, mdFiles[0]),
11272
10698
  versionHistory
11273
10699
  };
11274
10700
  if (versionHistoryError !== undefined)
@@ -11276,10 +10702,10 @@ styles:
11276
10702
  return result;
11277
10703
  }
11278
10704
  async function ensureGitignoreHasDist(projectDir) {
11279
- const gitignorePath = path16.join(projectDir, ".gitignore");
10705
+ const gitignorePath = path15.join(projectDir, ".gitignore");
11280
10706
  let existing = null;
11281
10707
  try {
11282
- existing = await readFile27(gitignorePath, "utf8");
10708
+ existing = await readFile26(gitignorePath, "utf8");
11283
10709
  } catch {}
11284
10710
  if (existing === null) {
11285
10711
  await writeFile8(gitignorePath, `dist/
@@ -11299,7 +10725,7 @@ async function ensureGitignoreHasDist(projectDir) {
11299
10725
  async function firstSourceFile(manifestPath) {
11300
10726
  let text;
11301
10727
  try {
11302
- text = await readFile27(manifestPath, "utf8");
10728
+ text = await readFile26(manifestPath, "utf8");
11303
10729
  } catch {
11304
10730
  return;
11305
10731
  }
@@ -11311,16 +10737,16 @@ async function firstSourceFile(manifestPath) {
11311
10737
  return m ? m[1].trim() : undefined;
11312
10738
  }
11313
10739
  async function fillTemplateFile(filePath, substitutions) {
11314
- let text = await readFile27(filePath, "utf8");
10740
+ let text = await readFile26(filePath, "utf8");
11315
10741
  for (const [token, value] of Object.entries(substitutions)) {
11316
10742
  text = text.split(token).join(value);
11317
10743
  }
11318
10744
  await writeFile8(filePath, text, "utf8");
11319
10745
  }
11320
10746
  // src/lib/extension-scaffold.ts
11321
- import { access as access2, mkdir as mkdir8, readFile as readFile28, writeFile as writeFile9 } from "node:fs/promises";
10747
+ import { access as access2, mkdir as mkdir8, readFile as readFile27, writeFile as writeFile9 } from "node:fs/promises";
11322
10748
  import { constants as FS2 } from "node:fs";
11323
- import path17 from "node:path";
10749
+ import path16 from "node:path";
11324
10750
  var EXTENSION_KINDS = ["plugin", "theme"];
11325
10751
  var RESERVED_PREFIX = "gp-";
11326
10752
  var TEMPLATE_FILES = {
@@ -11398,7 +10824,7 @@ async function scaffoldExtension(options) {
11398
10824
  }
11399
10825
  const prefix = resolveExtensionPrefix(slug, options.prefix);
11400
10826
  const parentDir = options.parentDir;
11401
- if (!parentDir || !path17.isAbsolute(parentDir)) {
10827
+ if (!parentDir || !path16.isAbsolute(parentDir)) {
11402
10828
  throw new ExtensionScaffoldError("parent-not-writable", "The save location must be an absolute path.");
11403
10829
  }
11404
10830
  try {
@@ -11406,7 +10832,7 @@ async function scaffoldExtension(options) {
11406
10832
  } catch {
11407
10833
  throw new ExtensionScaffoldError("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
11408
10834
  }
11409
- const extensionDir = path17.join(parentDir, slug);
10835
+ const extensionDir = path16.join(parentDir, slug);
11410
10836
  let targetExists = true;
11411
10837
  try {
11412
10838
  await access2(extensionDir, FS2.F_OK);
@@ -11433,9 +10859,9 @@ async function scaffoldExtension(options) {
11433
10859
  await mkdir8(extensionDir, { recursive: true });
11434
10860
  for (const [assetName, relDest] of entries) {
11435
10861
  const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
11436
- const dest = path17.join(extensionDir, relDest);
11437
- await mkdir8(path17.dirname(dest), { recursive: true });
11438
- const text = await readFile28(src, "utf8");
10862
+ const dest = path16.join(extensionDir, relDest);
10863
+ await mkdir8(path16.dirname(dest), { recursive: true });
10864
+ const text = await readFile27(src, "utf8");
11439
10865
  const table = relDest.endsWith(".json") ? jsonSafe : plain;
11440
10866
  await writeFile9(dest, substitute(text, table), "utf8");
11441
10867
  written.push(relDest);
@@ -11445,11 +10871,11 @@ async function scaffoldExtension(options) {
11445
10871
  }
11446
10872
  return {
11447
10873
  extensionDir,
11448
- manifestPath: path17.join(extensionDir, "package.json"),
10874
+ manifestPath: path16.join(extensionDir, "package.json"),
11449
10875
  kind,
11450
10876
  slug,
11451
10877
  prefix,
11452
- openFile: path17.join(extensionDir, OPEN_FILE[kind]),
10878
+ openFile: path16.join(extensionDir, OPEN_FILE[kind]),
11453
10879
  files: written
11454
10880
  };
11455
10881
  }
@@ -11461,10 +10887,10 @@ function substitute(text, table) {
11461
10887
  return out;
11462
10888
  }
11463
10889
  // src/lib/project-templates.ts
11464
- import { cp as cp3, mkdir as mkdir9, readFile as readFile29, readdir as readdir7, stat as stat6, writeFile as writeFile10 } from "node:fs/promises";
10890
+ import { cp as cp3, mkdir as mkdir9, readFile as readFile28, readdir as readdir6, stat as stat6, writeFile as writeFile10 } from "node:fs/promises";
11465
10891
  import { constants as FS3, existsSync as existsSync13 } from "node:fs";
11466
10892
  import { access as access3 } from "node:fs/promises";
11467
- import path18 from "node:path";
10893
+ import path17 from "node:path";
11468
10894
  import { isMap, isScalar, isSeq as isSeq2, parseDocument as parseDocument2 } from "yaml";
11469
10895
  var BUILT_IN_TEMPLATE_IDS = [
11470
10896
  "book",
@@ -11473,7 +10899,7 @@ var BUILT_IN_TEMPLATE_IDS = [
11473
10899
  ];
11474
10900
  async function readTemplateChoices(manifestPath) {
11475
10901
  try {
11476
- const text = await readFile29(manifestPath, "utf8");
10902
+ const text = await readFile28(manifestPath, "utf8");
11477
10903
  const doc = parseDocument2(text);
11478
10904
  const out = {};
11479
10905
  const preset = doc.get("preset");
@@ -11549,7 +10975,7 @@ function rewriteEntry(node, newPath) {
11549
10975
  node.value = newPath;
11550
10976
  }
11551
10977
  function commonAncestorDir(absFiles) {
11552
- const dirSegs = absFiles.map((f) => path18.resolve(f).split(path18.sep).slice(0, -1));
10978
+ const dirSegs = absFiles.map((f) => path17.resolve(f).split(path17.sep).slice(0, -1));
11553
10979
  if (dirSegs.length === 0)
11554
10980
  return "";
11555
10981
  let common = dirSegs[0];
@@ -11559,22 +10985,22 @@ function commonAncestorDir(absFiles) {
11559
10985
  i++;
11560
10986
  common = common.slice(0, i);
11561
10987
  }
11562
- return common.join(path18.sep) || path18.sep;
10988
+ return common.join(path17.sep) || path17.sep;
11563
10989
  }
11564
10990
  function pickVendorDir(templateDir, base) {
11565
- const preferred = path18.basename(base) || VENDOR_DIR;
11566
- if (!existsSync13(path18.join(templateDir, preferred)))
10991
+ const preferred = path17.basename(base) || VENDOR_DIR;
10992
+ if (!existsSync13(path17.join(templateDir, preferred)))
11567
10993
  return preferred;
11568
10994
  for (let n = 1;; n++) {
11569
10995
  const candidate = `${preferred}-${n}`;
11570
- if (!existsSync13(path18.join(templateDir, candidate)))
10996
+ if (!existsSync13(path17.join(templateDir, candidate)))
11571
10997
  return candidate;
11572
10998
  }
11573
10999
  }
11574
11000
  var TEMPLATE_META_FILE = ".gutterpress-template.json";
11575
11001
  async function reconcileSharedRefs(templateDir, sourceProjectDir, mode) {
11576
11002
  const { doc, file } = await loadManifestDoc(templateDir);
11577
- const escapes = (p) => escapesProjectRoot(sourceProjectDir, path18.resolve(sourceProjectDir, p));
11003
+ const escapes = (p) => escapesProjectRoot(sourceProjectDir, path17.resolve(sourceProjectDir, p));
11578
11004
  const stylesSeq = doc.get("styles", true);
11579
11005
  const extensionsSeq = doc.get("extensions", true);
11580
11006
  const escapingStyles = collectEscaping(isSeq2(stylesSeq) ? stylesSeq.items : [], scalarString, escapes);
@@ -11590,16 +11016,16 @@ async function reconcileSharedRefs(templateDir, sourceProjectDir, mode) {
11590
11016
  return { vendoredRefs: [], excludedRefs };
11591
11017
  }
11592
11018
  const styleClosure = await collectStyleDependencies(sourceProjectDir, escapingStyles.map((s) => s.value));
11593
- const extensionFiles = escapingExtensions.map((p) => path18.resolve(sourceProjectDir, p.value));
11019
+ const extensionFiles = escapingExtensions.map((p) => path17.resolve(sourceProjectDir, p.value));
11594
11020
  const externalFiles = [...new Set([...styleClosure, ...extensionFiles])].filter((f) => escapesProjectRoot(sourceProjectDir, f));
11595
11021
  const base = commonAncestorDir(externalFiles);
11596
11022
  const vendorDir = pickVendorDir(templateDir, base);
11597
11023
  await Promise.all(externalFiles.filter((abs) => existsSync13(abs)).map(async (abs) => {
11598
- const dest = path18.join(templateDir, vendorDir, path18.relative(base, abs));
11599
- await mkdir9(path18.dirname(dest), { recursive: true });
11024
+ const dest = path17.join(templateDir, vendorDir, path17.relative(base, abs));
11025
+ await mkdir9(path17.dirname(dest), { recursive: true });
11600
11026
  await cp3(abs, dest, { recursive: true });
11601
11027
  }));
11602
- const bookLocal = (value) => toPosixPath(path18.join(vendorDir, path18.relative(base, path18.resolve(sourceProjectDir, value))));
11028
+ const bookLocal = (value) => toPosixPath(path17.join(vendorDir, path17.relative(base, path17.resolve(sourceProjectDir, value))));
11603
11029
  const vendoredRefs = [];
11604
11030
  for (const { node, value } of escapingStyles) {
11605
11031
  const rewritten = bookLocal(value);
@@ -11619,7 +11045,7 @@ async function saveProjectAsTemplate(options) {
11619
11045
  const id = slugify(name);
11620
11046
  if (!id)
11621
11047
  throw new Error(`Could not derive a template id from "${name}".`);
11622
- const dir = path18.join(templatesRoot, id);
11048
+ const dir = path17.join(templatesRoot, id);
11623
11049
  let exists = true;
11624
11050
  try {
11625
11051
  await access3(dir, FS3.F_OK);
@@ -11630,19 +11056,19 @@ async function saveProjectAsTemplate(options) {
11630
11056
  throw new Error(`A template named "${name}" already exists. Choose a different name.`);
11631
11057
  }
11632
11058
  await mkdir9(dir, { recursive: true });
11633
- const entries = await readdir7(projectDir, { withFileTypes: true });
11059
+ const entries = await readdir6(projectDir, { withFileTypes: true });
11634
11060
  for (const entry of entries) {
11635
11061
  if (SKIP_ENTRIES.has(entry.name))
11636
11062
  continue;
11637
11063
  if (entry.name === TEMPLATE_META_FILE)
11638
11064
  continue;
11639
- await cp3(path18.join(projectDir, entry.name), path18.join(dir, entry.name), { recursive: true });
11065
+ await cp3(path17.join(projectDir, entry.name), path17.join(dir, entry.name), { recursive: true });
11640
11066
  }
11641
11067
  const manifestName = MANIFEST_FILENAMES.find((name2) => entries.some((entry) => entry.name === name2));
11642
11068
  const shared = manifestName ? await reconcileSharedRefs(dir, projectDir, options.sharedRefs ?? "vendor") : { vendoredRefs: [], excludedRefs: [] };
11643
11069
  if (manifestName)
11644
- await retokeniseManifest(path18.join(dir, manifestName));
11645
- await writeFile10(path18.join(dir, TEMPLATE_META_FILE), JSON.stringify({ label: name }, null, 2), "utf8");
11070
+ await retokeniseManifest(path17.join(dir, manifestName));
11071
+ await writeFile10(path17.join(dir, TEMPLATE_META_FILE), JSON.stringify({ label: name }, null, 2), "utf8");
11646
11072
  return {
11647
11073
  id,
11648
11074
  label: name,
@@ -11655,7 +11081,7 @@ async function saveProjectAsTemplate(options) {
11655
11081
  async function retokeniseManifest(manifestPath) {
11656
11082
  let text;
11657
11083
  try {
11658
- text = await readFile29(manifestPath, "utf8");
11084
+ text = await readFile28(manifestPath, "utf8");
11659
11085
  } catch {
11660
11086
  return;
11661
11087
  }
@@ -11665,7 +11091,7 @@ async function retokeniseManifest(manifestPath) {
11665
11091
  async function listCustomTemplates(templatesRoot) {
11666
11092
  let entries;
11667
11093
  try {
11668
- entries = await readdir7(templatesRoot, { withFileTypes: true });
11094
+ entries = await readdir6(templatesRoot, { withFileTypes: true });
11669
11095
  } catch {
11670
11096
  return [];
11671
11097
  }
@@ -11673,10 +11099,10 @@ async function listCustomTemplates(templatesRoot) {
11673
11099
  for (const entry of entries) {
11674
11100
  if (!entry.isDirectory())
11675
11101
  continue;
11676
- const dir = path18.join(templatesRoot, entry.name);
11102
+ const dir = path17.join(templatesRoot, entry.name);
11677
11103
  let label = prettify(entry.name);
11678
11104
  try {
11679
- const meta = JSON.parse(await readFile29(path18.join(dir, TEMPLATE_META_FILE), "utf8"));
11105
+ const meta = JSON.parse(await readFile28(path17.join(dir, TEMPLATE_META_FILE), "utf8"));
11680
11106
  if (typeof meta.label === "string" && meta.label)
11681
11107
  label = meta.label;
11682
11108
  } catch {}
@@ -11686,7 +11112,7 @@ async function listCustomTemplates(templatesRoot) {
11686
11112
  description: "Your saved template.",
11687
11113
  kind: "custom",
11688
11114
  dir,
11689
- ...await readTemplateChoices(path18.join(dir, MANIFEST_FILENAMES[0]))
11115
+ ...await readTemplateChoices(path17.join(dir, MANIFEST_FILENAMES[0]))
11690
11116
  });
11691
11117
  }
11692
11118
  out.sort((a, b) => a.label.localeCompare(b.label));
@@ -11698,37 +11124,37 @@ async function importTemplateFromFolder(options) {
11698
11124
  if (!srcStat.isDirectory()) {
11699
11125
  throw new Error("The chosen path is not a folder.");
11700
11126
  }
11701
- const name = options.name?.trim() || prettify(path18.basename(sourceDir));
11127
+ const name = options.name?.trim() || prettify(path17.basename(sourceDir));
11702
11128
  return saveProjectAsTemplate({ projectDir: sourceDir, name, templatesRoot });
11703
11129
  }
11704
11130
  // src/lib/snippets.ts
11705
- import { readdir as readdir8, readFile as readFile32, writeFile as writeFile11, mkdir as mkdir12, rm as rm5 } from "node:fs/promises";
11706
- import path21 from "node:path";
11131
+ import { readdir as readdir7, readFile as readFile31, writeFile as writeFile11, mkdir as mkdir12, rm as rm5 } from "node:fs/promises";
11132
+ import path20 from "node:path";
11707
11133
 
11708
11134
  // src/lib/extension-manager.ts
11709
- import { cp as cp4, mkdir as mkdir11, readFile as readFile31, rm as rm4, stat as stat7 } from "node:fs/promises";
11135
+ import { cp as cp4, mkdir as mkdir11, readFile as readFile30, rm as rm4, stat as stat7 } from "node:fs/promises";
11710
11136
  import { existsSync as existsSync14 } from "node:fs";
11711
- import path20 from "node:path";
11137
+ import path19 from "node:path";
11712
11138
  import { isSeq as isSeq3, isMap as isMap2, isScalar as isScalar2, YAMLMap, Scalar } from "yaml";
11713
11139
 
11714
11140
  // src/lib/npm-plugin-installer.ts
11715
- import { createHash as createHash7, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
11716
- import { createReadStream as createReadStream2 } from "node:fs";
11141
+ import { createHash as createHash6, randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
11142
+ import { createReadStream } from "node:fs";
11717
11143
  import {
11718
- lstat as lstat2,
11144
+ lstat,
11719
11145
  mkdir as mkdir10,
11720
11146
  mkdtemp as mkdtemp2,
11721
11147
  open as open2,
11722
- readFile as readFile30,
11723
- realpath as realpath2,
11148
+ readFile as readFile29,
11149
+ realpath,
11724
11150
  rename as rename4,
11725
11151
  rm as rm3
11726
11152
  } from "node:fs/promises";
11727
- import path19 from "node:path";
11153
+ import path18 from "node:path";
11728
11154
  import { Transform } from "node:stream";
11729
11155
  import { pipeline } from "node:stream/promises";
11730
11156
  import { createGunzip } from "node:zlib";
11731
- import { maxSatisfying, satisfies as satisfies2, validRange as validRange2 } from "semver";
11157
+ import { maxSatisfying, satisfies, validRange } from "semver";
11732
11158
 
11733
11159
  // src/lib/fetch-timeout.ts
11734
11160
  class FetchPassthroughError extends Error {
@@ -11785,11 +11211,11 @@ class PackageUnavailableError extends Error {
11785
11211
  function isOptionalAvailabilityError(error2) {
11786
11212
  return error2 instanceof PackageUnavailableError || error2 instanceof FriendlyHttpError || error2 instanceof FetchUnavailableError;
11787
11213
  }
11788
- function object3(value) {
11214
+ function object2(value) {
11789
11215
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
11790
11216
  }
11791
- function stringMap2(value) {
11792
- const map = object3(value);
11217
+ function stringMap(value) {
11218
+ const map = object2(value);
11793
11219
  if (!map)
11794
11220
  return {};
11795
11221
  const result = {};
@@ -11802,9 +11228,9 @@ function stringMap2(value) {
11802
11228
  function mb(bytes) {
11803
11229
  return `${Math.ceil(bytes / 1024 / 1024)}MB`;
11804
11230
  }
11805
- function isContained2(root, candidate) {
11806
- const relative2 = path19.relative(root, candidate);
11807
- return relative2 === "" || !relative2.startsWith("..") && !path19.isAbsolute(relative2);
11231
+ function isContained(root, candidate) {
11232
+ const relative2 = path18.relative(root, candidate);
11233
+ return relative2 === "" || !relative2.startsWith("..") && !path18.isAbsolute(relative2);
11808
11234
  }
11809
11235
  function mergeLimits(overrides) {
11810
11236
  const limits = { ...DEFAULT_LIMITS, ...overrides };
@@ -11882,7 +11308,7 @@ async function fetchPackument(ctx, name) {
11882
11308
  }
11883
11309
  let metadata = null;
11884
11310
  try {
11885
- metadata = object3(JSON.parse(new TextDecoder().decode(bytes)));
11311
+ metadata = object2(JSON.parse(new TextDecoder().decode(bytes)));
11886
11312
  } catch {}
11887
11313
  if (!metadata || metadata.name !== name) {
11888
11314
  throw new FriendlyHttpError(`npm returned invalid metadata for "${name}".`);
@@ -11938,29 +11364,29 @@ async function selectPackage(ctx, name, selector) {
11938
11364
  throw new PackageUnavailableError(`Dependency selector "${selector}" for ${name} is not a registry semver range or dist-tag.`);
11939
11365
  }
11940
11366
  const metadata = await fetchPackument(ctx, name);
11941
- const versions = object3(metadata.versions);
11942
- const tags = object3(metadata["dist-tags"]);
11367
+ const versions = object2(metadata.versions);
11368
+ const tags = object2(metadata["dist-tags"]);
11943
11369
  if (!versions || !tags)
11944
11370
  throw new PackageUnavailableError(`npm metadata for "${name}" has no versions.`);
11945
11371
  let version;
11946
11372
  if (!selector) {
11947
11373
  if (typeof tags.latest === "string")
11948
11374
  version = tags.latest;
11949
- } else if (object3(versions[selector])) {
11375
+ } else if (object2(versions[selector])) {
11950
11376
  version = selector;
11951
11377
  } else if (typeof tags[selector] === "string") {
11952
11378
  version = tags[selector];
11953
- } else if (validRange2(selector)) {
11379
+ } else if (validRange(selector)) {
11954
11380
  version = maxSatisfying(Object.keys(versions), selector) ?? undefined;
11955
11381
  }
11956
11382
  if (!version || !isExactNpmVersion(version)) {
11957
11383
  throw new PackageUnavailableError(`No exact npm version of "${name}" satisfies "${selector ?? "latest"}".`);
11958
11384
  }
11959
- const selected = object3(versions[version]);
11385
+ const selected = object2(versions[version]);
11960
11386
  if (!selected || selected.name !== name || selected.version !== version) {
11961
11387
  throw new SecurityInstallError(`npm metadata identity failed for "${name}@${version}".`);
11962
11388
  }
11963
- const dist = object3(selected.dist);
11389
+ const dist = object2(selected.dist);
11964
11390
  if (!dist || typeof dist.tarball !== "string") {
11965
11391
  throw new PackageUnavailableError(`npm metadata for "${name}@${version}" has no tarball.`);
11966
11392
  }
@@ -11977,7 +11403,7 @@ async function selectPackage(ctx, name, selector) {
11977
11403
  }
11978
11404
  async function downloadTarball(ctx, pkg, archivePath) {
11979
11405
  const file = await open2(archivePath, "wx");
11980
- const hash = createHash7(pkg.integrity.algorithm);
11406
+ const hash = createHash6(pkg.integrity.algorithm);
11981
11407
  try {
11982
11408
  await withFetchTimeout({
11983
11409
  timeoutMs: TARBALL_TIMEOUT_MS,
@@ -12099,7 +11525,7 @@ async function extractTarball(ctx, archivePath, packageDir, packageName) {
12099
11525
  }
12100
11526
  });
12101
11527
  try {
12102
- await pipeline(createReadStream2(archivePath), createGunzip(), limiter, unpack);
11528
+ await pipeline(createReadStream(archivePath), createGunzip(), limiter, unpack);
12103
11529
  } catch (error2) {
12104
11530
  if (rejected)
12105
11531
  throw rejected;
@@ -12111,13 +11537,13 @@ async function extractTarball(ctx, archivePath, packageDir, packageName) {
12111
11537
  throw rejected;
12112
11538
  }
12113
11539
  async function readInstalledManifest(packageDir, selected) {
12114
- const packageJsonPath = path19.join(packageDir, "package.json");
11540
+ const packageJsonPath = path18.join(packageDir, "package.json");
12115
11541
  let manifest = null;
12116
11542
  try {
12117
- const info2 = await lstat2(packageJsonPath);
11543
+ const info2 = await lstat(packageJsonPath);
12118
11544
  if (!info2.isFile() || info2.isSymbolicLink())
12119
11545
  throw new Error("not a regular file");
12120
- manifest = object3(JSON.parse(await readFile30(packageJsonPath, "utf8")));
11546
+ manifest = object2(JSON.parse(await readFile29(packageJsonPath, "utf8")));
12121
11547
  } catch (error2) {
12122
11548
  throw new SecurityInstallError(`Downloaded ${selected.name}@${selected.version} has no valid package.json (${error2 instanceof Error ? error2.message : String(error2)}).`);
12123
11549
  }
@@ -12180,17 +11606,17 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
12180
11606
  }
12181
11607
  throw error2;
12182
11608
  }
12183
- const ancestor = ancestors.find((candidate) => candidate.name === name && (candidate.version === selected.version || (selector ? satisfies2(candidate.version, selector) : false)));
11609
+ const ancestor = ancestors.find((candidate) => candidate.name === name && (candidate.version === selected.version || (selector ? satisfies(candidate.version, selector) : false)));
12184
11610
  if (ancestor)
12185
11611
  return { path: ancestor.path };
12186
11612
  if (ctx.records.length >= ctx.limits.totalPackages) {
12187
11613
  throw new SecurityInstallError(`Dependency graph exceeds ${ctx.limits.totalPackages} packages.`);
12188
11614
  }
12189
11615
  const relativePath = relativePackagePath(parentPackagePath, name);
12190
- const packageDir = path19.join(ctx.stageRoot, ...relativePath.split("/"));
12191
- const archivePath = path19.join(ctx.downloadsDir, `${ctx.archiveCounter++}.tgz`);
11616
+ const packageDir = path18.join(ctx.stageRoot, ...relativePath.split("/"));
11617
+ const archivePath = path18.join(ctx.downloadsDir, `${ctx.archiveCounter++}.tgz`);
12192
11618
  try {
12193
- await mkdir10(path19.dirname(packageDir), { recursive: true });
11619
+ await mkdir10(path18.dirname(packageDir), { recursive: true });
12194
11620
  await downloadTarball(ctx, selected, archivePath);
12195
11621
  await extractTarball(ctx, archivePath, packageDir, selected.name);
12196
11622
  const manifest = await readInstalledManifest(packageDir, selected);
@@ -12224,19 +11650,19 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
12224
11650
  if (selected.integrity.legacySha1) {
12225
11651
  ctx.warnings.add(`${name}@${selected.version} is an old npm release with only SHA-1 registry integrity. It was accepted for compatibility and recorded as legacy SHA-1 in the vendor receipt.`);
12226
11652
  }
12227
- const dependencies = stringMap2(manifest.dependencies);
12228
- const optionalDependencies = stringMap2(manifest.optionalDependencies);
11653
+ const dependencies = stringMap(manifest.dependencies);
11654
+ const optionalDependencies = stringMap(manifest.optionalDependencies);
12229
11655
  for (const optionalName of Object.keys(optionalDependencies))
12230
11656
  delete dependencies[optionalName];
12231
- const peerDependencies = stringMap2(manifest.peerDependencies);
12232
- const peerMeta = object3(manifest.peerDependenciesMeta) ?? {};
11657
+ const peerDependencies = stringMap(manifest.peerDependencies);
11658
+ const peerMeta = object2(manifest.peerDependenciesMeta) ?? {};
12233
11659
  for (const existing of [...Object.keys(dependencies), ...Object.keys(optionalDependencies)]) {
12234
11660
  delete peerDependencies[existing];
12235
11661
  }
12236
11662
  const nextAncestors = [...ancestors, record];
12237
11663
  const requiredEntries = [
12238
11664
  ...Object.entries(dependencies).map(([dependency, range]) => ({ dependency, range, kind: "required" })),
12239
- ...Object.entries(peerDependencies).filter(([dependency]) => object3(peerMeta[dependency])?.optional !== true).map(([dependency, range]) => ({ dependency, range, kind: "peer" }))
11665
+ ...Object.entries(peerDependencies).filter(([dependency]) => object2(peerMeta[dependency])?.optional !== true).map(([dependency, range]) => ({ dependency, range, kind: "peer" }))
12240
11666
  ].sort((a, b) => a.dependency.localeCompare(b.dependency));
12241
11667
  for (const { dependency, range } of requiredEntries) {
12242
11668
  const child = await installPackage(ctx, dependency, range, relativePath, nextAncestors, depth + 1, false);
@@ -12254,7 +11680,7 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
12254
11680
  }
12255
11681
  }
12256
11682
  for (const [dependency, range] of Object.entries(peerDependencies)) {
12257
- if (object3(peerMeta[dependency])?.optional === true) {
11683
+ if (object2(peerMeta[dependency])?.optional === true) {
12258
11684
  ctx.skipped.push({
12259
11685
  from: relativePath,
12260
11686
  name: dependency,
@@ -12278,7 +11704,7 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
12278
11704
  }
12279
11705
  async function ensureDirectory(pathname) {
12280
11706
  try {
12281
- const info2 = await lstat2(pathname);
11707
+ const info2 = await lstat(pathname);
12282
11708
  if (info2.isSymbolicLink() || !info2.isDirectory()) {
12283
11709
  throw new Error(`Plugin install path is not a normal directory: ${pathname}`);
12284
11710
  }
@@ -12289,19 +11715,19 @@ async function ensureDirectory(pathname) {
12289
11715
  }
12290
11716
  }
12291
11717
  async function prepareVendorParent(projectDir, name) {
12292
- const plugins = path19.join(projectDir, PLUGINS_DIR);
12293
- const npm = path19.join(plugins, VENDORED_NPM_DIR);
12294
- const packageParent = path19.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
11718
+ const plugins = path18.join(projectDir, PLUGINS_DIR);
11719
+ const npm = path18.join(plugins, VENDORED_NPM_DIR);
11720
+ const packageParent = path18.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
12295
11721
  await mkdir10(projectDir, { recursive: true });
12296
11722
  await ensureDirectory(plugins);
12297
11723
  await ensureDirectory(npm);
12298
11724
  await ensureDirectory(packageParent);
12299
11725
  const [realNpm, realPackageParent] = await Promise.all([
12300
- realpath2(npm),
12301
- realpath2(packageParent)
11726
+ realpath(npm),
11727
+ realpath(packageParent)
12302
11728
  ]);
12303
- if (!isContained2(projectDir, realNpm) || !isContained2(projectDir, realPackageParent)) {
12304
- throw new Error("Plugin install path resolves outside the project.");
11729
+ if (!isContained(projectDir, realNpm) || !isContained(projectDir, realPackageParent)) {
11730
+ throw new Error("Plugin install path resolves outside the book folder.");
12305
11731
  }
12306
11732
  return realNpm;
12307
11733
  }
@@ -12319,14 +11745,14 @@ function publicReceipt(record) {
12319
11745
  };
12320
11746
  }
12321
11747
  async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
12322
- const requestedProjectDir = path19.resolve(projectDirInput);
11748
+ const requestedProjectDir = path18.resolve(projectDirInput);
12323
11749
  await mkdir10(requestedProjectDir, { recursive: true });
12324
- const projectDir = await realpath2(requestedProjectDir);
11750
+ const projectDir = await realpath(requestedProjectDir);
12325
11751
  const requested = parseNpmPluginSpec(packageSpec);
12326
11752
  const npmRoot = await prepareVendorParent(projectDir, requested.name);
12327
- const container = await mkdtemp2(path19.join(npmRoot, ".install-"));
12328
- const stageRoot = path19.join(container, "root");
12329
- const downloadsDir = path19.join(container, "downloads");
11753
+ const container = await mkdtemp2(path18.join(npmRoot, ".install-"));
11754
+ const stageRoot = path18.join(container, "root");
11755
+ const downloadsDir = path18.join(container, "downloads");
12330
11756
  await Promise.all([mkdir10(stageRoot), mkdir10(downloadsDir)]);
12331
11757
  const ctx = {
12332
11758
  fetch: options.fetch ?? globalThis.fetch,
@@ -12372,7 +11798,7 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
12372
11798
  skipped: ctx.skipped,
12373
11799
  tree: { algorithm: "sha256", ...tree }
12374
11800
  };
12375
- const receiptFile = await open2(path19.join(stageRoot, VENDOR_RECEIPT_FILE), "wx");
11801
+ const receiptFile = await open2(path18.join(stageRoot, VENDOR_RECEIPT_FILE), "wx");
12376
11802
  try {
12377
11803
  await receiptFile.writeFile(`${JSON.stringify(receipt, null, 2)}
12378
11804
  `, "utf8");
@@ -12381,12 +11807,12 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
12381
11807
  await receiptFile.close();
12382
11808
  }
12383
11809
  finalRoot = vendoredNpmPluginRoot(projectDir, rootRecord.name, rootRecord.version);
12384
- const realFinalParent = await realpath2(path19.dirname(finalRoot));
12385
- if (!isContained2(projectDir, realFinalParent)) {
12386
- throw new Error("Plugin install destination resolves outside the project.");
11810
+ const realFinalParent = await realpath(path18.dirname(finalRoot));
11811
+ if (!isContained(projectDir, realFinalParent)) {
11812
+ throw new Error("Plugin install destination resolves outside the book folder.");
12387
11813
  }
12388
11814
  try {
12389
- await lstat2(finalRoot);
11815
+ await lstat(finalRoot);
12390
11816
  backupRoot = `${finalRoot}.backup-${randomUUID3()}`;
12391
11817
  await rename4(finalRoot, backupRoot);
12392
11818
  } catch (error2) {
@@ -12459,7 +11885,7 @@ var RECOMMENDED_EXTENSIONS = [
12459
11885
  var BUILT_IN_STYLE_SET_IDS = ["clean-book", "zine", "technical-doc"];
12460
11886
  var mutationQueues = new Map;
12461
11887
  function withMutationLock(projectDir, mutation) {
12462
- const resolved = path20.resolve(projectDir);
11888
+ const resolved = path19.resolve(projectDir);
12463
11889
  const key = process.platform === "win32" ? resolved.toLowerCase() : resolved;
12464
11890
  const previous = mutationQueues.get(key) ?? Promise.resolve();
12465
11891
  const run2 = previous.then(mutation, mutation);
@@ -12521,9 +11947,9 @@ function indexOfUse(seq, use) {
12521
11947
  });
12522
11948
  }
12523
11949
  function manifestPathFor(projectDir, abs) {
12524
- const rel = path20.relative(path20.resolve(projectDir), abs).split(path20.sep).join("/");
11950
+ const rel = path19.relative(path19.resolve(projectDir), abs).split(path19.sep).join("/");
12525
11951
  if (rel === "") {
12526
- throw new Error("An extension cannot be the project folder itself.");
11952
+ throw new Error("An extension cannot be the book folder itself.");
12527
11953
  }
12528
11954
  if (isPathSpecifier(rel))
12529
11955
  return rel;
@@ -12580,21 +12006,21 @@ async function describeEntry(projectDir, raw) {
12580
12006
  });
12581
12007
  }
12582
12008
  if (parsed.kind === "path") {
12583
- const abs = path20.resolve(projectDir, parsed.path);
12009
+ const abs = path19.resolve(projectDir, parsed.path);
12584
12010
  const base2 = { ...common, kind: "path", name: parsed.path };
12585
12011
  let info2;
12586
12012
  try {
12587
12013
  info2 = await stat7(abs);
12588
12014
  } catch {
12589
12015
  warnings.push(`Not found: ${abs}`);
12590
- return withWarnings({ ...base2, label: path20.basename(abs), carries: NO_CARRIES });
12016
+ return withWarnings({ ...base2, label: path19.basename(abs), carries: NO_CARRIES });
12591
12017
  }
12592
12018
  if (info2.isDirectory()) {
12593
- return withWarnings(await describeFolder(abs, base2, prettify(path20.basename(abs))));
12019
+ return withWarnings(await describeFolder(abs, base2, prettify(path19.basename(abs))));
12594
12020
  }
12595
12021
  return withWarnings({
12596
12022
  ...base2,
12597
- label: path20.basename(abs),
12023
+ label: path19.basename(abs),
12598
12024
  carries: { ...NO_CARRIES, markdown: true }
12599
12025
  });
12600
12026
  }
@@ -12619,7 +12045,7 @@ async function describeEntry(projectDir, raw) {
12619
12045
  return withWarnings({ ...base, label: parsed.name, carries: { ...NO_CARRIES, markdown: true } });
12620
12046
  }
12621
12047
  const pkgDir = vendoredNpmPluginPackageDir(installRoot, parsed.name);
12622
- if (!existsSync14(path20.join(pkgDir, EXTENSION_MANIFEST_FILENAME))) {
12048
+ if (!existsSync14(path19.join(pkgDir, EXTENSION_MANIFEST_FILENAME))) {
12623
12049
  return withWarnings({ ...base, label: parsed.name, carries: { ...NO_CARRIES, markdown: true } });
12624
12050
  }
12625
12051
  const described = await describeFolder(pkgDir, base, parsed.name, (name) => name);
@@ -12684,7 +12110,7 @@ async function reorderExtensions(projectDir, order) {
12684
12110
  const { doc, file } = await loadManifestDoc(projectDir);
12685
12111
  const seq = doc.get("extensions", true);
12686
12112
  if (!isSeq3(seq))
12687
- throw new Error(`No extensions are configured in ${path20.basename(file)}.`);
12113
+ throw new Error(`No extensions are configured in ${path19.basename(file)}.`);
12688
12114
  const items = seq.items;
12689
12115
  const current = items.map((item) => entryUse(item));
12690
12116
  const wanted = order.map((u) => u.trim());
@@ -12778,11 +12204,11 @@ async function addExtension(projectDir, specifier, options = {}) {
12778
12204
  async function addExtensionUnlocked(projectDir, specifier, options) {
12779
12205
  const input = specifier.trim();
12780
12206
  const exportName = options.exportName?.trim() || undefined;
12781
- if (path20.isAbsolute(input))
12207
+ if (path19.isAbsolute(input))
12782
12208
  return addPathExtension(projectDir, input, exportName);
12783
12209
  const parsed = parseExtensionSpecifier(input);
12784
12210
  if (parsed.kind === "path") {
12785
- return addPathExtension(projectDir, path20.resolve(projectDir, parsed.path), exportName);
12211
+ return addPathExtension(projectDir, path19.resolve(projectDir, parsed.path), exportName);
12786
12212
  }
12787
12213
  if (parsed.kind === "bundled") {
12788
12214
  if (exportName)
@@ -12824,7 +12250,7 @@ async function listBuiltInStyleSets() {
12824
12250
  const assets = await getAssetsDir();
12825
12251
  const out = [];
12826
12252
  for (const id of BUILT_IN_STYLE_SET_IDS) {
12827
- const meta = await readExtensionMeta(path20.join(assets, "themes", id));
12253
+ const meta = await readExtensionMeta(path19.join(assets, "themes", id));
12828
12254
  out.push({
12829
12255
  id,
12830
12256
  name: prettify(meta.name?.trim() || id),
@@ -12836,7 +12262,7 @@ async function listBuiltInStyleSets() {
12836
12262
  async function uniqueExtensionId(projectDir, base) {
12837
12263
  const slug = slugify(base) || "extension";
12838
12264
  let candidate = slug;
12839
- for (let n = 2;existsSync14(path20.join(projectDir, EXTENSIONS_DIR, candidate)); n++) {
12265
+ for (let n = 2;existsSync14(path19.join(projectDir, EXTENSIONS_DIR, candidate)); n++) {
12840
12266
  candidate = `${slug}-${n}`;
12841
12267
  }
12842
12268
  return candidate;
@@ -12846,10 +12272,10 @@ async function addBuiltInStyleSet(projectDir, id) {
12846
12272
  throw new Error(`Unknown built-in look "${id}" (one of: ${BUILT_IN_STYLE_SET_IDS.join(", ")}).`);
12847
12273
  }
12848
12274
  return withMutationLock(projectDir, async () => {
12849
- const dest = path20.join(projectDir, EXTENSIONS_DIR, id);
12275
+ const dest = path19.join(projectDir, EXTENSIONS_DIR, id);
12850
12276
  if (!existsSync14(dest)) {
12851
- const src = path20.join(await getAssetsDir(), "themes", id);
12852
- await mkdir11(path20.dirname(dest), { recursive: true });
12277
+ const src = path19.join(await getAssetsDir(), "themes", id);
12278
+ await mkdir11(path19.dirname(dest), { recursive: true });
12853
12279
  await cp4(src, dest, { recursive: true });
12854
12280
  }
12855
12281
  return addExtensionUnlocked(projectDir, `./${EXTENSIONS_DIR}/${id}`, {});
@@ -12889,7 +12315,7 @@ async function readExtensionCss(projectDir, use) {
12889
12315
  throw new Error(`Extension "${use}" declares no stylesheets.`);
12890
12316
  const parts = [];
12891
12317
  for (const sheet of sheets)
12892
- parts.push(await readFile31(sheet, "utf8"));
12318
+ parts.push(await readFile30(sheet, "utf8"));
12893
12319
  return parts.join(`
12894
12320
 
12895
12321
  `);
@@ -12914,19 +12340,19 @@ function substituteVariables(template, values) {
12914
12340
  return template.replace(PLACEHOLDER_RE, (_full, name) => Object.prototype.hasOwnProperty.call(values, name) ? values[name] : "");
12915
12341
  }
12916
12342
  function resolveSafeChildFile(dir, fileName) {
12917
- const full = path21.resolve(dir, fileName);
12918
- if (full !== path21.join(dir, path21.basename(fileName)) || path21.dirname(full) !== dir) {
12343
+ const full = path20.resolve(dir, fileName);
12344
+ if (full !== path20.join(dir, path20.basename(fileName)) || path20.dirname(full) !== dir) {
12919
12345
  throw new Error(`Unsafe snippet filename: ${fileName}`);
12920
12346
  }
12921
12347
  return full;
12922
12348
  }
12923
12349
  function resolveSnippetPath(projectDir, fileName) {
12924
- return resolveSafeChildFile(path21.resolve(projectDir, SNIPPETS_DIR), fileName);
12350
+ return resolveSafeChildFile(path20.resolve(projectDir, SNIPPETS_DIR), fileName);
12925
12351
  }
12926
12352
  async function scanSnippetFiles(dir) {
12927
12353
  let names;
12928
12354
  try {
12929
- names = await readdir8(dir);
12355
+ names = await readdir7(dir);
12930
12356
  } catch {
12931
12357
  return [];
12932
12358
  }
@@ -12936,7 +12362,7 @@ async function scanSnippetFiles(dir) {
12936
12362
  continue;
12937
12363
  let body = "";
12938
12364
  try {
12939
- body = await readFile32(path21.join(dir, fileName), "utf8");
12365
+ body = await readFile31(path20.join(dir, fileName), "utf8");
12940
12366
  } catch {
12941
12367
  continue;
12942
12368
  }
@@ -12950,18 +12376,18 @@ async function scanSnippetFiles(dir) {
12950
12376
  return entries;
12951
12377
  }
12952
12378
  async function listSnippets(projectDir) {
12953
- const files = await scanSnippetFiles(path21.join(projectDir, SNIPPETS_DIR));
12379
+ const files = await scanSnippetFiles(path20.join(projectDir, SNIPPETS_DIR));
12954
12380
  return files.map((file) => ({ ...file, source: { kind: "project" } }));
12955
12381
  }
12956
12382
  async function readSnippet(projectDir, fileName) {
12957
- return readFile32(resolveSnippetPath(projectDir, fileName), "utf8");
12383
+ return readFile31(resolveSnippetPath(projectDir, fileName), "utf8");
12958
12384
  }
12959
12385
  async function saveSnippet(projectDir, name, body) {
12960
12386
  const stem = slugify(name);
12961
12387
  if (!stem)
12962
12388
  throw new Error(`Could not derive a filename from "${name}".`);
12963
12389
  const fileName = `${stem}.md`;
12964
- const dir = path21.join(projectDir, SNIPPETS_DIR);
12390
+ const dir = path20.join(projectDir, SNIPPETS_DIR);
12965
12391
  await mkdir12(dir, { recursive: true });
12966
12392
  await writeFile11(resolveSnippetPath(projectDir, fileName), body, "utf8");
12967
12393
  return { name, fileName, variables: extractVariables(body), source: { kind: "project" } };
@@ -12985,7 +12411,7 @@ async function listInstalledExtensions(projectDir) {
12985
12411
  function extensionSnippetsDir(ext) {
12986
12412
  if (pathEscapesFolder(ext.snippetsRel))
12987
12413
  return null;
12988
- return path21.join(ext.dir, ext.snippetsRel);
12414
+ return path20.join(ext.dir, ext.snippetsRel);
12989
12415
  }
12990
12416
  async function listMergedSnippets(projectDir) {
12991
12417
  const project = await listSnippets(projectDir);
@@ -13013,13 +12439,13 @@ async function readExtensionSnippet(projectDir, source, fileName) {
13013
12439
  if (!dir) {
13014
12440
  throw new Error(`Extension "${source.ref}" does not declare a usable snippets folder.`);
13015
12441
  }
13016
- return readFile32(resolveSafeChildFile(dir, fileName), "utf8");
12442
+ return readFile31(resolveSafeChildFile(dir, fileName), "utf8");
13017
12443
  }
13018
12444
  // src/lib/extension-import.ts
13019
12445
  import { existsSync as existsSync15 } from "node:fs";
13020
- import { cp as cp5, mkdir as mkdir13, mkdtemp as mkdtemp3, readFile as readFile33, rm as rm6, writeFile as writeFile12 } from "node:fs/promises";
12446
+ import { cp as cp5, mkdir as mkdir13, mkdtemp as mkdtemp3, readFile as readFile32, rm as rm6, writeFile as writeFile12 } from "node:fs/promises";
13021
12447
  import { tmpdir as tmpdir3 } from "node:os";
13022
- import path22 from "node:path";
12448
+ import path21 from "node:path";
13023
12449
  import { unzipSync } from "fflate";
13024
12450
  var MAX_THEME_ARCHIVE_BYTES = 25 * 1024 * 1024;
13025
12451
  var MAX_THEME_UNZIPPED_BYTES = 25 * 1024 * 1024;
@@ -13086,11 +12512,11 @@ function mb2(n) {
13086
12512
  return `${Math.round(n / (1024 * 1024))}MB`;
13087
12513
  }
13088
12514
  async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallbackName) {
13089
- const cssPath = path22.join(sourceDir, "theme.css");
12515
+ const cssPath = path21.join(sourceDir, "theme.css");
13090
12516
  if (!existsSync15(cssPath)) {
13091
12517
  throw new Error("A theme must contain a theme.css file.");
13092
12518
  }
13093
- const css = await readFile33(cssPath, "utf8");
12519
+ const css = await readFile32(cssPath, "utf8");
13094
12520
  const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
13095
12521
  if (reject) {
13096
12522
  throw new Error(`theme.css could not be parsed — ${reject.message}`);
@@ -13099,7 +12525,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
13099
12525
  for (const w of printFindings) {
13100
12526
  warnings.push({ code: "print-safety", message: w.message });
13101
12527
  }
13102
- const metaExists = existsSync15(path22.join(sourceDir, EXTENSION_MANIFEST_FILENAME));
12528
+ const metaExists = existsSync15(path21.join(sourceDir, EXTENSION_MANIFEST_FILENAME));
13103
12529
  const meta = metaExists ? await readExtensionMeta(sourceDir) : {};
13104
12530
  if (!metaExists) {
13105
12531
  warnings.push({
@@ -13118,7 +12544,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
13118
12544
  for (let i = 0;i < extraSheets.length; i++) {
13119
12545
  const rel = extraSheets[i];
13120
12546
  const sheetPath = extraSheetPaths[i];
13121
- const sheetCss = await readFile33(sheetPath, "utf8");
12547
+ const sheetCss = await readFile32(sheetPath, "utf8");
13122
12548
  const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
13123
12549
  if (sheetReject) {
13124
12550
  throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
@@ -13137,8 +12563,8 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
13137
12563
  });
13138
12564
  }
13139
12565
  const id = await uniqueExtensionId(projectDir, meta.name?.trim() || fallbackName);
13140
- const dest = path22.join(projectDir, EXTENSIONS_DIR, id);
13141
- await mkdir13(path22.dirname(dest), { recursive: true });
12566
+ const dest = path21.join(projectDir, EXTENSIONS_DIR, id);
12567
+ await mkdir13(path21.dirname(dest), { recursive: true });
13142
12568
  await cp5(sourceDir, dest, { recursive: true });
13143
12569
  await completeLandedManifest(dest, id, meta, metaExists);
13144
12570
  const entry = await addExtension(projectDir, `./${EXTENSIONS_DIR}/${id}`);
@@ -13147,11 +12573,11 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
13147
12573
  async function completeLandedManifest(dest, id, meta, metaExists) {
13148
12574
  if (metaExists && extensionStyleList(meta).length > 0)
13149
12575
  return;
13150
- const file = path22.join(dest, EXTENSION_MANIFEST_FILENAME);
12576
+ const file = path21.join(dest, EXTENSION_MANIFEST_FILENAME);
13151
12577
  let pkg = {};
13152
12578
  if (metaExists) {
13153
12579
  try {
13154
- const parsed = JSON.parse(await readFile33(file, "utf8"));
12580
+ const parsed = JSON.parse(await readFile32(file, "utf8"));
13155
12581
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
13156
12582
  pkg = parsed;
13157
12583
  }
@@ -13205,7 +12631,7 @@ async function importExtensionFromZip(projectDir, archive) {
13205
12631
  throw new Error("No theme.css found in the package. A theme needs a theme.css at the package root or inside a single folder.");
13206
12632
  }
13207
12633
  const prefix = root === "" ? "" : `${root}/`;
13208
- const tmp = await mkdtemp3(path22.join(tmpdir3(), "gutterpress-theme-"));
12634
+ const tmp = await mkdtemp3(path21.join(tmpdir3(), "gutterpress-theme-"));
13209
12635
  try {
13210
12636
  const relPaths = [];
13211
12637
  for (const [name, data] of Object.entries(files)) {
@@ -13217,12 +12643,12 @@ async function importExtensionFromZip(projectDir, archive) {
13217
12643
  const rel = prefix ? norm.slice(prefix.length) : norm;
13218
12644
  if (!rel || rel.split("/").some((seg) => seg === ".."))
13219
12645
  continue;
13220
- const dest = path22.join(tmp, rel);
13221
- if (dest !== tmp && !dest.startsWith(tmp + path22.sep)) {
12646
+ const dest = path21.join(tmp, rel);
12647
+ if (dest !== tmp && !dest.startsWith(tmp + path21.sep)) {
13222
12648
  throw new Error(`The package contains an unsafe path and was rejected: ${name}`);
13223
12649
  }
13224
12650
  relPaths.push(rel);
13225
- await mkdir13(path22.dirname(dest), { recursive: true });
12651
+ await mkdir13(path21.dirname(dest), { recursive: true });
13226
12652
  await writeFile12(dest, data);
13227
12653
  }
13228
12654
  return await finalizeExtensionImport(projectDir, tmp, relPaths, root || "imported-look");
@@ -13236,10 +12662,10 @@ async function importExtensionFromCssText(projectDir, css, name) {
13236
12662
  throw new Error(`The CSS could not be parsed — ${reject.message}`);
13237
12663
  }
13238
12664
  const displayName = name.trim() || "Imported theme";
13239
- const tmp = await mkdtemp3(path22.join(tmpdir3(), "gutterpress-theme-"));
12665
+ const tmp = await mkdtemp3(path21.join(tmpdir3(), "gutterpress-theme-"));
13240
12666
  try {
13241
- await writeFile12(path22.join(tmp, "theme.css"), css, "utf8");
13242
- await writeFile12(path22.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify({ name: slugify(displayName, "look"), gutterpress: { styles: ["theme.css"] } }, null, 2), "utf8");
12667
+ await writeFile12(path21.join(tmp, "theme.css"), css, "utf8");
12668
+ await writeFile12(path21.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify({ name: slugify(displayName, "look"), gutterpress: { styles: ["theme.css"] } }, null, 2), "utf8");
13243
12669
  const result = await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], displayName);
13244
12670
  result.warnings.unshift({
13245
12671
  code: "no-theme-json",
@@ -13251,14 +12677,14 @@ async function importExtensionFromCssText(projectDir, css, name) {
13251
12677
  }
13252
12678
  }
13253
12679
  async function importExtensionFromFile(projectDir, filePath) {
13254
- const ext = path22.extname(filePath).toLowerCase();
12680
+ const ext = path21.extname(filePath).toLowerCase();
13255
12681
  if (ext === ".zip") {
13256
- const buf = await readFile33(filePath);
12682
+ const buf = await readFile32(filePath);
13257
12683
  return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
13258
12684
  }
13259
12685
  if (ext === ".css") {
13260
- const css = await readFile33(filePath, "utf8");
13261
- return importExtensionFromCssText(projectDir, css, prettify(path22.basename(filePath, ".css")));
12686
+ const css = await readFile32(filePath, "utf8");
12687
+ return importExtensionFromCssText(projectDir, css, prettify(path21.basename(filePath, ".css")));
13262
12688
  }
13263
12689
  throw new Error("Choose a .zip extension package or a .css stylesheet.");
13264
12690
  }
@@ -13306,7 +12732,7 @@ async function importExtensionFromUrl(projectDir, url) {
13306
12732
  let baseName;
13307
12733
  if (looksLikeCssUrl(trimmed)) {
13308
12734
  css = await fetchText(trimmed);
13309
- baseName = path22.basename(new URL(trimmed, "https://x/").pathname, ".css") || "look";
12735
+ baseName = path21.basename(new URL(trimmed, "https://x/").pathname, ".css") || "look";
13310
12736
  } else {
13311
12737
  const base = trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
13312
12738
  css = await fetchText(`${base}theme.css`);
@@ -13315,14 +12741,14 @@ async function importExtensionFromUrl(projectDir, url) {
13315
12741
  if (parsed && typeof parsed === "object")
13316
12742
  meta = parsed;
13317
12743
  } catch {}
13318
- baseName = typeof meta.name === "string" && meta.name || path22.basename(new URL(base, "https://x/").pathname.replace(/\/$/, "")) || "look";
12744
+ baseName = typeof meta.name === "string" && meta.name || path21.basename(new URL(base, "https://x/").pathname.replace(/\/$/, "")) || "look";
13319
12745
  }
13320
12746
  if (!css.trim())
13321
12747
  throw new Error(`Fetched CSS from ${trimmed} was empty.`);
13322
12748
  assertLooksLikeCss(css, trimmed);
13323
- const tmp = await mkdtemp3(path22.join(tmpdir3(), "gutterpress-ext-"));
12749
+ const tmp = await mkdtemp3(path21.join(tmpdir3(), "gutterpress-ext-"));
13324
12750
  try {
13325
- await writeFile12(path22.join(tmp, "theme.css"), css, "utf8");
12751
+ await writeFile12(path21.join(tmp, "theme.css"), css, "utf8");
13326
12752
  const gp = meta.gutterpress && typeof meta.gutterpress === "object" && !Array.isArray(meta.gutterpress) ? meta.gutterpress : {};
13327
12753
  const finalMeta = {
13328
12754
  name: typeof meta.name === "string" && meta.name || slugify(baseName, "look"),
@@ -13334,7 +12760,7 @@ async function importExtensionFromUrl(projectDir, url) {
13334
12760
  preview: typeof gp.preview === "string" ? gp.preview : null
13335
12761
  }
13336
12762
  };
13337
- await writeFile12(path22.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify(finalMeta, null, 2), "utf8");
12763
+ await writeFile12(path21.join(tmp, EXTENSION_MANIFEST_FILENAME), JSON.stringify(finalMeta, null, 2), "utf8");
13338
12764
  return await finalizeExtensionImport(projectDir, tmp, ["theme.css", EXTENSION_MANIFEST_FILENAME], baseName);
13339
12765
  } finally {
13340
12766
  await rm6(tmp, { recursive: true, force: true });
@@ -13351,7 +12777,7 @@ var KIND_FOR_KEYWORD = {
13351
12777
  function invalid(reason) {
13352
12778
  throw new Error(`Malformed npm search response: ${reason}.`);
13353
12779
  }
13354
- function object4(value) {
12780
+ function object3(value) {
13355
12781
  return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
13356
12782
  }
13357
12783
  function str2(value) {
@@ -13364,7 +12790,7 @@ function parseSearchPage(text, keyword) {
13364
12790
  } catch {
13365
12791
  invalid("not valid JSON");
13366
12792
  }
13367
- const root = object4(parsed);
12793
+ const root = object3(parsed);
13368
12794
  if (!root)
13369
12795
  invalid("root is not an object");
13370
12796
  if (!Array.isArray(root.objects))
@@ -13374,10 +12800,10 @@ function parseSearchPage(text, keyword) {
13374
12800
  const packages = [];
13375
12801
  const scores = [];
13376
12802
  root.objects.forEach((raw, i) => {
13377
- const entry = object4(raw);
12803
+ const entry = object3(raw);
13378
12804
  if (!entry)
13379
12805
  invalid(`objects[${i}] is not an object`);
13380
- const pkg = object4(entry.package);
12806
+ const pkg = object3(entry.package);
13381
12807
  if (!pkg)
13382
12808
  invalid(`objects[${i}].package is not an object`);
13383
12809
  const name = str2(pkg.name);
@@ -13391,7 +12817,7 @@ function parseSearchPage(text, keyword) {
13391
12817
  return;
13392
12818
  if (name === "markdown-it" || name.startsWith("@types/"))
13393
12819
  return;
13394
- const links = object4(pkg.links) ?? {};
12820
+ const links = object3(pkg.links) ?? {};
13395
12821
  packages.push({
13396
12822
  name,
13397
12823
  version,
@@ -14146,8 +13572,8 @@ async function listGitHubBranches(credential, owner, repo, options = {}) {
14146
13572
  return branches;
14147
13573
  }
14148
13574
  // src/lib/remote-auth/clone.ts
14149
- import { mkdir as mkdir14, readdir as readdir9, rm as rm7 } from "node:fs/promises";
14150
- import path23 from "node:path";
13575
+ import { mkdir as mkdir14, readdir as readdir8, rm as rm7 } from "node:fs/promises";
13576
+ import path22 from "node:path";
14151
13577
  import git3 from "isomorphic-git";
14152
13578
 
14153
13579
  // src/lib/remote-auth/git-http.ts
@@ -14447,9 +13873,9 @@ function sanitizeCloneFolderName(name) {
14447
13873
  }
14448
13874
  async function assertCloneTarget(dir) {
14449
13875
  try {
14450
- const entries = await readdir9(dir);
13876
+ const entries = await readdir8(dir);
14451
13877
  if (entries.length > 0) {
14452
- throw new Error("That folder already has files in it. Choose an empty folder for the project.");
13878
+ throw new Error("That folder already has files in it. Choose an empty folder for the book.");
14453
13879
  }
14454
13880
  } catch (e) {
14455
13881
  if (e.code === "ENOENT")
@@ -14474,11 +13900,11 @@ function friendlyCloneError(e) {
14474
13900
  if (e instanceof Error && /already has files/i.test(e.message)) {
14475
13901
  return e;
14476
13902
  }
14477
- return new Error("The project couldn't be downloaded from GitHub. Please try again.", { cause: e });
13903
+ return new Error("The book couldn't be downloaded from GitHub. Please try again.", { cause: e });
14478
13904
  }
14479
13905
  async function cloneRepository(options) {
14480
13906
  const { dir, onProgress, tokenStore } = options;
14481
- if (!path23.isAbsolute(dir)) {
13907
+ if (!path22.isAbsolute(dir)) {
14482
13908
  throw new Error("cloneRepository requires an absolute destination path.");
14483
13909
  }
14484
13910
  let parsed;
@@ -14550,7 +13976,7 @@ var FAILURE_MESSAGES = {
14550
13976
  auth: "The Git server didn't accept the saved connection. Reconnect to this server with a current access token and try again.",
14551
13977
  "not-found": "The server answered, but no repository was found at that address. It may have been moved, renamed, or you may no longer have access.",
14552
13978
  unreachable: "Couldn't reach the Git server. Check your internet connection (and VPN, if this is a private server), then try again.",
14553
- "ssh-unsupported": "This project's online address uses SSH (git@…), which gutterpress can't check or sync with. Everything on this computer still works — sync with your usual Git tool.",
13979
+ "ssh-unsupported": "This book's online address uses SSH (git@…), which gutterpress can't check or sync with. Everything on this computer still works — sync with your usual Git tool.",
14554
13980
  "insecure-transport": "This address isn't secure, so the saved connection wasn't sent — connections are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to use a saved connection.",
14555
13981
  tls: "The server's security certificate couldn't be verified. If this is a private server with its own certificate, ask its administrator about trusting it (NODE_EXTRA_CA_CERTS).",
14556
13982
  unknown: "The connection test failed unexpectedly. The server may not be a Git server, or it may be temporarily unavailable."
@@ -14792,7 +14218,7 @@ import { randomUUID as randomUUID4 } from "node:crypto";
14792
14218
 
14793
14219
  // src/lib/remote-auth/converge-merge.ts
14794
14220
  import { mkdir as mkdir15, unlink as unlink3, writeFile as writeFile13 } from "node:fs/promises";
14795
- import path24 from "node:path";
14221
+ import path23 from "node:path";
14796
14222
  import git5 from "isomorphic-git";
14797
14223
  import diff3Merge from "diff3";
14798
14224
  function isMergeConflictError(e) {
@@ -14834,8 +14260,8 @@ async function readBlob(dir, cache2, oid, filepath) {
14834
14260
  return git5.readBlob({ fs: gitFs, dir, cache: cache2, oid, filepath });
14835
14261
  }
14836
14262
  async function writeTreeFile(dir, filepath, content) {
14837
- const abs = path24.join(dir, filepath);
14838
- await mkdir15(path24.dirname(abs), { recursive: true });
14263
+ const abs = path23.join(dir, filepath);
14264
+ await mkdir15(path23.dirname(abs), { recursive: true });
14839
14265
  await writeFile13(abs, content);
14840
14266
  }
14841
14267
  async function bothAddedPaths(dir, cache2, ourTip, theirTip, allowUnrelated) {
@@ -14937,7 +14363,7 @@ async function convergeMerge(params) {
14937
14363
  step("read-conflict-blobs", { filepath: p });
14938
14364
  const ourBlob = await readBlob(dir, cache2, ourTip, p);
14939
14365
  step("delete-conflict-file", { filepath: p });
14940
- await unlink3(path24.join(dir, p)).catch((e) => {
14366
+ await unlink3(path23.join(dir, p)).catch((e) => {
14941
14367
  if (e?.code !== "ENOENT")
14942
14368
  throw e;
14943
14369
  });
@@ -15237,7 +14663,7 @@ async function syncProject(options) {
15237
14663
  if (setupMsg)
15238
14664
  return { status: "error", message: setupMsg, ...base() };
15239
14665
  if (await historyUnreadable(dir)) {
15240
- logger.error("sync", "the project's history could not be read");
14666
+ logger.error("sync", "the book's history could not be read");
15241
14667
  return { status: "error", message: MSG_HISTORY_UNREADABLE, ...base() };
15242
14668
  }
15243
14669
  return { ...failureOutcome(e, snapshotId), ...filesChanged ? { filesChanged: true } : {} };
@@ -15283,8 +14709,8 @@ async function refreshRemoteCopies(options) {
15283
14709
  }
15284
14710
  }
15285
14711
  // src/lib/publish/run-publish.ts
15286
- import { readdir as readdir11, stat as stat11 } from "node:fs/promises";
15287
- import path28 from "node:path";
14712
+ import { readdir as readdir10, stat as stat11 } from "node:fs/promises";
14713
+ import path27 from "node:path";
15288
14714
 
15289
14715
  // src/lib/publish/command-runner.ts
15290
14716
  var CAPTURE_LIMIT = 64 * 1024;
@@ -15434,24 +14860,24 @@ ${tail}` : ""}`);
15434
14860
 
15435
14861
  // src/lib/publish/providers/guided.ts
15436
14862
  import { copyFile as copyFile2, mkdir as mkdir16, writeFile as writeFile14 } from "node:fs/promises";
15437
- import path25 from "node:path";
14863
+ import path24 from "node:path";
15438
14864
  async function stageGuidedPackage(req, providerId, listingLines) {
15439
- const packageDir = path25.join(path25.dirname(req.artifact.path), "publish", providerId);
14865
+ const packageDir = path24.join(path24.dirname(req.artifact.path), "publish", providerId);
15440
14866
  await mkdir16(packageDir, { recursive: true });
15441
- await copyFile2(req.artifact.path, path25.join(packageDir, path25.basename(req.artifact.path)));
14867
+ await copyFile2(req.artifact.path, path24.join(packageDir, path24.basename(req.artifact.path)));
15442
14868
  const { title, authors } = req.project;
15443
14869
  const sheet = [
15444
14870
  `# ${title || "Untitled"}`,
15445
14871
  "",
15446
14872
  `- **Authors:** ${authors.length ? authors.join(", ") : "(not set)"}`,
15447
- `- **File:** ${path25.basename(req.artifact.path)}`,
14873
+ `- **File:** ${path24.basename(req.artifact.path)}`,
15448
14874
  ...listingLines,
15449
14875
  "",
15450
14876
  "Copy these details into the platform's listing form when uploading.",
15451
14877
  ""
15452
14878
  ].join(`
15453
14879
  `);
15454
- await writeFile14(path25.join(packageDir, "LISTING.md"), sheet, "utf8");
14880
+ await writeFile14(path24.join(packageDir, "LISTING.md"), sheet, "utf8");
15455
14881
  return packageDir;
15456
14882
  }
15457
14883
 
@@ -15514,9 +14940,9 @@ var drivethrurpgProvider = {
15514
14940
  };
15515
14941
 
15516
14942
  // src/lib/publish/providers/gdrive.ts
15517
- import { mkdtemp as mkdtemp4, readFile as readFile34, readdir as readdir10, rm as rm8, stat as stat9, writeFile as writeFile15 } from "node:fs/promises";
14943
+ import { mkdtemp as mkdtemp4, readFile as readFile33, readdir as readdir9, rm as rm8, stat as stat9, writeFile as writeFile15 } from "node:fs/promises";
15518
14944
  import { tmpdir as tmpdir4 } from "node:os";
15519
- import path26 from "node:path";
14945
+ import path25 from "node:path";
15520
14946
  import { Zip, ZipDeflate } from "fflate";
15521
14947
 
15522
14948
  // src/lib/publish/google-drive.ts
@@ -15899,26 +15325,26 @@ function zipEntriesNonBlocking(entries, opts) {
15899
15325
  });
15900
15326
  }
15901
15327
  async function collectZipEntries(root, dir, out, onWarn) {
15902
- const entries = await readdir10(dir, { withFileTypes: true });
15328
+ const entries = await readdir9(dir, { withFileTypes: true });
15903
15329
  for (const entry of entries) {
15904
- const full = path26.join(dir, entry.name);
15330
+ const full = path25.join(dir, entry.name);
15905
15331
  if (entry.isDirectory()) {
15906
15332
  await collectZipEntries(root, full, out, onWarn);
15907
15333
  } else if (entry.isFile()) {
15908
- const rel = path26.relative(root, full).split(path26.sep).join("/");
15909
- out[rel] = await readFile34(full);
15334
+ const rel = path25.relative(root, full).split(path25.sep).join("/");
15335
+ out[rel] = await readFile33(full);
15910
15336
  } else if (entry.isSymbolicLink()) {
15911
15337
  const resolved = await stat9(full).catch(() => null);
15912
15338
  if (resolved?.isFile()) {
15913
- const rel = path26.relative(root, full).split(path26.sep).join("/");
15914
- out[rel] = await readFile34(full);
15339
+ const rel = path25.relative(root, full).split(path25.sep).join("/");
15340
+ out[rel] = await readFile33(full);
15915
15341
  } else if (resolved?.isDirectory()) {
15916
15342
  await collectZipEntries(root, full, out, onWarn);
15917
15343
  } else {
15918
- onWarn?.(`Skipped "${path26.relative(root, full)}" in the website export — it's a broken symlink.`);
15344
+ onWarn?.(`Skipped "${path25.relative(root, full)}" in the website export — it's a broken symlink.`);
15919
15345
  }
15920
15346
  } else {
15921
- onWarn?.(`Skipped "${path26.relative(root, full)}" in the website export — not a file, folder, or symlink.`);
15347
+ onWarn?.(`Skipped "${path25.relative(root, full)}" in the website export — not a file, folder, or symlink.`);
15922
15348
  }
15923
15349
  }
15924
15350
  }
@@ -15926,9 +15352,9 @@ async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeF
15926
15352
  const entries = {};
15927
15353
  await collectZipEntries(exportDir, exportDir, entries, onWarn);
15928
15354
  const fileName = `${bookSlug(title)}-website.zip`;
15929
- const tmpRoot = await mkdtemp4(path26.join(tmpdir4(), "gutterpress-gdrive-"));
15355
+ const tmpRoot = await mkdtemp4(path25.join(tmpdir4(), "gutterpress-gdrive-"));
15930
15356
  try {
15931
- const filePath = path26.join(tmpRoot, fileName);
15357
+ const filePath = path25.join(tmpRoot, fileName);
15932
15358
  const zipped = await zipEntriesNonBlocking(entries, { level: 6 });
15933
15359
  await writeArchiveImpl(filePath, zipped);
15934
15360
  return {
@@ -15976,7 +15402,7 @@ var gdriveProvider = {
15976
15402
  const isHtml = req.artifact.format === "html";
15977
15403
  const source = isHtml ? await zipHtmlExport(req.artifact.path, req.project.title, (msg) => req.deps.onProgress?.(msg)) : {
15978
15404
  filePath: req.artifact.path,
15979
- fileName: path26.basename(req.artifact.path),
15405
+ fileName: path25.basename(req.artifact.path),
15980
15406
  mimeType: "application/pdf",
15981
15407
  cleanup: async () => {}
15982
15408
  };
@@ -16027,7 +15453,7 @@ var gdriveProvider = {
16027
15453
 
16028
15454
  // src/lib/publish/butler.ts
16029
15455
  import { chmod as chmod2, mkdir as mkdir17, stat as stat10, writeFile as writeFile16 } from "node:fs/promises";
16030
- import path27 from "node:path";
15456
+ import path26 from "node:path";
16031
15457
  import { unzipSync as unzipSync2 } from "fflate";
16032
15458
  var BUTLER_DOWNLOAD_TIMEOUT_MS = 300000;
16033
15459
  function butlerBrothChannel(platform2 = process.platform, arch2 = process.arch) {
@@ -16064,8 +15490,8 @@ async function ensureButler(deps) {
16064
15490
  }
16065
15491
  if (await commandExists("butler", runCommand, deps.env))
16066
15492
  return "butler";
16067
- const cacheDir = path27.join(deps.configDir ?? defaultConfigDir(), "tools", "butler");
16068
- const cached = path27.join(cacheDir, butlerBinaryName());
15493
+ const cacheDir = path26.join(deps.configDir ?? defaultConfigDir(), "tools", "butler");
15494
+ const cached = path26.join(cacheDir, butlerBinaryName());
16069
15495
  if (await fileExists2(cached))
16070
15496
  return cached;
16071
15497
  const channel = butlerBrothChannel();
@@ -16092,9 +15518,9 @@ async function ensureButler(deps) {
16092
15518
  for (const [name, data] of Object.entries(files)) {
16093
15519
  if (name.endsWith("/"))
16094
15520
  continue;
16095
- const target = path27.join(cacheDir, path27.basename(name));
15521
+ const target = path26.join(cacheDir, path26.basename(name));
16096
15522
  await writeFile16(target, data);
16097
- if (path27.basename(name) === butlerBinaryName()) {
15523
+ if (path26.basename(name) === butlerBinaryName()) {
16098
15524
  await chmod2(target, 493).catch(() => {});
16099
15525
  foundBinary = true;
16100
15526
  }
@@ -16543,9 +15969,9 @@ async function resolvePublishRequest(options, deps) {
16543
15969
  const effectiveDeps = effectiveAccount ? { ...deps, credentialAccount: effectiveAccount } : deps;
16544
15970
  const outDir = resolveOutputDir(manifestDir, config.title);
16545
15971
  const effectiveFormat = resolvePublishFormat(provider.info, providerConfig);
16546
- const defaultArtifact = effectiveFormat === "pdf" ? path28.join(outDir, artifactName(config.title, "pdf")) : outDir;
15972
+ const defaultArtifact = effectiveFormat === "pdf" ? path27.join(outDir, artifactName(config.title, "pdf")) : outDir;
16547
15973
  const artifact = {
16548
- path: options.artifactPath ? path28.resolve(options.projectDir, options.artifactPath) : defaultArtifact,
15974
+ path: options.artifactPath ? path27.resolve(options.projectDir, options.artifactPath) : defaultArtifact,
16549
15975
  format: effectiveFormat
16550
15976
  };
16551
15977
  return {
@@ -16609,7 +16035,7 @@ async function artifactIssues(artifact) {
16609
16035
  var BUILD_FINGERPRINT_FILENAME = "build-fingerprint.json";
16610
16036
  async function htmlDirIssues(dir) {
16611
16037
  const issues = [];
16612
- const isThere = async (rel) => stat11(path28.join(dir, rel)).then(() => true, () => false);
16038
+ const isThere = async (rel) => stat11(path27.join(dir, rel)).then(() => true, () => false);
16613
16039
  if (!await isThere(BOOK_HTML)) {
16614
16040
  issues.push({
16615
16041
  severity: "error",
@@ -16617,7 +16043,7 @@ async function htmlDirIssues(dir) {
16617
16043
  message: `${dir} has no ${BOOK_HTML} — it isn't an HTML export. ${HTML_HINT}`
16618
16044
  });
16619
16045
  }
16620
- const entries = await readdir11(dir).catch(() => []);
16046
+ const entries = await readdir10(dir).catch(() => []);
16621
16047
  const extras = entries.filter((name) => name.toLowerCase().endsWith(".pdf") || name === BUILD_FINGERPRINT_FILENAME);
16622
16048
  if (extras.length > 0) {
16623
16049
  issues.push({
@@ -16728,17 +16154,17 @@ async function disconnectPublishCredential(key, deps, options = {}) {
16728
16154
  }
16729
16155
  }
16730
16156
  // src/lib/publish/selections.ts
16731
- import { chmod as chmod3, mkdir as mkdir18, readFile as readFile35, writeFile as writeFile17 } from "node:fs/promises";
16732
- import path29 from "node:path";
16157
+ import { chmod as chmod3, mkdir as mkdir18, readFile as readFile34, writeFile as writeFile17 } from "node:fs/promises";
16158
+ import path28 from "node:path";
16733
16159
  class PublishSelectionsStore {
16734
16160
  filePath;
16735
16161
  queue = Promise.resolve();
16736
16162
  constructor(filePath) {
16737
- this.filePath = filePath ?? path29.join(defaultConfigDir(), "publish-selections.json");
16163
+ this.filePath = filePath ?? path28.join(defaultConfigDir(), "publish-selections.json");
16738
16164
  }
16739
16165
  async read() {
16740
16166
  try {
16741
- const raw = await readFile35(this.filePath, "utf8");
16167
+ const raw = await readFile34(this.filePath, "utf8");
16742
16168
  const parsed = JSON.parse(raw);
16743
16169
  if (parsed && typeof parsed === "object") {
16744
16170
  return {
@@ -16751,7 +16177,7 @@ class PublishSelectionsStore {
16751
16177
  return { version: 1, global: {}, projects: {} };
16752
16178
  }
16753
16179
  async write(data) {
16754
- await mkdir18(path29.dirname(this.filePath), { recursive: true });
16180
+ await mkdir18(path28.dirname(this.filePath), { recursive: true });
16755
16181
  await writeFile17(this.filePath, JSON.stringify(data, null, 2), {
16756
16182
  encoding: "utf8",
16757
16183
  mode: 384
@@ -16854,4 +16280,4 @@ async function setPublishProviderConfig(projectDir, providerId, values) {
16854
16280
  await writeManifestDoc(file, doc);
16855
16281
  return settingsFromDoc(doc);
16856
16282
  }
16857
- export { DEFAULT_NPM_REGISTRY, npmRegistryUrl, PLUGINS_DIR, BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, SYNC_SNAPSHOT_MESSAGE, log, resolveActiveStyles, listProjectStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, loadPluginsWithCss, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, rulePageContainment, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listBuiltInStyleSets, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets, readSnippet, saveSnippet, deleteSnippet, listMergedSnippets, readExtensionSnippet, importExtensionFromFile, importExtensionFromUrl, MAX_SEARCH_BYTES, SEARCH_KEYWORDS, searchNpmExtensions, setManifestFields, readManifestFields, setActiveStyles, AUTO_SNAPSHOT_DEFAULT_MINUTES, AUTO_SYNC_MIN_MINUTES, AUTO_SYNC_MAX_MINUTES, AUTO_SYNC_DEFAULT_MINUTES, AUTO_SYNC_PUSH_INTERVAL_MINUTES, autoSnapshotDelayMs, autoSyncDelayMs, isGitInternalPath, resolveGitHubClientId, GITHUB_HOST, GitHubAuthProvider, GDRIVE_HOST, resolveGoogleClientId, resolveGoogleClientSecret, GOOGLE_NOT_CONFIGURED_MESSAGE, requireGoogleClientCredentials, pkceChallengeFromVerifier, GoogleAuthProvider, revokeGoogleCredential, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listGitHubRepositories, listRepoBooks, listGitHubBranches, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, syncProject, refreshRemoteCopies, listPublishProviders, publishProviderFor, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig };
16283
+ export { BUNDLED_EXTENSIONS, isBundledExtension, isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, hasProjectManifest, loadManifest, loadManifestWithPath, resolveConfig, SYNC_SNAPSHOT_MESSAGE, log, resolveActiveStyles, listProjectStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, pathEscapesFolder, assertExtensionContained, resolveExtension, loadPluginsWithCss, getChecks, getCheckById, resolveCheckSelectors, formatReport, ruleRemoteUrls, ruleRiskyProps, ruleSyntax, rulePageContainment, checkCss, inspectImage, runChecks, checkToolAvailability, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, defaultConfigDir, FileTokenStore, extractUrlCredential, getSystemDiagnostics, slugifyProjectName, escapeYamlScalar, scaffoldProject, adoptFolder, ensureGitignoreHasDist, EXTENSION_KINDS, RESERVED_PREFIX, resolveExtensionPrefix, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, listBuiltInTemplates, saveProjectAsTemplate, listCustomTemplates, importTemplateFromFolder, EXTENSIONS_DIR, RECOMMENDED_EXTENSIONS, BUILT_IN_STYLE_SET_IDS, sameExtension, listProjectExtensions, describeExtension, setExtensionEnabled, reorderExtensions, removeExtension, addExtension, listBuiltInStyleSets, addBuiltInStyleSet, validateProjectExtensions, readExtensionCss, SNIPPETS_DIR, extractVariables, substituteVariables, listSnippets, readSnippet, saveSnippet, deleteSnippet, listMergedSnippets, readExtensionSnippet, importExtensionFromFile, importExtensionFromUrl, MAX_SEARCH_BYTES, SEARCH_KEYWORDS, searchNpmExtensions, setManifestFields, readManifestFields, setActiveStyles, AUTO_SNAPSHOT_DEFAULT_MINUTES, AUTO_SYNC_MIN_MINUTES, AUTO_SYNC_MAX_MINUTES, AUTO_SYNC_DEFAULT_MINUTES, AUTO_SYNC_PUSH_INTERVAL_MINUTES, autoSnapshotDelayMs, autoSyncDelayMs, isGitInternalPath, resolveGitHubClientId, GITHUB_HOST, GitHubAuthProvider, GDRIVE_HOST, resolveGoogleClientId, resolveGoogleClientSecret, GOOGLE_NOT_CONFIGURED_MESSAGE, requireGoogleClientCredentials, pkceChallengeFromVerifier, GoogleAuthProvider, revokeGoogleCredential, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listGitHubRepositories, listRepoBooks, listGitHubBranches, sanitizeCloneFolderName, cloneRepository, isSshRemoteUrl, testRemoteAccess, knownForgeTokenUrl, connectGenericHost, parseRemoteOrigin, forgeKindForHost, diagnoseProjectRemote, syncProject, refreshRemoteCopies, listPublishProviders, publishProviderFor, resolvePublishFormat, resolvePublishRequest, runPublish, connectPublishProvider, disconnectPublishCredential, PublishSelectionsStore, readPublishSettings, setPublishProviderConfig };