gutterpress 0.10.0 → 0.10.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -16
- package/dist/api/index.d.ts +10 -19
- package/dist/api/index.js +8 -64
- package/dist/{audit-cvarpa72.js → audit-dce99css.js} +5 -5
- package/dist/{build-6r502chw.js → build-bj4x0727.js} +5 -5
- package/dist/checks/source/index.d.ts +1 -0
- package/dist/checks/source/merge-markers.d.ts +3 -0
- package/dist/{cli-cqtggsng.js → cli-0z6zf98d.js} +2 -47
- package/dist/{cli-eq5naw4m.js → cli-7n848af2.js} +370 -1249
- package/dist/{cli-zfcryxg8.js → cli-eewynen9.js} +97 -60
- package/dist/{cli-k1065rkg.js → cli-w692g6dx.js} +75 -29
- package/dist/{cli-149edp6b.js → cli-ydpf09d9.js} +1 -1
- package/dist/cli.js +14 -15
- package/dist/{doctor-akvxbtjb.js → doctor-7nd6n2aa.js} +2 -2
- package/dist/{engine-ft4cr3ep.js → engine-57vw8zpd.js} +1 -1
- package/dist/{engine-b159tbns.js → engine-8kdwy0jz.js} +2 -2
- package/dist/{gutterpress-viewer-te8g5grx.js → gutterpress-viewer-tma1qwz2.js} +57 -15
- package/dist/{index-ycpvr0am.js → index-7hrvksrb.js} +4 -4
- package/dist/{index-mdefp0y5.js → index-dtrftayr.js} +75 -27
- package/dist/{index-ge7q9xj3.js → index-h1x7krkg.js} +771 -1482
- package/dist/{index-wq3r5pj7.js → index-p0ebr7dh.js} +94 -57
- package/dist/index.js +9 -65
- package/dist/lib/git-fs.d.ts +43 -0
- package/dist/lib/host-policy.d.ts +14 -2
- package/dist/lib/remote-auth/clone.d.ts +0 -21
- package/dist/lib/remote-auth/converge-merge.d.ts +24 -7
- package/dist/lib/remote-auth/generic-auth.d.ts +0 -22
- package/dist/lib/remote-auth/git-http.d.ts +1 -1
- package/dist/lib/remote-auth/github-auth.d.ts +2 -22
- package/dist/lib/remote-auth/operation-log.d.ts +4 -9
- package/dist/lib/remote-auth/sync-messages.d.ts +35 -12
- package/dist/lib/remote-auth/sync-types.d.ts +30 -101
- package/dist/lib/remote-auth/sync.d.ts +19 -21
- package/dist/lib/remote-auth/token-store.d.ts +0 -10
- package/dist/lib/remote-auth/transport.d.ts +42 -40
- package/dist/lib/source-provider.d.ts +12 -0
- package/dist/{lint-p2sw53d9.js → lint-c8ze8sqw.js} +5 -5
- package/dist/{new-hvq0x91q.js → new-xy2qc300.js} +5 -5
- package/dist/{plugin-pssmk0dx.js → plugin-g9hwzjsm.js} +5 -5
- package/dist/{preflight-4j00yd0g.js → preflight-prcvdvmk.js} +5 -5
- package/dist/{preview-bridge-fz7vpk8m.js → preview-bridge-2nnrs2hy.js} +15 -0
- package/dist/{preview-d4s6gk6p.js → preview-h479apj9.js} +5 -5
- package/dist/{preview-interface-0ssk8bmm.js → preview-interface-2f5v96hd.js} +411 -88
- package/dist/{preview-shell-c5mfa3q0.js → preview-shell-kyjq59d9.js} +80 -25
- package/dist/{publish-tkc26en7.js → publish-emk644a8.js} +5 -5
- package/dist/{source-provider-3tcj6qg2.js → source-provider-2j5x2hyw.js} +1 -1
- package/dist/{source-provider-vanafrt9.js → source-provider-kn41jmbs.js} +1 -1
- package/dist/{validate-w9vfefrw.js → validate-be54bdc6.js} +5 -5
- package/package.json +1 -1
- package/dist/lib/app-heartbeat.d.ts +0 -102
- package/dist/lib/remote-auth/image-clash.d.ts +0 -17
- package/dist/lib/remote-auth/recovery/classify.d.ts +0 -97
- package/dist/lib/remote-auth/recovery/inspect.d.ts +0 -75
- package/dist/lib/remote-auth/recovery/locks.d.ts +0 -20
- package/dist/lib/remote-auth/recovery/repair.d.ts +0 -29
- package/dist/lib/remote-auth/recovery/types.d.ts +0 -39
- package/dist/repair-2va4w12t.js +0 -137
|
@@ -2,11 +2,9 @@ import {
|
|
|
2
2
|
PACKAGE_META,
|
|
3
3
|
PACKAGE_VERSION,
|
|
4
4
|
convertToPdfxCmyk,
|
|
5
|
-
credentialHostKey,
|
|
6
5
|
debug,
|
|
7
6
|
defaultConfigDir,
|
|
8
7
|
error,
|
|
9
|
-
extractUrlCredential,
|
|
10
8
|
hasLiveTransparency,
|
|
11
9
|
info,
|
|
12
10
|
log,
|
|
@@ -15,12 +13,12 @@ import {
|
|
|
15
13
|
stampCreator,
|
|
16
14
|
stripAnnotations,
|
|
17
15
|
warn
|
|
18
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-0z6zf98d.js";
|
|
19
17
|
import {
|
|
20
18
|
DEBOUNCE,
|
|
21
19
|
UsageError,
|
|
22
20
|
resolvePort
|
|
23
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-ydpf09d9.js";
|
|
24
22
|
import {
|
|
25
23
|
MARGIN_BOX_IGNORED_PROPERTIES,
|
|
26
24
|
RENDER_TIMEOUT_MS,
|
|
@@ -29,7 +27,7 @@ import {
|
|
|
29
27
|
getAssetPath,
|
|
30
28
|
getBrowser,
|
|
31
29
|
prewarmBrowser
|
|
32
|
-
} from "./cli-
|
|
30
|
+
} from "./cli-eewynen9.js";
|
|
33
31
|
import {
|
|
34
32
|
INSTALL_HINTS,
|
|
35
33
|
execCapture,
|
|
@@ -42,19 +40,10 @@ import {
|
|
|
42
40
|
BuildError
|
|
43
41
|
} from "./cli-46ycxe6r.js";
|
|
44
42
|
import {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
hasPendingChanges,
|
|
48
|
-
hasUncommittedChanges,
|
|
49
|
-
isNoChangesError,
|
|
50
|
-
resolveGitAuthor,
|
|
51
|
-
resolveLogger,
|
|
52
|
-
snapshotWorkingTreeUnlocked,
|
|
53
|
-
withRepoLock
|
|
54
|
-
} from "./cli-k1065rkg.js";
|
|
43
|
+
hasUncommittedChanges
|
|
44
|
+
} from "./cli-w692g6dx.js";
|
|
55
45
|
import {
|
|
56
|
-
detectProjectSource
|
|
57
|
-
syncRemoteFor
|
|
46
|
+
detectProjectSource
|
|
58
47
|
} from "./cli-v5mp7a6q.js";
|
|
59
48
|
import {
|
|
60
49
|
__require
|
|
@@ -5148,11 +5137,9 @@ var check22 = {
|
|
|
5148
5137
|
};
|
|
5149
5138
|
registerCheck(check22);
|
|
5150
5139
|
|
|
5151
|
-
// src/checks/
|
|
5152
|
-
import {
|
|
5153
|
-
|
|
5154
|
-
// src/lib/image-inspect.ts
|
|
5155
|
-
import { stat as stat2, readFile as readFile15 } from "node:fs/promises";
|
|
5140
|
+
// src/checks/source/merge-markers.ts
|
|
5141
|
+
import { readFile as readFile15 } from "node:fs/promises";
|
|
5142
|
+
import path4 from "node:path";
|
|
5156
5143
|
|
|
5157
5144
|
// src/checks/asset/extensions.ts
|
|
5158
5145
|
var RASTER_INSPECTABLE_EXTS = [
|
|
@@ -5175,27 +5162,108 @@ var ASSET_SCAN_IGNORE_GLOBS = [
|
|
|
5175
5162
|
"**/dist/**"
|
|
5176
5163
|
];
|
|
5177
5164
|
|
|
5165
|
+
// src/checks/source/merge-markers.ts
|
|
5166
|
+
var OPEN_SENTINEL = "<<<<<<< your version";
|
|
5167
|
+
var CLOSE_SENTINEL = ">>>>>>> online version";
|
|
5168
|
+
function withoutOnlineTag(relPath) {
|
|
5169
|
+
return relPath.replace(/\.online(?=\.[^./]*$|$)/, "");
|
|
5170
|
+
}
|
|
5171
|
+
var check23 = {
|
|
5172
|
+
id: "source.sync.merge-markers",
|
|
5173
|
+
name: "Combined Versions",
|
|
5174
|
+
description: "Finds passages and files still holding two versions (yours and the online copy) after a sync",
|
|
5175
|
+
category: "source",
|
|
5176
|
+
phase: "pre-build",
|
|
5177
|
+
async run(ctx) {
|
|
5178
|
+
const results = [];
|
|
5179
|
+
const textFiles = [...ctx.markdownFiles ?? [], ...ctx.cssFiles ?? []].sort();
|
|
5180
|
+
for (const file of textFiles) {
|
|
5181
|
+
let content;
|
|
5182
|
+
try {
|
|
5183
|
+
content = await readFile15(file, "utf8");
|
|
5184
|
+
} catch {
|
|
5185
|
+
results.push(inspectionFailed(check23.id, `Could not read source file: ${file}`, { file }));
|
|
5186
|
+
continue;
|
|
5187
|
+
}
|
|
5188
|
+
if (!content.includes(OPEN_SENTINEL) && !content.includes(CLOSE_SENTINEL))
|
|
5189
|
+
continue;
|
|
5190
|
+
let inBlock = false;
|
|
5191
|
+
const lines = content.split(`
|
|
5192
|
+
`);
|
|
5193
|
+
for (let i = 0;i < lines.length; i++) {
|
|
5194
|
+
const line = lines[i].endsWith("\r") ? lines[i].slice(0, -1) : lines[i];
|
|
5195
|
+
if (line === OPEN_SENTINEL) {
|
|
5196
|
+
inBlock = true;
|
|
5197
|
+
results.push(finding(check23.id, {
|
|
5198
|
+
severity: "error",
|
|
5199
|
+
code: "two-versions-passage",
|
|
5200
|
+
message: "This passage has two versions (yours and the online copy) — keep what you want, then delete the marker lines.",
|
|
5201
|
+
file,
|
|
5202
|
+
line: i + 1
|
|
5203
|
+
}));
|
|
5204
|
+
} else if (line === CLOSE_SENTINEL) {
|
|
5205
|
+
if (!inBlock) {
|
|
5206
|
+
results.push(finding(check23.id, {
|
|
5207
|
+
severity: "error",
|
|
5208
|
+
code: "leftover-version-marker",
|
|
5209
|
+
message: "This marker line is left over from combining two versions — delete it.",
|
|
5210
|
+
file,
|
|
5211
|
+
line: i + 1
|
|
5212
|
+
}));
|
|
5213
|
+
}
|
|
5214
|
+
inBlock = false;
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
try {
|
|
5219
|
+
const { glob } = await import("glob");
|
|
5220
|
+
const siblings = await glob(["**/*.online", "**/*.online.*"], {
|
|
5221
|
+
cwd: ctx.inputDir,
|
|
5222
|
+
absolute: true,
|
|
5223
|
+
ignore: [...ASSET_SCAN_IGNORE_GLOBS]
|
|
5224
|
+
});
|
|
5225
|
+
for (const sibling of siblings.sort()) {
|
|
5226
|
+
const rel = path4.relative(ctx.inputDir, sibling).split(path4.sep).join("/");
|
|
5227
|
+
results.push(finding(check23.id, {
|
|
5228
|
+
severity: "warning",
|
|
5229
|
+
code: "kept-both-versions",
|
|
5230
|
+
message: `Two versions of ${withoutOnlineTag(rel)} are in your project — keep the one you want, then delete the other.`,
|
|
5231
|
+
file: sibling
|
|
5232
|
+
}));
|
|
5233
|
+
}
|
|
5234
|
+
} catch (error2) {
|
|
5235
|
+
results.push(inspectionFailed(check23.id, `Could not scan for kept-both files: ${error2 instanceof Error ? error2.message : String(error2)}`));
|
|
5236
|
+
}
|
|
5237
|
+
return results;
|
|
5238
|
+
}
|
|
5239
|
+
};
|
|
5240
|
+
registerCheck(check23);
|
|
5241
|
+
|
|
5242
|
+
// src/checks/asset/image-file-size.ts
|
|
5243
|
+
import { stat as stat3 } from "node:fs/promises";
|
|
5244
|
+
|
|
5178
5245
|
// src/lib/image-inspect.ts
|
|
5246
|
+
import { stat as stat2, readFile as readFile16 } from "node:fs/promises";
|
|
5179
5247
|
var DEFAULT_DPI = 72;
|
|
5180
5248
|
var cache = new Map;
|
|
5181
|
-
async function inspectImage(
|
|
5249
|
+
async function inspectImage(path5) {
|
|
5182
5250
|
let st;
|
|
5183
5251
|
try {
|
|
5184
|
-
st = await stat2(
|
|
5252
|
+
st = await stat2(path5);
|
|
5185
5253
|
} catch {
|
|
5186
5254
|
return null;
|
|
5187
5255
|
}
|
|
5188
|
-
const hit = cache.get(
|
|
5256
|
+
const hit = cache.get(path5);
|
|
5189
5257
|
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
|
|
5190
5258
|
return hit.info;
|
|
5191
5259
|
let info2 = null;
|
|
5192
5260
|
try {
|
|
5193
|
-
const buf = await
|
|
5261
|
+
const buf = await readFile16(path5);
|
|
5194
5262
|
info2 = parseImage(buf);
|
|
5195
5263
|
} catch {
|
|
5196
5264
|
info2 = null;
|
|
5197
5265
|
}
|
|
5198
|
-
cache.set(
|
|
5266
|
+
cache.set(path5, { mtimeMs: st.mtimeMs, size: st.size, info: info2 });
|
|
5199
5267
|
return info2;
|
|
5200
5268
|
}
|
|
5201
5269
|
function parseImage(b) {
|
|
@@ -5395,7 +5463,7 @@ async function collectImageFiles(dirs, exts, ignore = ASSET_SCAN_IGNORE_GLOBS) {
|
|
|
5395
5463
|
}
|
|
5396
5464
|
|
|
5397
5465
|
// src/checks/asset/image-file-size.ts
|
|
5398
|
-
var
|
|
5466
|
+
var check24 = {
|
|
5399
5467
|
id: "asset.image.file-size",
|
|
5400
5468
|
name: "Image File Size",
|
|
5401
5469
|
description: "Checks that image files do not exceed the maximum size limit",
|
|
@@ -5416,14 +5484,14 @@ var check23 = {
|
|
|
5416
5484
|
if (info2.size > maxSize) {
|
|
5417
5485
|
const sizeMb = (info2.size / 1e6).toFixed(1);
|
|
5418
5486
|
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
5419
|
-
results.push(finding(
|
|
5487
|
+
results.push(finding(check24.id, {
|
|
5420
5488
|
severity: "warning",
|
|
5421
5489
|
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
5422
5490
|
file
|
|
5423
5491
|
}));
|
|
5424
5492
|
}
|
|
5425
5493
|
} catch {
|
|
5426
|
-
results.push(inspectionFailed(
|
|
5494
|
+
results.push(inspectionFailed(check24.id, `Could not stat image file: ${file}`, {
|
|
5427
5495
|
file
|
|
5428
5496
|
}));
|
|
5429
5497
|
}
|
|
@@ -5431,10 +5499,10 @@ var check23 = {
|
|
|
5431
5499
|
return results;
|
|
5432
5500
|
}
|
|
5433
5501
|
};
|
|
5434
|
-
registerCheck(
|
|
5502
|
+
registerCheck(check24);
|
|
5435
5503
|
|
|
5436
5504
|
// src/checks/asset/image-resolution.ts
|
|
5437
|
-
var
|
|
5505
|
+
var check25 = {
|
|
5438
5506
|
id: "asset.image.resolution",
|
|
5439
5507
|
name: "Image Resolution",
|
|
5440
5508
|
description: "Checks source image DPI from embedded density metadata",
|
|
@@ -5456,7 +5524,7 @@ var check24 = {
|
|
|
5456
5524
|
const { xDpi, yDpi } = info2;
|
|
5457
5525
|
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
5458
5526
|
results.push({
|
|
5459
|
-
checkId:
|
|
5527
|
+
checkId: check25.id,
|
|
5460
5528
|
severity: "warning",
|
|
5461
5529
|
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
5462
5530
|
file
|
|
@@ -5466,7 +5534,7 @@ var check24 = {
|
|
|
5466
5534
|
return results;
|
|
5467
5535
|
}
|
|
5468
5536
|
};
|
|
5469
|
-
registerCheck(
|
|
5537
|
+
registerCheck(check25);
|
|
5470
5538
|
|
|
5471
5539
|
// src/checks/asset/image-color-space.ts
|
|
5472
5540
|
var LABEL = {
|
|
@@ -5474,7 +5542,7 @@ var LABEL = {
|
|
|
5474
5542
|
gray: "Gray",
|
|
5475
5543
|
cmyk: "CMYK"
|
|
5476
5544
|
};
|
|
5477
|
-
var
|
|
5545
|
+
var check26 = {
|
|
5478
5546
|
id: "asset.image.color-space",
|
|
5479
5547
|
name: "Image Color Space",
|
|
5480
5548
|
description: "Validates image color spaces against allowed list",
|
|
@@ -5495,7 +5563,7 @@ var check25 = {
|
|
|
5495
5563
|
const cs = info2?.colorSpace;
|
|
5496
5564
|
if (cs && !allowedLower.has(cs)) {
|
|
5497
5565
|
results.push({
|
|
5498
|
-
checkId:
|
|
5566
|
+
checkId: check26.id,
|
|
5499
5567
|
severity: "warning",
|
|
5500
5568
|
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
5501
5569
|
file
|
|
@@ -5505,10 +5573,10 @@ var check25 = {
|
|
|
5505
5573
|
return results;
|
|
5506
5574
|
}
|
|
5507
5575
|
};
|
|
5508
|
-
registerCheck(
|
|
5576
|
+
registerCheck(check26);
|
|
5509
5577
|
|
|
5510
5578
|
// src/checks/asset/image-alpha.ts
|
|
5511
|
-
var
|
|
5579
|
+
var check27 = {
|
|
5512
5580
|
id: "asset.image.alpha-channel",
|
|
5513
5581
|
name: "Image Alpha Channel",
|
|
5514
5582
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -5526,7 +5594,7 @@ var check26 = {
|
|
|
5526
5594
|
const info2 = await inspectImage(file);
|
|
5527
5595
|
if (info2?.hasAlpha) {
|
|
5528
5596
|
results.push({
|
|
5529
|
-
checkId:
|
|
5597
|
+
checkId: check27.id,
|
|
5530
5598
|
severity: "warning",
|
|
5531
5599
|
message: "Image contains alpha channel, which may cause print issues",
|
|
5532
5600
|
file
|
|
@@ -5536,10 +5604,10 @@ var check26 = {
|
|
|
5536
5604
|
return results;
|
|
5537
5605
|
}
|
|
5538
5606
|
};
|
|
5539
|
-
registerCheck(
|
|
5607
|
+
registerCheck(check27);
|
|
5540
5608
|
|
|
5541
5609
|
// src/checks/asset/image-tac.ts
|
|
5542
|
-
var
|
|
5610
|
+
var check28 = {
|
|
5543
5611
|
id: "asset.image.tac-raster",
|
|
5544
5612
|
name: "Image TAC (Raster)",
|
|
5545
5613
|
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
@@ -5558,7 +5626,7 @@ var check27 = {
|
|
|
5558
5626
|
const ghostscript = await resolveGhostscript();
|
|
5559
5627
|
if (!ghostscript) {
|
|
5560
5628
|
return [
|
|
5561
|
-
inspectionFailed(
|
|
5629
|
+
inspectionFailed(check28.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
5562
5630
|
];
|
|
5563
5631
|
}
|
|
5564
5632
|
for (const file of files) {
|
|
@@ -5576,7 +5644,7 @@ var check27 = {
|
|
|
5576
5644
|
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
5577
5645
|
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
5578
5646
|
if (tac > maxTac) {
|
|
5579
|
-
results.push(finding(
|
|
5647
|
+
results.push(finding(check28.id, {
|
|
5580
5648
|
severity: "warning",
|
|
5581
5649
|
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
5582
5650
|
file,
|
|
@@ -5588,16 +5656,16 @@ var check27 = {
|
|
|
5588
5656
|
}
|
|
5589
5657
|
}
|
|
5590
5658
|
} catch {
|
|
5591
|
-
results.push(inspectionFailed(
|
|
5659
|
+
results.push(inspectionFailed(check28.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
5592
5660
|
}
|
|
5593
5661
|
}
|
|
5594
5662
|
return results;
|
|
5595
5663
|
}
|
|
5596
5664
|
};
|
|
5597
|
-
registerCheck(
|
|
5665
|
+
registerCheck(check28);
|
|
5598
5666
|
|
|
5599
5667
|
// src/checks/asset/approved-fonts.ts
|
|
5600
|
-
var
|
|
5668
|
+
var check29 = {
|
|
5601
5669
|
id: "asset.font.approved-files",
|
|
5602
5670
|
name: "Approved Font Files",
|
|
5603
5671
|
description: "Checks font files against the approved file patterns",
|
|
@@ -5636,7 +5704,7 @@ var check28 = {
|
|
|
5636
5704
|
for (const font of allFonts) {
|
|
5637
5705
|
if (!approvedFonts.has(font)) {
|
|
5638
5706
|
results.push({
|
|
5639
|
-
checkId:
|
|
5707
|
+
checkId: check29.id,
|
|
5640
5708
|
severity: "warning",
|
|
5641
5709
|
message: "Font file not in approved list",
|
|
5642
5710
|
file: font
|
|
@@ -5646,7 +5714,7 @@ var check28 = {
|
|
|
5646
5714
|
return results;
|
|
5647
5715
|
}
|
|
5648
5716
|
};
|
|
5649
|
-
registerCheck(
|
|
5717
|
+
registerCheck(check29);
|
|
5650
5718
|
|
|
5651
5719
|
// src/checks/asset/font-license.ts
|
|
5652
5720
|
import { existsSync as existsSync7 } from "node:fs";
|
|
@@ -5661,7 +5729,7 @@ var LICENSE_NAMES = [
|
|
|
5661
5729
|
"OFL-1.1.txt",
|
|
5662
5730
|
"COPYING"
|
|
5663
5731
|
];
|
|
5664
|
-
var
|
|
5732
|
+
var check30 = {
|
|
5665
5733
|
id: "asset.font.license",
|
|
5666
5734
|
name: "Font License",
|
|
5667
5735
|
description: "Checks for license files in font directories",
|
|
@@ -5688,7 +5756,7 @@ var check29 = {
|
|
|
5688
5756
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync7(resolve6(fontDir, name)));
|
|
5689
5757
|
if (!hasLicense) {
|
|
5690
5758
|
results.push({
|
|
5691
|
-
checkId:
|
|
5759
|
+
checkId: check30.id,
|
|
5692
5760
|
severity: "warning",
|
|
5693
5761
|
message: `No font license file found in directory`,
|
|
5694
5762
|
file: fontDir
|
|
@@ -5698,10 +5766,10 @@ var check29 = {
|
|
|
5698
5766
|
return results;
|
|
5699
5767
|
}
|
|
5700
5768
|
};
|
|
5701
|
-
registerCheck(
|
|
5769
|
+
registerCheck(check30);
|
|
5702
5770
|
|
|
5703
5771
|
// src/checks/heuristic/text-density.ts
|
|
5704
|
-
var
|
|
5772
|
+
var check31 = {
|
|
5705
5773
|
id: "heuristic.whitespace.text-density",
|
|
5706
5774
|
name: "Text Density",
|
|
5707
5775
|
description: "Checks characters-per-page ratio",
|
|
@@ -5730,7 +5798,7 @@ var check30 = {
|
|
|
5730
5798
|
});
|
|
5731
5799
|
if (lowPages.length > 0) {
|
|
5732
5800
|
results.push({
|
|
5733
|
-
checkId:
|
|
5801
|
+
checkId: check31.id,
|
|
5734
5802
|
severity: "info",
|
|
5735
5803
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
5736
5804
|
file: ctx.pdfPath
|
|
@@ -5738,7 +5806,7 @@ var check30 = {
|
|
|
5738
5806
|
}
|
|
5739
5807
|
if (highPages.length > 0) {
|
|
5740
5808
|
results.push({
|
|
5741
|
-
checkId:
|
|
5809
|
+
checkId: check31.id,
|
|
5742
5810
|
severity: "info",
|
|
5743
5811
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
5744
5812
|
file: ctx.pdfPath
|
|
@@ -5747,11 +5815,11 @@ var check30 = {
|
|
|
5747
5815
|
return results;
|
|
5748
5816
|
}
|
|
5749
5817
|
};
|
|
5750
|
-
registerCheck(
|
|
5818
|
+
registerCheck(check31);
|
|
5751
5819
|
|
|
5752
5820
|
// src/checks/heuristic/section-density.ts
|
|
5753
|
-
import { readFile as
|
|
5754
|
-
var
|
|
5821
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
5822
|
+
var check32 = {
|
|
5755
5823
|
id: "heuristic.chunking.section-density",
|
|
5756
5824
|
name: "Section Density",
|
|
5757
5825
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -5767,7 +5835,7 @@ var check31 = {
|
|
|
5767
5835
|
const results = [];
|
|
5768
5836
|
for (const file of files) {
|
|
5769
5837
|
try {
|
|
5770
|
-
const content = await
|
|
5838
|
+
const content = await readFile17(file, "utf8");
|
|
5771
5839
|
const lines = content.split(`
|
|
5772
5840
|
`);
|
|
5773
5841
|
let currentSectionStart = 0;
|
|
@@ -5777,7 +5845,7 @@ var check31 = {
|
|
|
5777
5845
|
const line = lines[i];
|
|
5778
5846
|
if (/^#{1,6}\s/.test(line)) {
|
|
5779
5847
|
if (paragraphCount > maxParas) {
|
|
5780
|
-
results.push(finding(
|
|
5848
|
+
results.push(finding(check32.id, {
|
|
5781
5849
|
severity: "info",
|
|
5782
5850
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
5783
5851
|
file,
|
|
@@ -5797,7 +5865,7 @@ var check31 = {
|
|
|
5797
5865
|
}
|
|
5798
5866
|
}
|
|
5799
5867
|
if (paragraphCount > maxParas) {
|
|
5800
|
-
results.push(finding(
|
|
5868
|
+
results.push(finding(check32.id, {
|
|
5801
5869
|
severity: "info",
|
|
5802
5870
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
5803
5871
|
file,
|
|
@@ -5805,7 +5873,7 @@ var check31 = {
|
|
|
5805
5873
|
}));
|
|
5806
5874
|
}
|
|
5807
5875
|
} catch {
|
|
5808
|
-
results.push(inspectionFailed(
|
|
5876
|
+
results.push(inspectionFailed(check32.id, `Could not read source file: ${file}`, {
|
|
5809
5877
|
file
|
|
5810
5878
|
}));
|
|
5811
5879
|
}
|
|
@@ -5813,10 +5881,10 @@ var check31 = {
|
|
|
5813
5881
|
return results;
|
|
5814
5882
|
}
|
|
5815
5883
|
};
|
|
5816
|
-
registerCheck(
|
|
5884
|
+
registerCheck(check32);
|
|
5817
5885
|
|
|
5818
5886
|
// src/checks/heuristic/layer-count.ts
|
|
5819
|
-
var
|
|
5887
|
+
var check33 = {
|
|
5820
5888
|
id: "heuristic.decoration.layer-count",
|
|
5821
5889
|
name: "Layer Count",
|
|
5822
5890
|
description: "Counts image objects per page",
|
|
@@ -5841,7 +5909,7 @@ var check32 = {
|
|
|
5841
5909
|
heavyPages.sort((a, b) => a - b);
|
|
5842
5910
|
return [
|
|
5843
5911
|
{
|
|
5844
|
-
checkId:
|
|
5912
|
+
checkId: check33.id,
|
|
5845
5913
|
severity: "info",
|
|
5846
5914
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
5847
5915
|
file: ctx.pdfPath
|
|
@@ -5851,10 +5919,10 @@ var check32 = {
|
|
|
5851
5919
|
return [];
|
|
5852
5920
|
}
|
|
5853
5921
|
};
|
|
5854
|
-
registerCheck(
|
|
5922
|
+
registerCheck(check33);
|
|
5855
5923
|
|
|
5856
5924
|
// src/checks/heuristic/placement-variance.ts
|
|
5857
|
-
var
|
|
5925
|
+
var check34 = {
|
|
5858
5926
|
id: "heuristic.layout.placement-variance",
|
|
5859
5927
|
name: "Placement Variance",
|
|
5860
5928
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -5872,7 +5940,7 @@ var check33 = {
|
|
|
5872
5940
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
5873
5941
|
return [
|
|
5874
5942
|
{
|
|
5875
|
-
checkId:
|
|
5943
|
+
checkId: check34.id,
|
|
5876
5944
|
severity: "info",
|
|
5877
5945
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
5878
5946
|
file: ctx.pdfPath
|
|
@@ -5880,21 +5948,21 @@ var check33 = {
|
|
|
5880
5948
|
];
|
|
5881
5949
|
}
|
|
5882
5950
|
};
|
|
5883
|
-
registerCheck(
|
|
5951
|
+
registerCheck(check34);
|
|
5884
5952
|
|
|
5885
5953
|
// src/checks/tool-check.ts
|
|
5886
5954
|
async function checkToolAvailability(config, opts = {}) {
|
|
5887
5955
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
5888
5956
|
const toolToChecks = new Map;
|
|
5889
|
-
for (const
|
|
5890
|
-
if (!
|
|
5957
|
+
for (const check35 of checks2) {
|
|
5958
|
+
if (!check35.requiredTools?.length)
|
|
5891
5959
|
continue;
|
|
5892
|
-
for (const tool of
|
|
5960
|
+
for (const tool of check35.requiredTools) {
|
|
5893
5961
|
const existing = toolToChecks.get(tool);
|
|
5894
5962
|
if (existing) {
|
|
5895
|
-
existing.push(
|
|
5963
|
+
existing.push(check35.id);
|
|
5896
5964
|
} else {
|
|
5897
|
-
toolToChecks.set(tool, [
|
|
5965
|
+
toolToChecks.set(tool, [check35.id]);
|
|
5898
5966
|
}
|
|
5899
5967
|
}
|
|
5900
5968
|
}
|
|
@@ -5910,11 +5978,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
5910
5978
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
5911
5979
|
const missingSet = new Set(missing);
|
|
5912
5980
|
const skippedChecks = [];
|
|
5913
|
-
for (const
|
|
5914
|
-
if (!
|
|
5981
|
+
for (const check35 of checks2) {
|
|
5982
|
+
if (!check35.requiredTools?.length)
|
|
5915
5983
|
continue;
|
|
5916
|
-
if (
|
|
5917
|
-
skippedChecks.push(
|
|
5984
|
+
if (check35.requiredTools.some((t) => missingSet.has(t))) {
|
|
5985
|
+
skippedChecks.push(check35.id);
|
|
5918
5986
|
}
|
|
5919
5987
|
}
|
|
5920
5988
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -5936,7 +6004,7 @@ import { existsSync as existsSync8 } from "node:fs";
|
|
|
5936
6004
|
import { dirname as dirname4, join as join3, resolve as resolve7 } from "node:path";
|
|
5937
6005
|
|
|
5938
6006
|
// src/lib/output-paths.ts
|
|
5939
|
-
import
|
|
6007
|
+
import path5 from "node:path";
|
|
5940
6008
|
|
|
5941
6009
|
// src/lib/slug.ts
|
|
5942
6010
|
function slugify(name, fallback = "") {
|
|
@@ -5950,7 +6018,7 @@ function bookSlug(title) {
|
|
|
5950
6018
|
return slugify(title ?? "", "book");
|
|
5951
6019
|
}
|
|
5952
6020
|
function resolveOutputDir(manifestDir, title) {
|
|
5953
|
-
return
|
|
6021
|
+
return path5.resolve(manifestDir, DIST_DIRNAME, bookSlug(title));
|
|
5954
6022
|
}
|
|
5955
6023
|
function artifactName(title, format) {
|
|
5956
6024
|
return `${bookSlug(title)}-${format}.pdf`;
|
|
@@ -5960,7 +6028,17 @@ function artifactName(title, format) {
|
|
|
5960
6028
|
var BOOK_HTML_FILENAME = "book.html";
|
|
5961
6029
|
|
|
5962
6030
|
// src/lib/markdown/index.ts
|
|
5963
|
-
import { readdir as readdir3, readFile as
|
|
6031
|
+
import { readdir as readdir3, readFile as readFile18, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
6032
|
+
|
|
6033
|
+
// src/lib/remote-auth/sync-messages.ts
|
|
6034
|
+
function isOnlineSibling(filepath) {
|
|
6035
|
+
const dot = filepath.lastIndexOf(".");
|
|
6036
|
+
const slash = filepath.lastIndexOf("/");
|
|
6037
|
+
const stem = dot > slash + 1 ? filepath.slice(0, dot) : filepath;
|
|
6038
|
+
return stem.endsWith(".online");
|
|
6039
|
+
}
|
|
6040
|
+
|
|
6041
|
+
// src/lib/markdown/index.ts
|
|
5964
6042
|
import { join as join2 } from "node:path";
|
|
5965
6043
|
|
|
5966
6044
|
// src/lib/markdown/chapter-id.ts
|
|
@@ -6229,7 +6307,7 @@ ${bodyContent}
|
|
|
6229
6307
|
async function resolveActiveMarkdownFiles(inputDir, configuredFiles) {
|
|
6230
6308
|
if (configuredFiles && configuredFiles.length > 0)
|
|
6231
6309
|
return configuredFiles;
|
|
6232
|
-
return (await readdir3(inputDir)).filter((f) => f.endsWith(".md")).sort();
|
|
6310
|
+
return (await readdir3(inputDir)).filter((f) => f.endsWith(".md") && !isOnlineSibling(f)).sort();
|
|
6233
6311
|
}
|
|
6234
6312
|
async function renderChapters(inputDir, opts = {}) {
|
|
6235
6313
|
const styles = await resolveActiveStyles(inputDir, opts.styles);
|
|
@@ -6244,7 +6322,7 @@ async function renderChapters(inputDir, opts = {}) {
|
|
|
6244
6322
|
}
|
|
6245
6323
|
return assembleBookHtml({
|
|
6246
6324
|
files,
|
|
6247
|
-
readText: (relPath) =>
|
|
6325
|
+
readText: (relPath) => readFile18(join2(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
6248
6326
|
projectCss: inlined.css,
|
|
6249
6327
|
title: opts.title,
|
|
6250
6328
|
plugins: opts.plugins,
|
|
@@ -6359,25 +6437,25 @@ async function runChecks(ctx, opts = {}) {
|
|
|
6359
6437
|
}
|
|
6360
6438
|
const release = retainPdfCache();
|
|
6361
6439
|
try {
|
|
6362
|
-
for (const
|
|
6440
|
+
for (const check35 of checks2) {
|
|
6363
6441
|
try {
|
|
6364
|
-
const results = await
|
|
6365
|
-
const severityOverride = getCheckSeverityOverride(
|
|
6442
|
+
const results = await check35.run(ctx);
|
|
6443
|
+
const severityOverride = getCheckSeverityOverride(check35.id, ctx.config);
|
|
6366
6444
|
if (severityOverride) {
|
|
6367
6445
|
for (const r of results) {
|
|
6368
6446
|
r.severity = severityOverride;
|
|
6369
6447
|
}
|
|
6370
6448
|
}
|
|
6371
6449
|
if (results.length === 0) {
|
|
6372
|
-
passed.push(
|
|
6450
|
+
passed.push(check35.id);
|
|
6373
6451
|
} else {
|
|
6374
6452
|
allResults.push(...results);
|
|
6375
6453
|
}
|
|
6376
6454
|
} catch (err) {
|
|
6377
6455
|
allResults.push({
|
|
6378
|
-
checkId:
|
|
6456
|
+
checkId: check35.id,
|
|
6379
6457
|
severity: "error",
|
|
6380
|
-
message: `Check "${
|
|
6458
|
+
message: `Check "${check35.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
6381
6459
|
});
|
|
6382
6460
|
}
|
|
6383
6461
|
}
|
|
@@ -6734,7 +6812,7 @@ function buildPdfSummaryLines(results) {
|
|
|
6734
6812
|
}
|
|
6735
6813
|
|
|
6736
6814
|
// src/lib/build-runner.ts
|
|
6737
|
-
import
|
|
6815
|
+
import path8 from "node:path";
|
|
6738
6816
|
import fs2 from "node:fs";
|
|
6739
6817
|
import fsp2 from "node:fs/promises";
|
|
6740
6818
|
import os2 from "node:os";
|
|
@@ -6742,9 +6820,9 @@ import { randomBytes } from "node:crypto";
|
|
|
6742
6820
|
|
|
6743
6821
|
// src/lib/build-fingerprint.ts
|
|
6744
6822
|
import * as fs from "node:fs";
|
|
6745
|
-
import { mkdir as mkdir3, readFile as
|
|
6823
|
+
import { mkdir as mkdir3, readFile as readFile19, writeFile as writeFile3 } from "node:fs/promises";
|
|
6746
6824
|
import { createHash as createHash4 } from "node:crypto";
|
|
6747
|
-
import
|
|
6825
|
+
import path6 from "node:path";
|
|
6748
6826
|
import git from "isomorphic-git";
|
|
6749
6827
|
var FINGERPRINT_FILENAME = "build-fingerprint.json";
|
|
6750
6828
|
var VERSION_TIMEOUT_MS = 4000;
|
|
@@ -6793,7 +6871,7 @@ async function getGitRevision(sourceDir) {
|
|
|
6793
6871
|
const candidateDirs = [sourceDir, process.cwd()].filter((v) => Boolean(v));
|
|
6794
6872
|
const seen = new Set;
|
|
6795
6873
|
for (const dir of candidateDirs) {
|
|
6796
|
-
const abs =
|
|
6874
|
+
const abs = path6.resolve(dir);
|
|
6797
6875
|
if (seen.has(abs))
|
|
6798
6876
|
continue;
|
|
6799
6877
|
seen.add(abs);
|
|
@@ -6829,8 +6907,8 @@ async function getEngineBundleHash() {
|
|
|
6829
6907
|
getAssetPath("engine/gutterpress-agent.js")
|
|
6830
6908
|
]);
|
|
6831
6909
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
6832
|
-
|
|
6833
|
-
|
|
6910
|
+
readFile19(viewerPath),
|
|
6911
|
+
readFile19(agentPath)
|
|
6834
6912
|
]);
|
|
6835
6913
|
const hash = createHash4("sha256");
|
|
6836
6914
|
hash.update(viewerBytes);
|
|
@@ -6878,8 +6956,8 @@ function sanitizeArgs(args) {
|
|
|
6878
6956
|
return out;
|
|
6879
6957
|
}
|
|
6880
6958
|
async function writeBuildFingerprint(input) {
|
|
6881
|
-
const outputDir =
|
|
6882
|
-
const outPath =
|
|
6959
|
+
const outputDir = path6.resolve(input.outputDir);
|
|
6960
|
+
const outPath = path6.join(outputDir, FINGERPRINT_FILENAME);
|
|
6883
6961
|
const [tools, sourceRevision] = await Promise.all([
|
|
6884
6962
|
getToolVersions(),
|
|
6885
6963
|
getGitRevision(input.sourceDir)
|
|
@@ -6895,7 +6973,7 @@ async function writeBuildFingerprint(input) {
|
|
|
6895
6973
|
iccPath: input.pdfx.iccPath,
|
|
6896
6974
|
stripAnnotations: input.pdfx.stripAnnotations
|
|
6897
6975
|
},
|
|
6898
|
-
outputDir: input.recordedOutputDir ?
|
|
6976
|
+
outputDir: input.recordedOutputDir ? path6.resolve(input.recordedOutputDir) : outputDir
|
|
6899
6977
|
},
|
|
6900
6978
|
sourceRevision,
|
|
6901
6979
|
tools
|
|
@@ -6971,7 +7049,7 @@ function computeGates(format, opts, config) {
|
|
|
6971
7049
|
}
|
|
6972
7050
|
|
|
6973
7051
|
// src/lib/build-staging.ts
|
|
6974
|
-
import
|
|
7052
|
+
import path7 from "node:path";
|
|
6975
7053
|
import os from "node:os";
|
|
6976
7054
|
import fsp from "node:fs/promises";
|
|
6977
7055
|
|
|
@@ -7154,8 +7232,8 @@ function placeholderPng(width = 640, height = 480, cell = 32) {
|
|
|
7154
7232
|
|
|
7155
7233
|
// src/lib/build-staging.ts
|
|
7156
7234
|
async function shipViewerHtml(htmlFile, outDir) {
|
|
7157
|
-
await fsp.mkdir(
|
|
7158
|
-
await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"),
|
|
7235
|
+
await fsp.mkdir(path7.join(outDir, "engine"), { recursive: true });
|
|
7236
|
+
await fsp.copyFile(await getAssetPath("engine/gutterpress-viewer.js"), path7.join(outDir, "engine/gutterpress-viewer.js"));
|
|
7159
7237
|
const tag = ` <script src="engine/gutterpress-viewer.js"></script>
|
|
7160
7238
|
`;
|
|
7161
7239
|
const html = await fsp.readFile(htmlFile, "utf-8");
|
|
@@ -7184,18 +7262,18 @@ async function stageBookAssets(options) {
|
|
|
7184
7262
|
return { missing: [...missingPlaceholders.keys()].sort() };
|
|
7185
7263
|
}
|
|
7186
7264
|
async function copyReferencedAssets(copies, outDir) {
|
|
7187
|
-
const dirs = new Set(copies.map((c) =>
|
|
7265
|
+
const dirs = new Set(copies.map((c) => path7.dirname(path7.resolve(outDir, c.to))));
|
|
7188
7266
|
await Promise.all([...dirs].map((d) => fsp.mkdir(d, { recursive: true })));
|
|
7189
7267
|
const missing = new Map;
|
|
7190
7268
|
await Promise.all(copies.map(async (c) => {
|
|
7191
|
-
const dest =
|
|
7269
|
+
const dest = path7.resolve(outDir, c.to);
|
|
7192
7270
|
try {
|
|
7193
7271
|
await fsp.copyFile(c.from, dest);
|
|
7194
7272
|
} catch (err) {
|
|
7195
7273
|
if (err?.code === "ENOENT") {
|
|
7196
7274
|
const placeholder = placeholderOutputPath(c.to);
|
|
7197
|
-
const placeholderDest =
|
|
7198
|
-
await fsp.mkdir(
|
|
7275
|
+
const placeholderDest = path7.resolve(outDir, placeholder);
|
|
7276
|
+
await fsp.mkdir(path7.dirname(placeholderDest), { recursive: true });
|
|
7199
7277
|
await fsp.writeFile(placeholderDest, placeholderPng());
|
|
7200
7278
|
missing.set(c.to, placeholder);
|
|
7201
7279
|
return;
|
|
@@ -7206,7 +7284,7 @@ async function copyReferencedAssets(copies, outDir) {
|
|
|
7206
7284
|
return missing;
|
|
7207
7285
|
}
|
|
7208
7286
|
async function createStageRoot() {
|
|
7209
|
-
return fsp.mkdtemp(
|
|
7287
|
+
return fsp.mkdtemp(path7.join(os.tmpdir(), "gutterpress-stage-"));
|
|
7210
7288
|
}
|
|
7211
7289
|
|
|
7212
7290
|
// src/lib/build-runner.ts
|
|
@@ -7214,18 +7292,18 @@ function splitOutPath(outArg, format) {
|
|
|
7214
7292
|
if (typeof outArg !== "string" || outArg.length === 0) {
|
|
7215
7293
|
return { outDir: undefined, pdfFileOverride: null };
|
|
7216
7294
|
}
|
|
7217
|
-
const resolved =
|
|
7295
|
+
const resolved = path8.resolve(outArg);
|
|
7218
7296
|
if ((format === "pdf" || format === "pdfx") && resolved.toLowerCase().endsWith(".pdf")) {
|
|
7219
|
-
return { outDir:
|
|
7297
|
+
return { outDir: path8.dirname(resolved), pdfFileOverride: resolved };
|
|
7220
7298
|
}
|
|
7221
7299
|
return { outDir: resolved, pdfFileOverride: null };
|
|
7222
7300
|
}
|
|
7223
7301
|
function layoutWarningKey(file, line, message) {
|
|
7224
|
-
return `${
|
|
7302
|
+
return `${path8.resolve(file)}\x00${line ?? 0}\x00${message}`;
|
|
7225
7303
|
}
|
|
7226
7304
|
async function resolveBuildContext(opts) {
|
|
7227
7305
|
const { format } = opts;
|
|
7228
|
-
const inputDir =
|
|
7306
|
+
const inputDir = path8.resolve(opts.inputDir);
|
|
7229
7307
|
const { manifest, manifestDir, manifestPath } = await loadManifestWithPath(opts.manifestPath ?? inputDir, { explicit: opts.manifestPath !== undefined });
|
|
7230
7308
|
if (manifestPath === null) {
|
|
7231
7309
|
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>`.");
|
|
@@ -7240,10 +7318,10 @@ async function resolveBuildContext(opts) {
|
|
|
7240
7318
|
pdfx: pdfxConfigOverride,
|
|
7241
7319
|
engine: opts.engine
|
|
7242
7320
|
}, manifest);
|
|
7243
|
-
const target = opts.pdfFileOverride ? { kind: "file", file:
|
|
7244
|
-
const outDir = target.kind === "file" ?
|
|
7321
|
+
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) };
|
|
7322
|
+
const outDir = target.kind === "file" ? path8.dirname(target.file) : target.dir;
|
|
7245
7323
|
const gates = computeGates(format, opts, config);
|
|
7246
|
-
const workDir = target.kind === "project" ?
|
|
7324
|
+
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")}`);
|
|
7247
7325
|
return {
|
|
7248
7326
|
opts,
|
|
7249
7327
|
format,
|
|
@@ -7310,7 +7388,7 @@ async function renderBook(ctx) {
|
|
|
7310
7388
|
pluginCss,
|
|
7311
7389
|
onChapterWarnings: (file, warnings) => {
|
|
7312
7390
|
for (const w of warnings) {
|
|
7313
|
-
const key = layoutWarningKey(
|
|
7391
|
+
const key = layoutWarningKey(path8.resolve(renderDir, file), w.line, w.message);
|
|
7314
7392
|
if (!ctx.prevalidatedLayoutWarningKeys.has(key)) {
|
|
7315
7393
|
log.warn(` ${file}, line ${w.line}: ${w.message}`);
|
|
7316
7394
|
}
|
|
@@ -7348,9 +7426,9 @@ async function renderBook(ctx) {
|
|
|
7348
7426
|
return htmlFile;
|
|
7349
7427
|
}
|
|
7350
7428
|
async function resolveIccProfile(icc, manifestDir, explicitIccPath) {
|
|
7351
|
-
const iccCandidates =
|
|
7429
|
+
const iccCandidates = path8.isAbsolute(icc) ? [icc] : [path8.resolve(manifestDir, icc), path8.resolve(icc)];
|
|
7352
7430
|
let effectiveIccPath = iccCandidates.find((candidate) => fs2.existsSync(candidate)) ?? null;
|
|
7353
|
-
if (!effectiveIccPath && !explicitIccPath &&
|
|
7431
|
+
if (!effectiveIccPath && !explicitIccPath && path8.basename(icc) === "CGATS21_CRPC1.icc") {
|
|
7354
7432
|
effectiveIccPath = await getAssetPath("profiles/CGATS21_CRPC1.icc");
|
|
7355
7433
|
}
|
|
7356
7434
|
if (!effectiveIccPath) {
|
|
@@ -7366,13 +7444,13 @@ async function finalizeBuild(ctx, fingerprint, wroteMessage, paths, artifactName
|
|
|
7366
7444
|
});
|
|
7367
7445
|
await publishBuild(ctx, artifactName2);
|
|
7368
7446
|
const delivered = ctx.target.kind !== "file";
|
|
7369
|
-
const fingerprintPath = delivered ?
|
|
7447
|
+
const fingerprintPath = delivered ? path8.join(ctx.outDir, path8.basename(workFingerprint)) : null;
|
|
7370
7448
|
log.success(wroteMessage);
|
|
7371
7449
|
if (fingerprintPath)
|
|
7372
7450
|
log.info(`Fingerprint: ${fingerprintPath}`);
|
|
7373
7451
|
return {
|
|
7374
7452
|
outDir: ctx.outDir,
|
|
7375
|
-
htmlPath: delivered ?
|
|
7453
|
+
htmlPath: delivered ? path8.join(ctx.outDir, path8.relative(ctx.workDir, paths.htmlPath)) : null,
|
|
7376
7454
|
pdfPath: paths.pdfPath,
|
|
7377
7455
|
fingerprintPath,
|
|
7378
7456
|
diagnostics
|
|
@@ -7383,8 +7461,8 @@ async function publishBuild(ctx, artifactName2) {
|
|
|
7383
7461
|
if (target.kind === "file") {
|
|
7384
7462
|
if (!artifactName2)
|
|
7385
7463
|
throw new BuildError("No artifact to write for this format", 1);
|
|
7386
|
-
await fsp2.mkdir(
|
|
7387
|
-
await fsp2.copyFile(
|
|
7464
|
+
await fsp2.mkdir(path8.dirname(target.file), { recursive: true });
|
|
7465
|
+
await fsp2.copyFile(path8.join(workDir, artifactName2), target.file);
|
|
7388
7466
|
return;
|
|
7389
7467
|
}
|
|
7390
7468
|
if (target.kind === "directory") {
|
|
@@ -7393,12 +7471,12 @@ async function publishBuild(ctx, artifactName2) {
|
|
|
7393
7471
|
return;
|
|
7394
7472
|
}
|
|
7395
7473
|
for (const name of await listPdfs(target.dir)) {
|
|
7396
|
-
if (!fs2.existsSync(
|
|
7397
|
-
await fsp2.rename(
|
|
7474
|
+
if (!fs2.existsSync(path8.join(workDir, name))) {
|
|
7475
|
+
await fsp2.rename(path8.join(target.dir, name), path8.join(workDir, name));
|
|
7398
7476
|
}
|
|
7399
7477
|
}
|
|
7400
7478
|
await fsp2.rm(target.dir, { recursive: true, force: true });
|
|
7401
|
-
await fsp2.mkdir(
|
|
7479
|
+
await fsp2.mkdir(path8.dirname(target.dir), { recursive: true });
|
|
7402
7480
|
await fsp2.rename(workDir, target.dir);
|
|
7403
7481
|
}
|
|
7404
7482
|
async function listPdfs(dir) {
|
|
@@ -7414,7 +7492,7 @@ class HtmlOutput {
|
|
|
7414
7492
|
const { workDir, outDir, inputDir, config, opts } = ctx;
|
|
7415
7493
|
log.info("Shipping self-contained HTML + viewer bundle (native engine)");
|
|
7416
7494
|
await shipViewerHtml(htmlFile, workDir);
|
|
7417
|
-
await fsp2.writeFile(
|
|
7495
|
+
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>
|
|
7418
7496
|
`, "utf-8");
|
|
7419
7497
|
return finalizeBuild(ctx, {
|
|
7420
7498
|
command: "build",
|
|
@@ -7427,7 +7505,7 @@ class HtmlOutput {
|
|
|
7427
7505
|
iccPath: null,
|
|
7428
7506
|
stripAnnotations: null
|
|
7429
7507
|
}
|
|
7430
|
-
}, `Wrote: ${
|
|
7508
|
+
}, `Wrote: ${path8.join(outDir, BOOK_HTML)}`, { htmlPath: htmlFile, pdfPath: null });
|
|
7431
7509
|
}
|
|
7432
7510
|
}
|
|
7433
7511
|
|
|
@@ -7436,14 +7514,14 @@ class PdfOutput {
|
|
|
7436
7514
|
const { workDir, outDir, inputDir, config, opts, format, manifestDir, gates } = ctx;
|
|
7437
7515
|
const pdfxMode = format === "pdfx" ? opts.pdfxFlavor ?? config.pdfx.flavor : undefined;
|
|
7438
7516
|
const pdfName = artifactName(config.title, pdfxMode ? "pdfx" : "pdf");
|
|
7439
|
-
const pdfFile =
|
|
7440
|
-
const reportedPdf = ctx.target.kind === "file" ? ctx.target.file :
|
|
7517
|
+
const pdfFile = path8.join(workDir, pdfName);
|
|
7518
|
+
const reportedPdf = ctx.target.kind === "file" ? ctx.target.file : path8.join(outDir, pdfName);
|
|
7441
7519
|
const stage = await createStageRoot();
|
|
7442
7520
|
try {
|
|
7443
|
-
const rawPdf = pdfxMode ?
|
|
7444
|
-
await fsp2.mkdir(
|
|
7521
|
+
const rawPdf = pdfxMode ? path8.join(stage, "raw.pdf") : path8.resolve(pdfFile);
|
|
7522
|
+
await fsp2.mkdir(path8.dirname(path8.resolve(pdfFile)), { recursive: true });
|
|
7445
7523
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
7446
|
-
const { buildNativePdf } = await import("./engine-
|
|
7524
|
+
const { buildNativePdf } = await import("./engine-57vw8zpd.js");
|
|
7447
7525
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
7448
7526
|
title: config.title,
|
|
7449
7527
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -7474,7 +7552,7 @@ class PdfOutput {
|
|
|
7474
7552
|
await stripAnnotations(rawPdf, stage);
|
|
7475
7553
|
}
|
|
7476
7554
|
log.info(`Converting to CMYK PDF/X (${pdfxMode})`);
|
|
7477
|
-
await convertToPdfxCmyk(rawPdf,
|
|
7555
|
+
await convertToPdfxCmyk(rawPdf, path8.resolve(pdfFile), {
|
|
7478
7556
|
iccPath: effectiveIccPath,
|
|
7479
7557
|
pdfx: pdfxMode,
|
|
7480
7558
|
title: config.title,
|
|
@@ -7563,10 +7641,10 @@ function openPath(filePath) {
|
|
|
7563
7641
|
}
|
|
7564
7642
|
|
|
7565
7643
|
// src/preview/lifecycle.ts
|
|
7566
|
-
import
|
|
7644
|
+
import path10 from "path";
|
|
7567
7645
|
import { tmpdir as tmpdir2 } from "os";
|
|
7568
7646
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
7569
|
-
import { readdir as readdir4, readFile as
|
|
7647
|
+
import { readdir as readdir4, readFile as readFile20, writeFile as writeFile4 } from "node:fs/promises";
|
|
7570
7648
|
|
|
7571
7649
|
// src/utils/file-utils.ts
|
|
7572
7650
|
import { promises as fs3 } from "fs";
|
|
@@ -7588,7 +7666,7 @@ async function mkdir4(dirPath) {
|
|
|
7588
7666
|
// src/preview/file-watcher.ts
|
|
7589
7667
|
import { watch } from "chokidar";
|
|
7590
7668
|
import fsp3 from "node:fs/promises";
|
|
7591
|
-
import
|
|
7669
|
+
import path9 from "path";
|
|
7592
7670
|
var EMPTY_BOOK_HTML = `<!doctype html>
|
|
7593
7671
|
<html lang="en">
|
|
7594
7672
|
<head>
|
|
@@ -7641,7 +7719,7 @@ function injectPreviewScripts(html, pageIsolateChapters) {
|
|
|
7641
7719
|
async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
7642
7720
|
if (!inputPath) {
|
|
7643
7721
|
cssAssets.clear();
|
|
7644
|
-
await fsp3.writeFile(
|
|
7722
|
+
await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), EMPTY_BOOK_HTML, "utf-8");
|
|
7645
7723
|
return;
|
|
7646
7724
|
}
|
|
7647
7725
|
const nextAssets = new Map;
|
|
@@ -7656,7 +7734,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets) {
|
|
|
7656
7734
|
cssAssets.clear();
|
|
7657
7735
|
for (const [to, from] of nextAssets)
|
|
7658
7736
|
cssAssets.set(to, from);
|
|
7659
|
-
await fsp3.writeFile(
|
|
7737
|
+
await fsp3.writeFile(path9.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, false), "utf-8");
|
|
7660
7738
|
}
|
|
7661
7739
|
async function renderChapterPreviewHtml(inputPath, file, config) {
|
|
7662
7740
|
const html = await renderPreviewBook(inputPath, config, {
|
|
@@ -7668,12 +7746,12 @@ async function renderChapterPreviewHtml(inputPath, file, config) {
|
|
|
7668
7746
|
function describeChanges(changes, inputResolved) {
|
|
7669
7747
|
const files = [];
|
|
7670
7748
|
for (const [changedPath, event] of changes) {
|
|
7671
|
-
const relative2 =
|
|
7672
|
-
if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${
|
|
7749
|
+
const relative2 = path9.relative(inputResolved, path9.resolve(changedPath));
|
|
7750
|
+
if (relative2 === "" || relative2 === ".." || relative2.startsWith(`..${path9.sep}`) || path9.isAbsolute(relative2))
|
|
7673
7751
|
continue;
|
|
7674
7752
|
files.push({
|
|
7675
7753
|
relativePath: canonicalChapterId(relative2),
|
|
7676
|
-
ext:
|
|
7754
|
+
ext: path9.extname(changedPath).toLowerCase(),
|
|
7677
7755
|
event
|
|
7678
7756
|
});
|
|
7679
7757
|
}
|
|
@@ -7725,23 +7803,23 @@ function isIgnoredWatchPath(candidatePath, projectRoot) {
|
|
|
7725
7803
|
return isDotPathUnderRoot(candidatePath, projectRoot) || isGeneratedProjectPath(relative2);
|
|
7726
7804
|
}
|
|
7727
7805
|
function isExternalWatchCandidate(candidatePath, targets) {
|
|
7728
|
-
const candidate =
|
|
7806
|
+
const candidate = path9.resolve(candidatePath);
|
|
7729
7807
|
for (const rawTarget of targets) {
|
|
7730
|
-
const target =
|
|
7731
|
-
const rel =
|
|
7732
|
-
if (rel === "" || rel !== ".." && !rel.startsWith(`..${
|
|
7808
|
+
const target = path9.resolve(rawTarget);
|
|
7809
|
+
const rel = path9.relative(candidate, target);
|
|
7810
|
+
if (rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel))
|
|
7733
7811
|
return true;
|
|
7734
7812
|
}
|
|
7735
7813
|
return false;
|
|
7736
7814
|
}
|
|
7737
7815
|
async function nearestExistingDirectory(start) {
|
|
7738
|
-
let current =
|
|
7816
|
+
let current = path9.resolve(start);
|
|
7739
7817
|
while (true) {
|
|
7740
7818
|
try {
|
|
7741
7819
|
if ((await fsp3.stat(current)).isDirectory())
|
|
7742
7820
|
return current;
|
|
7743
7821
|
} catch {}
|
|
7744
|
-
const parent =
|
|
7822
|
+
const parent = path9.dirname(current);
|
|
7745
7823
|
if (parent === current)
|
|
7746
7824
|
return current;
|
|
7747
7825
|
current = parent;
|
|
@@ -7750,12 +7828,12 @@ async function nearestExistingDirectory(start) {
|
|
|
7750
7828
|
async function externalWatchRoots(targets) {
|
|
7751
7829
|
const roots = new Set;
|
|
7752
7830
|
for (const target of targets) {
|
|
7753
|
-
roots.add(await nearestExistingDirectory(
|
|
7831
|
+
roots.add(await nearestExistingDirectory(path9.dirname(path9.resolve(target))));
|
|
7754
7832
|
}
|
|
7755
7833
|
return [...roots];
|
|
7756
7834
|
}
|
|
7757
7835
|
async function externalWatchTargets(projectDir, config) {
|
|
7758
|
-
const root =
|
|
7836
|
+
const root = path9.resolve(projectDir);
|
|
7759
7837
|
let canonicalRoot = root;
|
|
7760
7838
|
try {
|
|
7761
7839
|
canonicalRoot = await fsp3.realpath(root);
|
|
@@ -7763,21 +7841,21 @@ async function externalWatchTargets(projectDir, config) {
|
|
|
7763
7841
|
const styles = await resolveActiveStyles(root, config.styles);
|
|
7764
7842
|
const candidates = [
|
|
7765
7843
|
...await collectStyleDependencies(root, styles),
|
|
7766
|
-
...(config.plugins ?? []).map((p) => p.path).filter((p) => !!p).map((p) =>
|
|
7844
|
+
...(config.plugins ?? []).map((p) => p.path).filter((p) => !!p).map((p) => path9.resolve(root, p))
|
|
7767
7845
|
];
|
|
7768
7846
|
const external = new Set;
|
|
7769
7847
|
for (const abs of candidates) {
|
|
7770
|
-
const authoredPath =
|
|
7771
|
-
const authoredRel =
|
|
7772
|
-
if (authoredRel !== "" && (authoredRel === ".." || authoredRel.startsWith(`..${
|
|
7848
|
+
const authoredPath = path9.resolve(abs);
|
|
7849
|
+
const authoredRel = path9.relative(root, authoredPath);
|
|
7850
|
+
if (authoredRel !== "" && (authoredRel === ".." || authoredRel.startsWith(`..${path9.sep}`) || path9.isAbsolute(authoredRel))) {
|
|
7773
7851
|
external.add(authoredPath);
|
|
7774
7852
|
}
|
|
7775
7853
|
let canonicalPath = authoredPath;
|
|
7776
7854
|
try {
|
|
7777
7855
|
canonicalPath = await fsp3.realpath(canonicalPath);
|
|
7778
7856
|
} catch {}
|
|
7779
|
-
const rel =
|
|
7780
|
-
if (rel === "" || rel !== ".." && !rel.startsWith(`..${
|
|
7857
|
+
const rel = path9.relative(canonicalRoot, canonicalPath);
|
|
7858
|
+
if (rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel))
|
|
7781
7859
|
continue;
|
|
7782
7860
|
external.add(canonicalPath);
|
|
7783
7861
|
}
|
|
@@ -7804,7 +7882,7 @@ async function addAndAwaitWatch(watcher, roots) {
|
|
|
7804
7882
|
}
|
|
7805
7883
|
}
|
|
7806
7884
|
function createFileWatcher(state) {
|
|
7807
|
-
const inputResolved =
|
|
7885
|
+
const inputResolved = path9.resolve(state.currentInputPath);
|
|
7808
7886
|
const watcher = watch(inputResolved, {
|
|
7809
7887
|
persistent: true,
|
|
7810
7888
|
ignoreInitial: true,
|
|
@@ -7844,7 +7922,7 @@ function createFileWatcher(state) {
|
|
|
7844
7922
|
async function recordChange(event, filePath) {
|
|
7845
7923
|
if (closed)
|
|
7846
7924
|
return;
|
|
7847
|
-
const target =
|
|
7925
|
+
const target = path9.resolve(filePath);
|
|
7848
7926
|
const settled = settledWrites.get(target);
|
|
7849
7927
|
if (settled && event !== "unlink" && Date.now() <= settled.expiresAt) {
|
|
7850
7928
|
const diskContent = await fsp3.readFile(target, "utf8").catch(() => null);
|
|
@@ -7865,7 +7943,7 @@ function createFileWatcher(state) {
|
|
|
7865
7943
|
recordChange(event, filePath);
|
|
7866
7944
|
});
|
|
7867
7945
|
externalWatcher.on("all", (event, filePath) => {
|
|
7868
|
-
const target =
|
|
7946
|
+
const target = path9.resolve(filePath);
|
|
7869
7947
|
if (!desiredExternals.has(target))
|
|
7870
7948
|
return;
|
|
7871
7949
|
if (event === "add" && suppressInitialExternalAdds.delete(target))
|
|
@@ -7873,8 +7951,8 @@ function createFileWatcher(state) {
|
|
|
7873
7951
|
recordChange(event, target);
|
|
7874
7952
|
});
|
|
7875
7953
|
function isWatchedSource(target) {
|
|
7876
|
-
const rel =
|
|
7877
|
-
const inProject = rel === "" || rel !== ".." && !rel.startsWith(`..${
|
|
7954
|
+
const rel = path9.relative(inputResolved, target);
|
|
7955
|
+
const inProject = rel === "" || rel !== ".." && !rel.startsWith(`..${path9.sep}`) && !path9.isAbsolute(rel);
|
|
7878
7956
|
if (inProject)
|
|
7879
7957
|
return !isIgnoredWatchPath(target, inputResolved);
|
|
7880
7958
|
return desiredExternals.has(target);
|
|
@@ -7899,7 +7977,7 @@ function createFileWatcher(state) {
|
|
|
7899
7977
|
state.notifySettledWrite = (filePath, writtenContent) => {
|
|
7900
7978
|
if (closed)
|
|
7901
7979
|
return;
|
|
7902
|
-
const target =
|
|
7980
|
+
const target = path9.resolve(filePath);
|
|
7903
7981
|
if (isWatchedSource(target)) {
|
|
7904
7982
|
acceptSettledWrite(target, writtenContent);
|
|
7905
7983
|
return;
|
|
@@ -7920,7 +7998,7 @@ function createFileWatcher(state) {
|
|
|
7920
7998
|
return;
|
|
7921
7999
|
let nextTargets;
|
|
7922
8000
|
try {
|
|
7923
|
-
nextTargets = new Set((await externalWatchTargets(inputResolved, state.config)).map((target) =>
|
|
8001
|
+
nextTargets = new Set((await externalWatchTargets(inputResolved, state.config)).map((target) => path9.resolve(target)));
|
|
7924
8002
|
} catch (err) {
|
|
7925
8003
|
debug(`Could not resolve external watch targets: ${err}`);
|
|
7926
8004
|
return;
|
|
@@ -8052,7 +8130,7 @@ async function stopFileWatcher(state) {
|
|
|
8052
8130
|
}
|
|
8053
8131
|
|
|
8054
8132
|
// src/preview/lifecycle.ts
|
|
8055
|
-
var TEMP_DIR_BASE =
|
|
8133
|
+
var TEMP_DIR_BASE = path10.join(tmpdir2(), "gutterpress-preview");
|
|
8056
8134
|
var PID_FILE_NAME = ".gutterpress.pid";
|
|
8057
8135
|
function isProcessAlive(pid) {
|
|
8058
8136
|
if (!Number.isFinite(pid) || pid <= 0)
|
|
@@ -8072,10 +8150,10 @@ async function cleanupOrphanTempDirs() {
|
|
|
8072
8150
|
return;
|
|
8073
8151
|
}
|
|
8074
8152
|
for (const entry of entries) {
|
|
8075
|
-
const dir =
|
|
8076
|
-
const pidFile =
|
|
8153
|
+
const dir = path10.join(TEMP_DIR_BASE, entry);
|
|
8154
|
+
const pidFile = path10.join(dir, PID_FILE_NAME);
|
|
8077
8155
|
try {
|
|
8078
|
-
const raw = await
|
|
8156
|
+
const raw = await readFile20(pidFile, "utf8");
|
|
8079
8157
|
const pid = parseInt(raw.trim(), 10);
|
|
8080
8158
|
if (!isProcessAlive(pid)) {
|
|
8081
8159
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -8087,10 +8165,10 @@ async function cleanupOrphanTempDirs() {
|
|
|
8087
8165
|
async function initializePreviewDirectories() {
|
|
8088
8166
|
await cleanupOrphanTempDirs();
|
|
8089
8167
|
const tempDirSuffix = randomBytes2(8).toString("hex");
|
|
8090
|
-
const tempDir =
|
|
8168
|
+
const tempDir = path10.join(TEMP_DIR_BASE, tempDirSuffix);
|
|
8091
8169
|
await mkdir4(tempDir);
|
|
8092
8170
|
debug(`Created temporary directory: ${tempDir}`);
|
|
8093
|
-
await writeFile4(
|
|
8171
|
+
await writeFile4(path10.join(tempDir, PID_FILE_NAME), `${process.pid}
|
|
8094
8172
|
`, "utf8");
|
|
8095
8173
|
return tempDir;
|
|
8096
8174
|
}
|
|
@@ -8181,12 +8259,12 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
8181
8259
|
import http from "node:http";
|
|
8182
8260
|
import net from "node:net";
|
|
8183
8261
|
import { createHash as createHash6, randomUUID } from "node:crypto";
|
|
8184
|
-
import { readFile as
|
|
8185
|
-
import
|
|
8262
|
+
import { readFile as readFile21, stat as stat4 } from "node:fs/promises";
|
|
8263
|
+
import path12 from "path";
|
|
8186
8264
|
import { WebSocket, WebSocketServer } from "ws";
|
|
8187
8265
|
|
|
8188
8266
|
// src/lib/static-serve.ts
|
|
8189
|
-
import
|
|
8267
|
+
import path11 from "node:path";
|
|
8190
8268
|
var STATIC_MIME = {
|
|
8191
8269
|
".html": "text/html; charset=utf-8",
|
|
8192
8270
|
".htm": "text/html; charset=utf-8",
|
|
@@ -8208,9 +8286,9 @@ var STATIC_MIME = {
|
|
|
8208
8286
|
".otf": "font/otf"
|
|
8209
8287
|
};
|
|
8210
8288
|
function resolveWithinRoot(relPath, root) {
|
|
8211
|
-
const resolvedRoot =
|
|
8212
|
-
const candidate =
|
|
8213
|
-
if (candidate !== resolvedRoot && !candidate.startsWith(resolvedRoot +
|
|
8289
|
+
const resolvedRoot = path11.resolve(root);
|
|
8290
|
+
const candidate = path11.resolve(resolvedRoot, "." + (relPath.startsWith("/") ? relPath : "/" + relPath));
|
|
8291
|
+
if (candidate !== resolvedRoot && !candidate.startsWith(resolvedRoot + path11.sep)) {
|
|
8214
8292
|
return null;
|
|
8215
8293
|
}
|
|
8216
8294
|
return candidate;
|
|
@@ -8463,7 +8541,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
8463
8541
|
try {
|
|
8464
8542
|
const s = await stat4(filePath);
|
|
8465
8543
|
if (s.isDirectory())
|
|
8466
|
-
filePath =
|
|
8544
|
+
filePath = path12.join(filePath, "index.html");
|
|
8467
8545
|
} catch {
|
|
8468
8546
|
res.writeHead(404);
|
|
8469
8547
|
res.end("Not Found");
|
|
@@ -8471,7 +8549,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
8471
8549
|
}
|
|
8472
8550
|
let data;
|
|
8473
8551
|
try {
|
|
8474
|
-
data = await
|
|
8552
|
+
data = await readFile21(filePath);
|
|
8475
8553
|
} catch {
|
|
8476
8554
|
res.writeHead(404);
|
|
8477
8555
|
res.end("Not Found");
|
|
@@ -8484,7 +8562,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
8484
8562
|
res.end(withHmr);
|
|
8485
8563
|
return;
|
|
8486
8564
|
}
|
|
8487
|
-
const ct = STATIC_MIME[
|
|
8565
|
+
const ct = STATIC_MIME[path12.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
8488
8566
|
res.writeHead(200, { "Content-Type": ct, "Cache-Control": cacheControl, ...extraHeaders });
|
|
8489
8567
|
res.end(data);
|
|
8490
8568
|
}
|
|
@@ -8494,7 +8572,7 @@ var EMBEDDED_CACHE_CONTROL = "public, no-cache";
|
|
|
8494
8572
|
var EMBEDDED_ETAG = `"gutterpress-${PACKAGE_VERSION}"`;
|
|
8495
8573
|
var PROJECT_ASSET_CACHE_CONTROL = "private, no-cache";
|
|
8496
8574
|
async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
8497
|
-
const extension =
|
|
8575
|
+
const extension = path12.extname(absPath).toLowerCase();
|
|
8498
8576
|
if (extension === ".html" || extension === ".htm") {
|
|
8499
8577
|
await serveStatic(absPath, res);
|
|
8500
8578
|
return;
|
|
@@ -8521,7 +8599,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
8521
8599
|
let data;
|
|
8522
8600
|
if (!validator || validator.stamp !== stamp) {
|
|
8523
8601
|
try {
|
|
8524
|
-
data = await
|
|
8602
|
+
data = await readFile21(absPath);
|
|
8525
8603
|
} catch {
|
|
8526
8604
|
await serveStatic(absPath, res);
|
|
8527
8605
|
return;
|
|
@@ -8660,7 +8738,7 @@ async function createPreviewServer(state, port) {
|
|
|
8660
8738
|
const file = rawFile ? canonicalChapterId(rawFile) : "";
|
|
8661
8739
|
const revision = Number(url.searchParams.get("revision"));
|
|
8662
8740
|
const configuredFiles = state.config.source?.files;
|
|
8663
|
-
if (!file || !state.currentInputPath || !incrementalPreviewEnabled() || !Number.isSafeInteger(revision) || revision !== reloadRevision ||
|
|
8741
|
+
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)) {
|
|
8664
8742
|
res.writeHead(400);
|
|
8665
8743
|
res.end("Bad Request");
|
|
8666
8744
|
return;
|
|
@@ -8860,25 +8938,25 @@ async function startPreviewServer(options) {
|
|
|
8860
8938
|
}
|
|
8861
8939
|
|
|
8862
8940
|
// src/lib/project-scaffold.ts
|
|
8863
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir6, readdir as readdir5, readFile as
|
|
8941
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir6, readdir as readdir5, readFile as readFile23, stat as stat5, writeFile as writeFile5 } from "node:fs/promises";
|
|
8864
8942
|
import { constants as FS, existsSync as existsSync10 } from "node:fs";
|
|
8865
|
-
import
|
|
8943
|
+
import path14 from "node:path";
|
|
8866
8944
|
|
|
8867
8945
|
// src/lib/manifest-doc.ts
|
|
8868
8946
|
import { existsSync as existsSync9 } from "node:fs";
|
|
8869
8947
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8870
|
-
import { mkdir as mkdir5, open, readFile as
|
|
8871
|
-
import
|
|
8948
|
+
import { mkdir as mkdir5, open, readFile as readFile22, rename, rm as rm2 } from "node:fs/promises";
|
|
8949
|
+
import path13 from "node:path";
|
|
8872
8950
|
import { parseDocument, isSeq, YAMLSeq } from "yaml";
|
|
8873
8951
|
function resolveManifestPath(projectDir) {
|
|
8874
|
-
const existing = MANIFEST_FILENAMES.find((name) => existsSync9(
|
|
8875
|
-
return
|
|
8952
|
+
const existing = MANIFEST_FILENAMES.find((name) => existsSync9(path13.join(projectDir, name)));
|
|
8953
|
+
return path13.join(projectDir, existing ?? MANIFEST_FILENAMES[0]);
|
|
8876
8954
|
}
|
|
8877
8955
|
async function loadManifestDoc(projectDir) {
|
|
8878
8956
|
const file = resolveManifestPath(projectDir);
|
|
8879
8957
|
let text = "";
|
|
8880
8958
|
try {
|
|
8881
|
-
text = await
|
|
8959
|
+
text = await readFile22(file, "utf8");
|
|
8882
8960
|
} catch (error2) {
|
|
8883
8961
|
if (error2.code !== "ENOENT")
|
|
8884
8962
|
throw error2;
|
|
@@ -8886,9 +8964,9 @@ async function loadManifestDoc(projectDir) {
|
|
|
8886
8964
|
return { doc: parseDocument(text), file };
|
|
8887
8965
|
}
|
|
8888
8966
|
async function writeManifestDoc(file, doc) {
|
|
8889
|
-
const dir =
|
|
8967
|
+
const dir = path13.dirname(file);
|
|
8890
8968
|
await mkdir5(dir, { recursive: true });
|
|
8891
|
-
const temporary =
|
|
8969
|
+
const temporary = path13.join(dir, `.${path13.basename(file)}.tmp-${process.pid}-${randomUUID2()}`);
|
|
8892
8970
|
try {
|
|
8893
8971
|
const handle = await open(temporary, "wx", 438);
|
|
8894
8972
|
try {
|
|
@@ -8983,7 +9061,7 @@ async function scaffoldProject(options) {
|
|
|
8983
9061
|
throw new CreateProjectErrorImpl("invalid-name", `Could not derive a valid folder name from "${name}".`);
|
|
8984
9062
|
}
|
|
8985
9063
|
const parentDir = options.parentDir;
|
|
8986
|
-
if (!parentDir || !
|
|
9064
|
+
if (!parentDir || !path14.isAbsolute(parentDir)) {
|
|
8987
9065
|
throw new CreateProjectErrorImpl("parent-not-writable", "The save location must be an absolute path.");
|
|
8988
9066
|
}
|
|
8989
9067
|
try {
|
|
@@ -8991,7 +9069,7 @@ async function scaffoldProject(options) {
|
|
|
8991
9069
|
} catch {
|
|
8992
9070
|
throw new CreateProjectErrorImpl("parent-not-writable", `The chosen save location can't be written to: ${parentDir}`);
|
|
8993
9071
|
}
|
|
8994
|
-
const projectDir =
|
|
9072
|
+
const projectDir = path14.join(parentDir, folderName);
|
|
8995
9073
|
let targetExists = true;
|
|
8996
9074
|
try {
|
|
8997
9075
|
await access(projectDir, FS.F_OK);
|
|
@@ -9012,19 +9090,19 @@ async function scaffoldProject(options) {
|
|
|
9012
9090
|
for (const entry of entries) {
|
|
9013
9091
|
if (entry.name === ".gutterpress-template.json")
|
|
9014
9092
|
continue;
|
|
9015
|
-
await cp2(
|
|
9093
|
+
await cp2(path14.join(customTemplateDir, entry.name), path14.join(projectDir, entry.name), { recursive: true });
|
|
9016
9094
|
}
|
|
9017
|
-
await mkdir6(
|
|
9095
|
+
await mkdir6(path14.join(projectDir, "assets"), { recursive: true });
|
|
9018
9096
|
} else {
|
|
9019
9097
|
const tplManifest = await getAssetPath(`templates/${template}/manifest.yaml`);
|
|
9020
9098
|
const tplChapter = await getAssetPath(`templates/${template}/chapter-01.md`);
|
|
9021
|
-
await mkdir6(
|
|
9022
|
-
await copyFile(tplManifest,
|
|
9023
|
-
await copyFile(tplChapter,
|
|
9099
|
+
await mkdir6(path14.join(projectDir, "assets"), { recursive: true });
|
|
9100
|
+
await copyFile(tplManifest, path14.join(projectDir, MANIFEST_FILENAMES[0]));
|
|
9101
|
+
await copyFile(tplChapter, path14.join(projectDir, "chapter-01.md"));
|
|
9024
9102
|
const starterThemeId = STARTER_THEME_FOR_TEMPLATE[template] ?? "clean-book";
|
|
9025
9103
|
const starterCss = await getAssetPath(`themes/${starterThemeId}/theme.css`);
|
|
9026
|
-
await mkdir6(
|
|
9027
|
-
await copyFile(starterCss,
|
|
9104
|
+
await mkdir6(path14.join(projectDir, "styles"), { recursive: true });
|
|
9105
|
+
await copyFile(starterCss, path14.join(projectDir, "styles", "book.css"));
|
|
9028
9106
|
}
|
|
9029
9107
|
} catch (e) {
|
|
9030
9108
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not create the project files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -9034,13 +9112,13 @@ async function scaffoldProject(options) {
|
|
|
9034
9112
|
} catch (e) {
|
|
9035
9113
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not write .gitignore: ${e instanceof Error ? e.message : String(e)}`);
|
|
9036
9114
|
}
|
|
9037
|
-
const manifestPath = MANIFEST_FILENAMES.map((name2) =>
|
|
9115
|
+
const manifestPath = MANIFEST_FILENAMES.map((name2) => path14.join(projectDir, name2)).find(existsSync10) ?? path14.join(projectDir, MANIFEST_FILENAMES[0]);
|
|
9038
9116
|
const author = (options.author ?? "").trim() || DEFAULT_AUTHOR;
|
|
9039
9117
|
const substitutions = {
|
|
9040
9118
|
"{{TITLE}}": escapeYamlScalar(name),
|
|
9041
9119
|
"{{AUTHOR}}": escapeYamlScalar(author)
|
|
9042
9120
|
};
|
|
9043
|
-
let openFile =
|
|
9121
|
+
let openFile = path14.join(projectDir, "chapter-01.md");
|
|
9044
9122
|
try {
|
|
9045
9123
|
await fillTemplateFile(manifestPath, substitutions);
|
|
9046
9124
|
if (preset) {
|
|
@@ -9061,7 +9139,7 @@ async function scaffoldProject(options) {
|
|
|
9061
9139
|
}
|
|
9062
9140
|
const firstSource = await firstSourceFile(manifestPath);
|
|
9063
9141
|
if (firstSource)
|
|
9064
|
-
openFile =
|
|
9142
|
+
openFile = path14.join(projectDir, firstSource);
|
|
9065
9143
|
await fillTemplateFile(openFile, { "{{TITLE}}": name }).catch(() => {});
|
|
9066
9144
|
} catch (e) {
|
|
9067
9145
|
throw new CreateProjectErrorImpl("scaffold-io", `Could not finalise the project files: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -9071,7 +9149,7 @@ async function scaffoldProject(options) {
|
|
|
9071
9149
|
let versionHistoryError;
|
|
9072
9150
|
if (requested === "local-git") {
|
|
9073
9151
|
try {
|
|
9074
|
-
const { providerFor } = await import("./source-provider-
|
|
9152
|
+
const { providerFor } = await import("./source-provider-2j5x2hyw.js");
|
|
9075
9153
|
const provider = providerFor({ type: "local-folder", path: projectDir });
|
|
9076
9154
|
await provider.initVersionHistory({
|
|
9077
9155
|
projectDir,
|
|
@@ -9083,7 +9161,7 @@ async function scaffoldProject(options) {
|
|
|
9083
9161
|
versionHistory = "none";
|
|
9084
9162
|
const { findEnclosingRepoDir } = await import("./project-source-ekcyp63q.js");
|
|
9085
9163
|
const enclosingRepoDir = await findEnclosingRepoDir(projectDir);
|
|
9086
|
-
versionHistoryError = enclosingRepoDir ? `This book was added to ${
|
|
9164
|
+
versionHistoryError = enclosingRepoDir ? `This book was added to ${path14.basename(enclosingRepoDir)}'s shared version history; save a snapshot to record it.` : e instanceof Error ? e.message : String(e);
|
|
9087
9165
|
}
|
|
9088
9166
|
}
|
|
9089
9167
|
const result = {
|
|
@@ -9098,10 +9176,10 @@ async function scaffoldProject(options) {
|
|
|
9098
9176
|
return result;
|
|
9099
9177
|
}
|
|
9100
9178
|
async function ensureGitignoreHasDist(projectDir) {
|
|
9101
|
-
const gitignorePath =
|
|
9179
|
+
const gitignorePath = path14.join(projectDir, ".gitignore");
|
|
9102
9180
|
let existing = null;
|
|
9103
9181
|
try {
|
|
9104
|
-
existing = await
|
|
9182
|
+
existing = await readFile23(gitignorePath, "utf8");
|
|
9105
9183
|
} catch {}
|
|
9106
9184
|
if (existing === null) {
|
|
9107
9185
|
await writeFile5(gitignorePath, `dist/
|
|
@@ -9121,7 +9199,7 @@ async function ensureGitignoreHasDist(projectDir) {
|
|
|
9121
9199
|
async function firstSourceFile(manifestPath) {
|
|
9122
9200
|
let text;
|
|
9123
9201
|
try {
|
|
9124
|
-
text = await
|
|
9202
|
+
text = await readFile23(manifestPath, "utf8");
|
|
9125
9203
|
} catch {
|
|
9126
9204
|
return;
|
|
9127
9205
|
}
|
|
@@ -9133,7 +9211,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
9133
9211
|
return m ? m[1].trim() : undefined;
|
|
9134
9212
|
}
|
|
9135
9213
|
async function fillTemplateFile(filePath, substitutions) {
|
|
9136
|
-
let text = await
|
|
9214
|
+
let text = await readFile23(filePath, "utf8");
|
|
9137
9215
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
9138
9216
|
text = text.split(token).join(value);
|
|
9139
9217
|
}
|
|
@@ -9158,7 +9236,7 @@ var SKIP_ENTRIES = new Set([
|
|
|
9158
9236
|
]);
|
|
9159
9237
|
|
|
9160
9238
|
// src/lib/plugin-manager.ts
|
|
9161
|
-
import
|
|
9239
|
+
import path16 from "node:path";
|
|
9162
9240
|
import { isSeq as isSeq3, isMap as isMap2, isScalar as isScalar2, YAMLMap, Scalar } from "yaml";
|
|
9163
9241
|
|
|
9164
9242
|
// src/lib/npm-plugin-installer.ts
|
|
@@ -9169,12 +9247,12 @@ import {
|
|
|
9169
9247
|
mkdir as mkdir7,
|
|
9170
9248
|
mkdtemp as mkdtemp2,
|
|
9171
9249
|
open as open2,
|
|
9172
|
-
readFile as
|
|
9250
|
+
readFile as readFile24,
|
|
9173
9251
|
realpath as realpath2,
|
|
9174
9252
|
rename as rename2,
|
|
9175
9253
|
rm as rm3
|
|
9176
9254
|
} from "node:fs/promises";
|
|
9177
|
-
import
|
|
9255
|
+
import path15 from "node:path";
|
|
9178
9256
|
import { Transform } from "node:stream";
|
|
9179
9257
|
import { pipeline } from "node:stream/promises";
|
|
9180
9258
|
import { createGunzip } from "node:zlib";
|
|
@@ -9254,8 +9332,8 @@ function mb(bytes) {
|
|
|
9254
9332
|
return `${Math.ceil(bytes / 1024 / 1024)}MB`;
|
|
9255
9333
|
}
|
|
9256
9334
|
function isContained2(root, candidate) {
|
|
9257
|
-
const relative2 =
|
|
9258
|
-
return relative2 === "" || !relative2.startsWith("..") && !
|
|
9335
|
+
const relative2 = path15.relative(root, candidate);
|
|
9336
|
+
return relative2 === "" || !relative2.startsWith("..") && !path15.isAbsolute(relative2);
|
|
9259
9337
|
}
|
|
9260
9338
|
function mergeLimits(overrides) {
|
|
9261
9339
|
const limits = { ...DEFAULT_LIMITS, ...overrides };
|
|
@@ -9562,13 +9640,13 @@ async function extractTarball(ctx, archivePath, packageDir, packageName) {
|
|
|
9562
9640
|
throw rejected;
|
|
9563
9641
|
}
|
|
9564
9642
|
async function readInstalledManifest(packageDir, selected) {
|
|
9565
|
-
const packageJsonPath =
|
|
9643
|
+
const packageJsonPath = path15.join(packageDir, "package.json");
|
|
9566
9644
|
let manifest = null;
|
|
9567
9645
|
try {
|
|
9568
9646
|
const info2 = await lstat2(packageJsonPath);
|
|
9569
9647
|
if (!info2.isFile() || info2.isSymbolicLink())
|
|
9570
9648
|
throw new Error("not a regular file");
|
|
9571
|
-
manifest = object2(JSON.parse(await
|
|
9649
|
+
manifest = object2(JSON.parse(await readFile24(packageJsonPath, "utf8")));
|
|
9572
9650
|
} catch (error2) {
|
|
9573
9651
|
throw new SecurityInstallError(`Downloaded ${selected.name}@${selected.version} has no valid package.json (${error2 instanceof Error ? error2.message : String(error2)}).`);
|
|
9574
9652
|
}
|
|
@@ -9638,10 +9716,10 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
|
|
|
9638
9716
|
throw new SecurityInstallError(`Dependency graph exceeds ${ctx.limits.totalPackages} packages.`);
|
|
9639
9717
|
}
|
|
9640
9718
|
const relativePath = relativePackagePath(parentPackagePath, name);
|
|
9641
|
-
const packageDir =
|
|
9642
|
-
const archivePath =
|
|
9719
|
+
const packageDir = path15.join(ctx.stageRoot, ...relativePath.split("/"));
|
|
9720
|
+
const archivePath = path15.join(ctx.downloadsDir, `${ctx.archiveCounter++}.tgz`);
|
|
9643
9721
|
try {
|
|
9644
|
-
await mkdir7(
|
|
9722
|
+
await mkdir7(path15.dirname(packageDir), { recursive: true });
|
|
9645
9723
|
await downloadTarball(ctx, selected, archivePath);
|
|
9646
9724
|
await extractTarball(ctx, archivePath, packageDir, selected.name);
|
|
9647
9725
|
const manifest = await readInstalledManifest(packageDir, selected);
|
|
@@ -9740,9 +9818,9 @@ async function ensureDirectory(pathname) {
|
|
|
9740
9818
|
}
|
|
9741
9819
|
}
|
|
9742
9820
|
async function prepareVendorParent(projectDir, name) {
|
|
9743
|
-
const plugins =
|
|
9744
|
-
const npm =
|
|
9745
|
-
const packageParent =
|
|
9821
|
+
const plugins = path15.join(projectDir, PLUGINS_DIR);
|
|
9822
|
+
const npm = path15.join(plugins, VENDORED_NPM_DIR);
|
|
9823
|
+
const packageParent = path15.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
|
|
9746
9824
|
await mkdir7(projectDir, { recursive: true });
|
|
9747
9825
|
await ensureDirectory(plugins);
|
|
9748
9826
|
await ensureDirectory(npm);
|
|
@@ -9770,14 +9848,14 @@ function publicReceipt(record) {
|
|
|
9770
9848
|
};
|
|
9771
9849
|
}
|
|
9772
9850
|
async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
9773
|
-
const requestedProjectDir =
|
|
9851
|
+
const requestedProjectDir = path15.resolve(projectDirInput);
|
|
9774
9852
|
await mkdir7(requestedProjectDir, { recursive: true });
|
|
9775
9853
|
const projectDir = await realpath2(requestedProjectDir);
|
|
9776
9854
|
const requested = parseNpmPluginSpec(packageSpec);
|
|
9777
9855
|
const npmRoot = await prepareVendorParent(projectDir, requested.name);
|
|
9778
|
-
const container = await mkdtemp2(
|
|
9779
|
-
const stageRoot =
|
|
9780
|
-
const downloadsDir =
|
|
9856
|
+
const container = await mkdtemp2(path15.join(npmRoot, ".install-"));
|
|
9857
|
+
const stageRoot = path15.join(container, "root");
|
|
9858
|
+
const downloadsDir = path15.join(container, "downloads");
|
|
9781
9859
|
await Promise.all([mkdir7(stageRoot), mkdir7(downloadsDir)]);
|
|
9782
9860
|
const ctx = {
|
|
9783
9861
|
fetch: options.fetch ?? globalThis.fetch,
|
|
@@ -9823,7 +9901,7 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
9823
9901
|
skipped: ctx.skipped,
|
|
9824
9902
|
tree: { algorithm: "sha256", ...tree }
|
|
9825
9903
|
};
|
|
9826
|
-
const receiptFile = await open2(
|
|
9904
|
+
const receiptFile = await open2(path15.join(stageRoot, VENDOR_RECEIPT_FILE), "wx");
|
|
9827
9905
|
try {
|
|
9828
9906
|
await receiptFile.writeFile(`${JSON.stringify(receipt, null, 2)}
|
|
9829
9907
|
`, "utf8");
|
|
@@ -9832,7 +9910,7 @@ async function installNpmPlugin(projectDirInput, packageSpec, options = {}) {
|
|
|
9832
9910
|
await receiptFile.close();
|
|
9833
9911
|
}
|
|
9834
9912
|
finalRoot = vendoredNpmPluginRoot(projectDir, rootRecord.name, rootRecord.version);
|
|
9835
|
-
const realFinalParent = await realpath2(
|
|
9913
|
+
const realFinalParent = await realpath2(path15.dirname(finalRoot));
|
|
9836
9914
|
if (!isContained2(projectDir, realFinalParent)) {
|
|
9837
9915
|
throw new Error("Plugin install destination resolves outside the project.");
|
|
9838
9916
|
}
|
|
@@ -9881,7 +9959,7 @@ async function rollbackNpmPluginInstall(installed) {
|
|
|
9881
9959
|
// src/lib/plugin-manager.ts
|
|
9882
9960
|
var pluginMutationQueues = new Map;
|
|
9883
9961
|
function withPluginMutationLock(projectDir, mutation) {
|
|
9884
|
-
const resolved =
|
|
9962
|
+
const resolved = path16.resolve(projectDir);
|
|
9885
9963
|
const key = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
9886
9964
|
const previous = pluginMutationQueues.get(key) ?? Promise.resolve();
|
|
9887
9965
|
const run = previous.then(mutation, mutation);
|
|
@@ -10074,221 +10152,6 @@ async function addNpmPluginUnlocked(projectDir, packageSpec, options) {
|
|
|
10074
10152
|
};
|
|
10075
10153
|
}
|
|
10076
10154
|
|
|
10077
|
-
// src/lib/app-heartbeat.ts
|
|
10078
|
-
import { readFile as readFile24, unlink as unlink2, writeFile as writeFile6 } from "node:fs/promises";
|
|
10079
|
-
import path16 from "node:path";
|
|
10080
|
-
var HEARTBEAT_FILENAME = "gutterpress-app-heartbeat";
|
|
10081
|
-
var APP_HEARTBEAT_FRESH_MS = 2 * 60000;
|
|
10082
|
-
function appHeartbeatPath(repoDir) {
|
|
10083
|
-
return path16.join(gitDirFor(repoDir), HEARTBEAT_FILENAME);
|
|
10084
|
-
}
|
|
10085
|
-
async function readAppHeartbeat(repoDir) {
|
|
10086
|
-
try {
|
|
10087
|
-
const raw = await readFile24(appHeartbeatPath(repoDir), "utf8");
|
|
10088
|
-
const parsed = JSON.parse(raw);
|
|
10089
|
-
if (parsed && typeof parsed === "object" && typeof parsed.pid === "number" && typeof parsed.timestamp === "number") {
|
|
10090
|
-
const candidate = parsed;
|
|
10091
|
-
const ttlMs = typeof candidate.ttlMs === "number" ? candidate.ttlMs : undefined;
|
|
10092
|
-
return { pid: candidate.pid, timestamp: candidate.timestamp, ...ttlMs !== undefined ? { ttlMs } : {} };
|
|
10093
|
-
}
|
|
10094
|
-
return null;
|
|
10095
|
-
} catch {
|
|
10096
|
-
return null;
|
|
10097
|
-
}
|
|
10098
|
-
}
|
|
10099
|
-
async function isAppHeartbeatFresh(repoDir, now = Date.now(), maxAgeMs = APP_HEARTBEAT_FRESH_MS) {
|
|
10100
|
-
const heartbeat = await readAppHeartbeat(repoDir);
|
|
10101
|
-
if (!heartbeat)
|
|
10102
|
-
return false;
|
|
10103
|
-
return now - heartbeat.timestamp < (heartbeat.ttlMs ?? maxAgeMs);
|
|
10104
|
-
}
|
|
10105
|
-
|
|
10106
|
-
// src/lib/remote-auth/recovery/classify.ts
|
|
10107
|
-
var STALE_LOCK_MIN_AGE_MS = 2 * 60 * 1000;
|
|
10108
|
-
class InsecureTransportError extends Error {
|
|
10109
|
-
code = "InsecureTransport";
|
|
10110
|
-
constructor() {
|
|
10111
|
-
super("This 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) to sync with a saved connection.");
|
|
10112
|
-
this.name = "InsecureTransportError";
|
|
10113
|
-
}
|
|
10114
|
-
}
|
|
10115
|
-
function isInsecureTransportError(e) {
|
|
10116
|
-
return e?.code === "InsecureTransport";
|
|
10117
|
-
}
|
|
10118
|
-
function isMergeConflictError(e) {
|
|
10119
|
-
return e?.code === "MergeConflictError";
|
|
10120
|
-
}
|
|
10121
|
-
function classifyFromHealth(health, opts = {}) {
|
|
10122
|
-
const minLockAgeMs = opts.minLockAgeMs ?? STALE_LOCK_MIN_AGE_MS;
|
|
10123
|
-
if (!health.hasGitDir)
|
|
10124
|
-
return "needs_repair";
|
|
10125
|
-
if (health.hasInterruptedRebase)
|
|
10126
|
-
return "needs_repair";
|
|
10127
|
-
if (health.hasInterruptedCherryPick)
|
|
10128
|
-
return "needs_repair";
|
|
10129
|
-
if (health.hasInterruptedMerge)
|
|
10130
|
-
return "needs_repair";
|
|
10131
|
-
if (health.headUnreadable)
|
|
10132
|
-
return "needs_repair";
|
|
10133
|
-
if (health.isDetachedHead)
|
|
10134
|
-
return "needs_repair";
|
|
10135
|
-
if (health.hasStaleLock && (health.lockAgeMs ?? 0) >= minLockAgeMs) {
|
|
10136
|
-
return "stale_lock";
|
|
10137
|
-
}
|
|
10138
|
-
return null;
|
|
10139
|
-
}
|
|
10140
|
-
|
|
10141
|
-
// src/lib/remote-auth/recovery/inspect.ts
|
|
10142
|
-
import * as fs4 from "node:fs";
|
|
10143
|
-
import path18 from "node:path";
|
|
10144
|
-
import git2 from "isomorphic-git";
|
|
10145
|
-
|
|
10146
|
-
// src/lib/remote-auth/recovery/locks.ts
|
|
10147
|
-
import { readdir as readdir6, rm as rm4, stat as stat6 } from "node:fs/promises";
|
|
10148
|
-
import path17 from "node:path";
|
|
10149
|
-
var TOP_LEVEL_LOCK_NAMES = [
|
|
10150
|
-
"index.lock",
|
|
10151
|
-
"HEAD.lock",
|
|
10152
|
-
"config.lock",
|
|
10153
|
-
"packed-refs.lock"
|
|
10154
|
-
];
|
|
10155
|
-
async function collectRefLockPaths(dir, out) {
|
|
10156
|
-
let entries;
|
|
10157
|
-
try {
|
|
10158
|
-
entries = await readdir6(dir, { withFileTypes: true });
|
|
10159
|
-
} catch {
|
|
10160
|
-
return;
|
|
10161
|
-
}
|
|
10162
|
-
for (const entry of entries) {
|
|
10163
|
-
const abs = path17.join(dir, entry.name);
|
|
10164
|
-
if (entry.isDirectory()) {
|
|
10165
|
-
await collectRefLockPaths(abs, out);
|
|
10166
|
-
} else if (entry.isFile() && entry.name.endsWith(".lock")) {
|
|
10167
|
-
out.push(abs);
|
|
10168
|
-
}
|
|
10169
|
-
}
|
|
10170
|
-
}
|
|
10171
|
-
async function findLockCandidates(gitDir, now) {
|
|
10172
|
-
const paths = TOP_LEVEL_LOCK_NAMES.map((name) => path17.join(gitDir, name));
|
|
10173
|
-
await collectRefLockPaths(path17.join(gitDir, "refs"), paths);
|
|
10174
|
-
const candidates = [];
|
|
10175
|
-
for (const p of paths) {
|
|
10176
|
-
try {
|
|
10177
|
-
const s = await stat6(p);
|
|
10178
|
-
if (s.isFile())
|
|
10179
|
-
candidates.push({ path: p, ageMs: now - s.mtimeMs });
|
|
10180
|
-
} catch {}
|
|
10181
|
-
}
|
|
10182
|
-
return candidates;
|
|
10183
|
-
}
|
|
10184
|
-
async function sweepStaleLocks(gitDir, now = Date.now(), minAgeMs = STALE_LOCK_MIN_AGE_MS) {
|
|
10185
|
-
const candidates = await findLockCandidates(gitDir, now);
|
|
10186
|
-
if (candidates.length === 0)
|
|
10187
|
-
return "clean";
|
|
10188
|
-
if (candidates.some((c) => c.ageMs < minAgeMs))
|
|
10189
|
-
return "fresh";
|
|
10190
|
-
for (const c of candidates) {
|
|
10191
|
-
await rm4(c.path, { force: true });
|
|
10192
|
-
}
|
|
10193
|
-
return "swept";
|
|
10194
|
-
}
|
|
10195
|
-
|
|
10196
|
-
// src/lib/remote-auth/recovery/inspect.ts
|
|
10197
|
-
async function inspectRepo(ctx, opts = {}) {
|
|
10198
|
-
let repoDir = ctx.repoDir;
|
|
10199
|
-
try {
|
|
10200
|
-
const source = ctx.source ?? await detectProjectSource(ctx.repoDir);
|
|
10201
|
-
if (source.type === "local-git-folder")
|
|
10202
|
-
repoDir = gitScopeFor(source);
|
|
10203
|
-
} catch {}
|
|
10204
|
-
const gitDir = gitDirFor(repoDir);
|
|
10205
|
-
const hasGitDir = fs4.existsSync(gitDir);
|
|
10206
|
-
if (!hasGitDir) {
|
|
10207
|
-
return {
|
|
10208
|
-
hasGitDir: false,
|
|
10209
|
-
isDetachedHead: false,
|
|
10210
|
-
headUnreadable: false,
|
|
10211
|
-
hasStaleLock: false,
|
|
10212
|
-
hasInterruptedMerge: false,
|
|
10213
|
-
hasInterruptedRebase: false,
|
|
10214
|
-
hasInterruptedCherryPick: false,
|
|
10215
|
-
hasLocalChanges: false
|
|
10216
|
-
};
|
|
10217
|
-
}
|
|
10218
|
-
let currentBranch;
|
|
10219
|
-
let isDetachedHead = false;
|
|
10220
|
-
let headUnreadable = false;
|
|
10221
|
-
try {
|
|
10222
|
-
const branch = await git2.currentBranch({ fs: fs4, dir: repoDir });
|
|
10223
|
-
if (branch == null) {
|
|
10224
|
-
isDetachedHead = true;
|
|
10225
|
-
} else {
|
|
10226
|
-
currentBranch = branch;
|
|
10227
|
-
}
|
|
10228
|
-
} catch {
|
|
10229
|
-
headUnreadable = true;
|
|
10230
|
-
}
|
|
10231
|
-
const lockCandidates = await findLockCandidates(gitDir, Date.now());
|
|
10232
|
-
const hasStaleLock = lockCandidates.length > 0;
|
|
10233
|
-
const lockAgeMs = hasStaleLock ? Math.min(...lockCandidates.map((c) => c.ageMs)) : undefined;
|
|
10234
|
-
const hasInterruptedMerge = fs4.existsSync(path18.join(gitDir, "MERGE_HEAD"));
|
|
10235
|
-
const hasInterruptedRebase = fs4.existsSync(path18.join(gitDir, "rebase-merge")) || fs4.existsSync(path18.join(gitDir, "rebase-apply"));
|
|
10236
|
-
const hasInterruptedCherryPick = fs4.existsSync(path18.join(gitDir, "CHERRY_PICK_HEAD"));
|
|
10237
|
-
let hasLocalChanges = false;
|
|
10238
|
-
if (opts.checkLocalChanges !== false) {
|
|
10239
|
-
try {
|
|
10240
|
-
hasLocalChanges = await hasPendingChanges(repoDir);
|
|
10241
|
-
} catch {
|
|
10242
|
-
hasLocalChanges = true;
|
|
10243
|
-
}
|
|
10244
|
-
}
|
|
10245
|
-
return {
|
|
10246
|
-
hasGitDir,
|
|
10247
|
-
currentBranch,
|
|
10248
|
-
isDetachedHead,
|
|
10249
|
-
headUnreadable,
|
|
10250
|
-
hasStaleLock,
|
|
10251
|
-
lockAgeMs,
|
|
10252
|
-
hasInterruptedMerge,
|
|
10253
|
-
hasInterruptedRebase,
|
|
10254
|
-
hasInterruptedCherryPick,
|
|
10255
|
-
hasLocalChanges
|
|
10256
|
-
};
|
|
10257
|
-
}
|
|
10258
|
-
async function verifyRepoReadable(dir) {
|
|
10259
|
-
const headOid = await git2.resolveRef({ fs: fs4, dir, ref: "HEAD" });
|
|
10260
|
-
const { commit } = await git2.readCommit({ fs: fs4, dir, oid: headOid });
|
|
10261
|
-
await git2.readTree({ fs: fs4, dir, oid: commit.tree });
|
|
10262
|
-
}
|
|
10263
|
-
function isUnbornRepo(repoDir) {
|
|
10264
|
-
const objectsDir = path18.join(gitDirFor(repoDir), "objects");
|
|
10265
|
-
let entries;
|
|
10266
|
-
try {
|
|
10267
|
-
entries = fs4.readdirSync(objectsDir);
|
|
10268
|
-
} catch {
|
|
10269
|
-
return false;
|
|
10270
|
-
}
|
|
10271
|
-
for (const entry of entries) {
|
|
10272
|
-
if (/^[0-9a-f]{2}$/.test(entry)) {
|
|
10273
|
-
try {
|
|
10274
|
-
if (fs4.readdirSync(path18.join(objectsDir, entry)).length > 0)
|
|
10275
|
-
return false;
|
|
10276
|
-
} catch {
|
|
10277
|
-
return false;
|
|
10278
|
-
}
|
|
10279
|
-
} else if (entry === "pack") {
|
|
10280
|
-
try {
|
|
10281
|
-
if (fs4.readdirSync(path18.join(objectsDir, entry)).some((f) => f.endsWith(".pack"))) {
|
|
10282
|
-
return false;
|
|
10283
|
-
}
|
|
10284
|
-
} catch {
|
|
10285
|
-
return false;
|
|
10286
|
-
}
|
|
10287
|
-
}
|
|
10288
|
-
}
|
|
10289
|
-
return true;
|
|
10290
|
-
}
|
|
10291
|
-
|
|
10292
10155
|
// src/lib/publish/types.ts
|
|
10293
10156
|
function publishCredentialKey(host, account) {
|
|
10294
10157
|
const a = (account ?? "").trim();
|
|
@@ -10474,25 +10337,25 @@ ${tail}` : ""}`);
|
|
|
10474
10337
|
};
|
|
10475
10338
|
|
|
10476
10339
|
// src/lib/publish/providers/guided.ts
|
|
10477
|
-
import { copyFile as copyFile2, mkdir as mkdir8, writeFile as
|
|
10478
|
-
import
|
|
10340
|
+
import { copyFile as copyFile2, mkdir as mkdir8, writeFile as writeFile6 } from "node:fs/promises";
|
|
10341
|
+
import path17 from "node:path";
|
|
10479
10342
|
async function stageGuidedPackage(req, providerId, listingLines) {
|
|
10480
|
-
const packageDir =
|
|
10343
|
+
const packageDir = path17.join(path17.dirname(req.artifact.path), "publish", providerId);
|
|
10481
10344
|
await mkdir8(packageDir, { recursive: true });
|
|
10482
|
-
await copyFile2(req.artifact.path,
|
|
10345
|
+
await copyFile2(req.artifact.path, path17.join(packageDir, path17.basename(req.artifact.path)));
|
|
10483
10346
|
const { title, authors } = req.project;
|
|
10484
10347
|
const sheet = [
|
|
10485
10348
|
`# ${title || "Untitled"}`,
|
|
10486
10349
|
"",
|
|
10487
10350
|
`- **Authors:** ${authors.length ? authors.join(", ") : "(not set)"}`,
|
|
10488
|
-
`- **File:** ${
|
|
10351
|
+
`- **File:** ${path17.basename(req.artifact.path)}`,
|
|
10489
10352
|
...listingLines,
|
|
10490
10353
|
"",
|
|
10491
10354
|
"Copy these details into the platform's listing form when uploading.",
|
|
10492
10355
|
""
|
|
10493
10356
|
].join(`
|
|
10494
10357
|
`);
|
|
10495
|
-
await
|
|
10358
|
+
await writeFile6(path17.join(packageDir, "LISTING.md"), sheet, "utf8");
|
|
10496
10359
|
return packageDir;
|
|
10497
10360
|
}
|
|
10498
10361
|
|
|
@@ -10555,8 +10418,8 @@ var drivethrurpgProvider = {
|
|
|
10555
10418
|
};
|
|
10556
10419
|
|
|
10557
10420
|
// src/lib/publish/butler.ts
|
|
10558
|
-
import { chmod, mkdir as mkdir9, stat as
|
|
10559
|
-
import
|
|
10421
|
+
import { chmod, mkdir as mkdir9, stat as stat6, writeFile as writeFile7 } from "node:fs/promises";
|
|
10422
|
+
import path18 from "node:path";
|
|
10560
10423
|
import { unzipSync } from "fflate";
|
|
10561
10424
|
var BUTLER_DOWNLOAD_TIMEOUT_MS = 300000;
|
|
10562
10425
|
function butlerBrothChannel(platform = process.platform, arch = process.arch) {
|
|
@@ -10576,7 +10439,7 @@ function butlerBinaryName(platform = process.platform) {
|
|
|
10576
10439
|
}
|
|
10577
10440
|
async function fileExists2(p) {
|
|
10578
10441
|
try {
|
|
10579
|
-
return (await
|
|
10442
|
+
return (await stat6(p)).isFile();
|
|
10580
10443
|
} catch {
|
|
10581
10444
|
return false;
|
|
10582
10445
|
}
|
|
@@ -10593,8 +10456,8 @@ async function ensureButler(deps) {
|
|
|
10593
10456
|
}
|
|
10594
10457
|
if (await commandExists("butler", runCommand, deps.env))
|
|
10595
10458
|
return "butler";
|
|
10596
|
-
const cacheDir =
|
|
10597
|
-
const cached =
|
|
10459
|
+
const cacheDir = path18.join(deps.configDir ?? defaultConfigDir(), "tools", "butler");
|
|
10460
|
+
const cached = path18.join(cacheDir, butlerBinaryName());
|
|
10598
10461
|
if (await fileExists2(cached))
|
|
10599
10462
|
return cached;
|
|
10600
10463
|
const channel = butlerBrothChannel();
|
|
@@ -10621,9 +10484,9 @@ async function ensureButler(deps) {
|
|
|
10621
10484
|
for (const [name, data] of Object.entries(files)) {
|
|
10622
10485
|
if (name.endsWith("/"))
|
|
10623
10486
|
continue;
|
|
10624
|
-
const target =
|
|
10625
|
-
await
|
|
10626
|
-
if (
|
|
10487
|
+
const target = path18.join(cacheDir, path18.basename(name));
|
|
10488
|
+
await writeFile7(target, data);
|
|
10489
|
+
if (path18.basename(name) === butlerBinaryName()) {
|
|
10627
10490
|
await chmod(target, 493).catch(() => {});
|
|
10628
10491
|
foundBinary = true;
|
|
10629
10492
|
}
|
|
@@ -11051,8 +10914,8 @@ function publishProviderFor(id) {
|
|
|
11051
10914
|
}
|
|
11052
10915
|
|
|
11053
10916
|
// src/lib/publish/run-publish.ts
|
|
11054
|
-
import { readdir as
|
|
11055
|
-
import
|
|
10917
|
+
import { readdir as readdir6, stat as stat7 } from "node:fs/promises";
|
|
10918
|
+
import path19 from "node:path";
|
|
11056
10919
|
async function resolvePublishRequest(options, deps) {
|
|
11057
10920
|
const provider = publishProviderFor(options.providerId);
|
|
11058
10921
|
const { manifest, manifestDir } = await loadManifestWithPath(options.manifestPath ?? options.projectDir, { explicit: options.manifestPath !== undefined });
|
|
@@ -11063,9 +10926,9 @@ async function resolvePublishRequest(options, deps) {
|
|
|
11063
10926
|
const effectiveAccount = bookAccount || deps.credentialAccount;
|
|
11064
10927
|
const effectiveDeps = effectiveAccount ? { ...deps, credentialAccount: effectiveAccount } : deps;
|
|
11065
10928
|
const outDir = resolveOutputDir(manifestDir, config.title);
|
|
11066
|
-
const defaultArtifact = provider.info.format === "pdf" ?
|
|
10929
|
+
const defaultArtifact = provider.info.format === "pdf" ? path19.join(outDir, artifactName(config.title, "pdf")) : outDir;
|
|
11067
10930
|
const artifact = {
|
|
11068
|
-
path: options.artifactPath ?
|
|
10931
|
+
path: options.artifactPath ? path19.resolve(options.projectDir, options.artifactPath) : defaultArtifact,
|
|
11069
10932
|
format: provider.info.format
|
|
11070
10933
|
};
|
|
11071
10934
|
return {
|
|
@@ -11083,7 +10946,7 @@ var PDF_HINT = "Build the PDF first (gutterpress build, or export it from the ap
|
|
|
11083
10946
|
var HTML_HINT = "Build the website export first (gutterpress build --format html).";
|
|
11084
10947
|
async function artifactIssues(artifact) {
|
|
11085
10948
|
try {
|
|
11086
|
-
const s = await
|
|
10949
|
+
const s = await stat7(artifact.path);
|
|
11087
10950
|
if (artifact.format === "pdf" && !s.isFile()) {
|
|
11088
10951
|
return [
|
|
11089
10952
|
{
|
|
@@ -11129,7 +10992,7 @@ async function artifactIssues(artifact) {
|
|
|
11129
10992
|
var BUILD_FINGERPRINT_FILENAME = "build-fingerprint.json";
|
|
11130
10993
|
async function htmlDirIssues(dir) {
|
|
11131
10994
|
const issues = [];
|
|
11132
|
-
const isThere = async (rel) =>
|
|
10995
|
+
const isThere = async (rel) => stat7(path19.join(dir, rel)).then(() => true, () => false);
|
|
11133
10996
|
if (!await isThere(BOOK_HTML)) {
|
|
11134
10997
|
issues.push({
|
|
11135
10998
|
severity: "error",
|
|
@@ -11137,7 +11000,7 @@ async function htmlDirIssues(dir) {
|
|
|
11137
11000
|
message: `${dir} has no ${BOOK_HTML} — it isn't an HTML export. ${HTML_HINT}`
|
|
11138
11001
|
});
|
|
11139
11002
|
}
|
|
11140
|
-
const entries = await
|
|
11003
|
+
const entries = await readdir6(dir).catch(() => []);
|
|
11141
11004
|
const extras = entries.filter((name) => name.toLowerCase().endsWith(".pdf") || name === BUILD_FINGERPRINT_FILENAME);
|
|
11142
11005
|
if (extras.length > 0) {
|
|
11143
11006
|
issues.push({
|
|
@@ -11236,285 +11099,39 @@ async function connectPublishProvider(options, deps) {
|
|
|
11236
11099
|
await deps.tokenStore.set(key, candidate);
|
|
11237
11100
|
return { connected: true, providerId: info7.id };
|
|
11238
11101
|
}
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
import
|
|
11244
|
-
import
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
merged += `<<<<<<< ${OUR_LABEL}
|
|
11271
|
-
`;
|
|
11272
|
-
merged += item.conflict.a.join("");
|
|
11273
|
-
merged += `=======
|
|
11274
|
-
`;
|
|
11275
|
-
merged += item.conflict.b.join("");
|
|
11276
|
-
merged += `>>>>>>> ${THEIR_LABEL}
|
|
11277
|
-
`;
|
|
11278
|
-
}
|
|
11279
|
-
}
|
|
11280
|
-
return merged;
|
|
11281
|
-
}
|
|
11282
|
-
async function readBlobOrNull(dir, cache2, oid, filepath) {
|
|
11283
|
-
try {
|
|
11284
|
-
const r = await git3.readBlob({ fs: fs5, dir, cache: cache2, oid, filepath });
|
|
11285
|
-
return { oid: r.oid, blob: r.blob };
|
|
11286
|
-
} catch {
|
|
11287
|
-
return null;
|
|
11288
|
-
}
|
|
11289
|
-
}
|
|
11290
|
-
async function writeTreeFile(dir, filepath, content) {
|
|
11291
|
-
const abs = path22.join(dir, filepath);
|
|
11292
|
-
await mkdir10(path22.dirname(abs), { recursive: true });
|
|
11293
|
-
await writeFile9(abs, content);
|
|
11294
|
-
}
|
|
11295
|
-
async function bothAddedPaths(dir, cache2, ourTip, theirTip, allowUnrelated) {
|
|
11296
|
-
const bases = await git3.findMergeBase({ fs: fs5, dir, cache: cache2, oids: [ourTip, theirTip] });
|
|
11297
|
-
const base = bases[0];
|
|
11298
|
-
if (!base && !allowUnrelated)
|
|
11299
|
-
return null;
|
|
11300
|
-
const out = [];
|
|
11301
|
-
await git3.walk({
|
|
11302
|
-
fs: fs5,
|
|
11303
|
-
dir,
|
|
11304
|
-
cache: cache2,
|
|
11305
|
-
trees: [
|
|
11306
|
-
git3.TREE({ ref: ourTip }),
|
|
11307
|
-
git3.TREE({ ref: theirTip }),
|
|
11308
|
-
...base ? [git3.TREE({ ref: base })] : []
|
|
11309
|
-
],
|
|
11310
|
-
map: async (filepath, entries) => {
|
|
11311
|
-
if (filepath === ".")
|
|
11312
|
-
return true;
|
|
11313
|
-
const [ours, theirs, inBase] = entries;
|
|
11314
|
-
const types = await Promise.all([ours, theirs, inBase].map((e) => e ? e.type() : Promise.resolve(undefined)));
|
|
11315
|
-
if (types.some((t) => t === "tree"))
|
|
11316
|
-
return true;
|
|
11317
|
-
if (!ours || !theirs || inBase)
|
|
11318
|
-
return null;
|
|
11319
|
-
const [a, b] = await Promise.all([ours.oid(), theirs.oid()]);
|
|
11320
|
-
if (a !== b)
|
|
11321
|
-
out.push(filepath);
|
|
11322
|
-
return null;
|
|
11323
|
-
}
|
|
11324
|
-
});
|
|
11325
|
-
return out;
|
|
11326
|
-
}
|
|
11327
|
-
async function convergeMerge(params) {
|
|
11328
|
-
const { dir, cache: cache2, branch, theirs, author } = params;
|
|
11329
|
-
const ourTip = await git3.resolveRef({ fs: fs5, dir, ref: branch });
|
|
11330
|
-
const [ourCommit, theirCommit] = await Promise.all([
|
|
11331
|
-
git3.readCommit({ fs: fs5, dir, cache: cache2, oid: ourTip }),
|
|
11332
|
-
git3.readCommit({ fs: fs5, dir, cache: cache2, oid: theirs })
|
|
11333
|
-
]);
|
|
11334
|
-
const localNewer = ourCommit.commit.committer.timestamp >= theirCommit.commit.committer.timestamp;
|
|
11335
|
-
const combined = new Set;
|
|
11336
|
-
const driverBinary = new Set;
|
|
11337
|
-
const imageClashes = [];
|
|
11338
|
-
const attempt = () => git3.merge({
|
|
11339
|
-
fs: fs5,
|
|
11340
|
-
dir,
|
|
11341
|
-
cache: cache2,
|
|
11342
|
-
ours: branch,
|
|
11343
|
-
theirs,
|
|
11344
|
-
author,
|
|
11345
|
-
message: CONVERGE_MERGE_MESSAGE,
|
|
11346
|
-
allowUnrelatedHistories: params.allowUnrelatedHistories ?? false,
|
|
11347
|
-
mergeDriver: ({ contents, path: filepath }) => {
|
|
11348
|
-
const [base, ours, theirsContent] = contents;
|
|
11349
|
-
if (IMAGE_EXT.test(filepath) || stringLooksBinary(ours) || stringLooksBinary(theirsContent) || stringLooksBinary(base)) {
|
|
11350
|
-
driverBinary.add(filepath);
|
|
11351
|
-
return { cleanMerge: false, mergedText: ours };
|
|
11352
|
-
}
|
|
11353
|
-
combined.add(filepath);
|
|
11354
|
-
return {
|
|
11355
|
-
cleanMerge: true,
|
|
11356
|
-
mergedText: mergeWithMarkers(base, ours, theirsContent)
|
|
11357
|
-
};
|
|
11358
|
-
}
|
|
11359
|
-
});
|
|
11360
|
-
const snapshot = (message) => snapshotWorkingTreeUnlocked({
|
|
11361
|
-
projectDir: dir,
|
|
11362
|
-
repoRoot: dir,
|
|
11363
|
-
message,
|
|
11364
|
-
authorName: params.authorName,
|
|
11365
|
-
authorEmail: params.authorEmail
|
|
11366
|
-
});
|
|
11367
|
-
const postRestore = [];
|
|
11368
|
-
const equalize = async (paths) => {
|
|
11369
|
-
for (const p of paths.binary) {
|
|
11370
|
-
const [ourBlob, theirBlob] = await Promise.all([
|
|
11371
|
-
readBlobOrNull(dir, cache2, ourTip, p),
|
|
11372
|
-
readBlobOrNull(dir, cache2, theirs, p)
|
|
11373
|
-
]);
|
|
11374
|
-
if (!ourBlob || !theirBlob)
|
|
11375
|
-
continue;
|
|
11376
|
-
await writeTreeFile(dir, p, theirBlob.blob);
|
|
11377
|
-
if (localNewer)
|
|
11378
|
-
postRestore.push({ path: p, bytes: ourBlob.blob });
|
|
11379
|
-
if (IMAGE_EXT.test(p)) {
|
|
11380
|
-
imageClashes.push({
|
|
11381
|
-
path: p,
|
|
11382
|
-
localOid: ourBlob.oid,
|
|
11383
|
-
remoteOid: theirBlob.oid,
|
|
11384
|
-
kept: localNewer ? "local" : "online"
|
|
11385
|
-
});
|
|
11386
|
-
}
|
|
11387
|
-
}
|
|
11388
|
-
for (const p of paths.deleteByUs) {
|
|
11389
|
-
const theirBlob = await readBlobOrNull(dir, cache2, theirs, p);
|
|
11390
|
-
if (theirBlob)
|
|
11391
|
-
await writeTreeFile(dir, p, theirBlob.blob);
|
|
11392
|
-
}
|
|
11393
|
-
for (const p of paths.deleteByTheirs) {
|
|
11394
|
-
const ourBlob = await readBlobOrNull(dir, cache2, ourTip, p);
|
|
11395
|
-
await unlink3(path22.join(dir, p)).catch((e) => {
|
|
11396
|
-
if (e?.code !== "ENOENT")
|
|
11397
|
-
throw e;
|
|
11398
|
-
});
|
|
11399
|
-
if (ourBlob)
|
|
11400
|
-
postRestore.push({ path: p, bytes: ourBlob.blob });
|
|
11401
|
-
}
|
|
11402
|
-
if (await hasPendingChanges(dir))
|
|
11403
|
-
await snapshot(CONVERGE_PREPARE_MESSAGE);
|
|
11404
|
-
};
|
|
11405
|
-
try {
|
|
11406
|
-
await attempt();
|
|
11407
|
-
} catch (e) {
|
|
11408
|
-
if (isMergeConflictError(e)) {
|
|
11409
|
-
await equalize({
|
|
11410
|
-
binary: (e.data.bothModified ?? []).filter((p) => driverBinary.has(p)),
|
|
11411
|
-
deleteByUs: e.data.deleteByUs ?? [],
|
|
11412
|
-
deleteByTheirs: e.data.deleteByTheirs ?? []
|
|
11413
|
-
});
|
|
11414
|
-
await attempt();
|
|
11415
|
-
} else if (e?.code === "MergeNotSupportedError") {
|
|
11416
|
-
const added = await bothAddedPaths(dir, cache2, ourTip, theirs, params.allowUnrelatedHistories ?? false);
|
|
11417
|
-
if (added === null || added.length === 0)
|
|
11418
|
-
throw e;
|
|
11419
|
-
await equalize({ binary: added, deleteByUs: [], deleteByTheirs: [] });
|
|
11420
|
-
await attempt();
|
|
11421
|
-
} else {
|
|
11422
|
-
throw e;
|
|
11423
|
-
}
|
|
11424
|
-
}
|
|
11425
|
-
await git3.checkout({ fs: fs5, dir, cache: cache2, ref: branch, force: true });
|
|
11426
|
-
if (postRestore.length > 0) {
|
|
11427
|
-
for (const r of postRestore)
|
|
11428
|
-
await writeTreeFile(dir, r.path, r.bytes);
|
|
11429
|
-
if (await hasPendingChanges(dir))
|
|
11430
|
-
await snapshot(CONVERGE_RESTORE_MESSAGE);
|
|
11431
|
-
}
|
|
11432
|
-
return {
|
|
11433
|
-
oid: await git3.resolveRef({ fs: fs5, dir, ref: branch }),
|
|
11434
|
-
combinedFiles: [...combined].sort(),
|
|
11435
|
-
imageClashes
|
|
11436
|
-
};
|
|
11437
|
-
}
|
|
11438
|
-
|
|
11439
|
-
// src/lib/remote-auth/transport.ts
|
|
11440
|
-
import git4 from "isomorphic-git";
|
|
11441
|
-
|
|
11442
|
-
// src/lib/remote-auth/sync-messages.ts
|
|
11443
|
-
var MSG_NO_REMOTE = "This project isn't connected to an online repository yet.";
|
|
11444
|
-
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.";
|
|
11445
|
-
|
|
11446
|
-
// src/lib/remote-auth/transport.ts
|
|
11447
|
-
async function repoDirFor(projectDir) {
|
|
11448
|
-
const source = await detectProjectSource(projectDir);
|
|
11449
|
-
if (source.type === "local-git-folder")
|
|
11450
|
-
return gitScopeFor(source);
|
|
11451
|
-
return projectDir;
|
|
11452
|
-
}
|
|
11453
|
-
function isCredentialTransmissionSafe(url) {
|
|
11454
|
-
try {
|
|
11455
|
-
const u = new URL(url);
|
|
11456
|
-
if (u.protocol === "https:")
|
|
11457
|
-
return true;
|
|
11458
|
-
if (u.protocol === "http:") {
|
|
11459
|
-
const host = u.hostname.toLowerCase();
|
|
11460
|
-
return host === "127.0.0.1" || host === "[::1]" || host === "localhost";
|
|
11461
|
-
}
|
|
11462
|
-
return false;
|
|
11463
|
-
} catch {
|
|
11464
|
-
return false;
|
|
11465
|
-
}
|
|
11466
|
-
}
|
|
11467
|
-
function onAuthFor(credential) {
|
|
11468
|
-
if (!credential)
|
|
11469
|
-
return {};
|
|
11470
|
-
return {
|
|
11471
|
-
onAuth: (url) => {
|
|
11472
|
-
if (!isCredentialTransmissionSafe(url))
|
|
11473
|
-
throw new InsecureTransportError;
|
|
11474
|
-
return {
|
|
11475
|
-
username: credential.kind === "github-oauth" ? "x-access-token" : credential.username || credential.token,
|
|
11476
|
-
password: credential.token
|
|
11477
|
-
};
|
|
11478
|
-
}
|
|
11479
|
-
};
|
|
11480
|
-
}
|
|
11481
|
-
async function resolveTransport(dir, options) {
|
|
11482
|
-
const origin = await syncRemoteFor(dir);
|
|
11483
|
-
if (!origin?.url)
|
|
11484
|
-
throw new Error(MSG_NO_REMOTE);
|
|
11485
|
-
const { cleanUrl, credential: urlCredential } = extractUrlCredential(origin.url);
|
|
11486
|
-
let parsed;
|
|
11487
|
-
try {
|
|
11488
|
-
parsed = new URL(cleanUrl);
|
|
11489
|
-
} catch {
|
|
11490
|
-
throw new Error(MSG_SSH_REMOTE);
|
|
11491
|
-
}
|
|
11492
|
-
if (!/^https?:$/.test(parsed.protocol))
|
|
11493
|
-
throw new Error(MSG_SSH_REMOTE);
|
|
11494
|
-
const host = credentialHostKey(cleanUrl);
|
|
11495
|
-
let credential = options.credential ?? undefined;
|
|
11496
|
-
if (!credential && options.tokenStore) {
|
|
11497
|
-
try {
|
|
11498
|
-
credential = await options.tokenStore.get(host) ?? undefined;
|
|
11499
|
-
} catch {
|
|
11500
|
-
credential = undefined;
|
|
11501
|
-
}
|
|
11502
|
-
}
|
|
11503
|
-
if (!credential && urlCredential)
|
|
11504
|
-
credential = urlCredential;
|
|
11505
|
-
return {
|
|
11506
|
-
remote: origin.remote,
|
|
11507
|
-
url: cleanUrl,
|
|
11508
|
-
host,
|
|
11509
|
-
...credential ? { credential } : {}
|
|
11510
|
-
};
|
|
11511
|
-
}
|
|
11512
|
-
|
|
11102
|
+
// src/lib/theme-manager.ts
|
|
11103
|
+
import { isSeq as isSeq4, Scalar as Scalar2 } from "yaml";
|
|
11104
|
+
var THEMES_DIR = "themes";
|
|
11105
|
+
var THEME_HREF_RE = new RegExp(`^${THEMES_DIR}/([^/]+)/theme\\.css$`);
|
|
11106
|
+
// src/lib/theme-import.ts
|
|
11107
|
+
import { unzipSync as unzipSync2 } from "fflate";
|
|
11108
|
+
var MAX_THEME_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
11109
|
+
var MAX_THEME_UNZIPPED_BYTES = 25 * 1024 * 1024;
|
|
11110
|
+
var KNOWN_THEME_FILES = new Set(["theme.css", "theme.json"]);
|
|
11111
|
+
var ALLOWED_ASSET_EXTS = new Set([
|
|
11112
|
+
".woff",
|
|
11113
|
+
".woff2",
|
|
11114
|
+
".ttf",
|
|
11115
|
+
".otf",
|
|
11116
|
+
".eot",
|
|
11117
|
+
".png",
|
|
11118
|
+
".jpg",
|
|
11119
|
+
".jpeg",
|
|
11120
|
+
".gif",
|
|
11121
|
+
".svg",
|
|
11122
|
+
".webp",
|
|
11123
|
+
".avif",
|
|
11124
|
+
".css"
|
|
11125
|
+
]);
|
|
11126
|
+
// src/lib/manifest-config.ts
|
|
11127
|
+
import { isSeq as isSeq5, isMap as isMap3, YAMLSeq as YAMLSeq3, Scalar as Scalar3 } from "yaml";
|
|
11128
|
+
// src/lib/host-policy.ts
|
|
11129
|
+
var AUTO_SNAPSHOT_MAX_MINUTES = 24 * 60;
|
|
11130
|
+
var AUTO_SYNC_MAX_MINUTES = 24 * 60;
|
|
11131
|
+
// src/lib/remote-auth/github-repos.ts
|
|
11132
|
+
var MANIFEST_NAMES = new Set(MANIFEST_FILENAMES);
|
|
11513
11133
|
// src/lib/remote-auth/clone.ts
|
|
11514
|
-
import
|
|
11515
|
-
import { mkdir as mkdir11, readdir as readdir8, readFile as readFile25, rm as rm5, writeFile as writeFile10 } from "node:fs/promises";
|
|
11516
|
-
import path23 from "node:path";
|
|
11517
|
-
import git5 from "isomorphic-git";
|
|
11134
|
+
import git3 from "isomorphic-git";
|
|
11518
11135
|
|
|
11519
11136
|
// src/lib/remote-auth/git-http.ts
|
|
11520
11137
|
import httpNode from "isomorphic-git/http/node";
|
|
@@ -11607,525 +11224,29 @@ function withIdleTimeout(http2, idleMs = GIT_HTTP_IDLE_TIMEOUT_MS, uploadMs = GI
|
|
|
11607
11224
|
}
|
|
11608
11225
|
var defaultGitHttp = withIdleTimeout(httpNode);
|
|
11609
11226
|
|
|
11610
|
-
// src/lib/remote-auth/
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
// src/lib/remote-auth/clone.ts
|
|
11614
|
-
var PROVENANCE_FILE = "gutterpress-remote.json";
|
|
11615
|
-
function provenancePath(projectDir) {
|
|
11616
|
-
return path23.join(projectDir, ".git", PROVENANCE_FILE);
|
|
11617
|
-
}
|
|
11618
|
-
async function assertCloneTarget(dir) {
|
|
11619
|
-
try {
|
|
11620
|
-
const entries = await readdir8(dir);
|
|
11621
|
-
if (entries.length > 0) {
|
|
11622
|
-
throw new Error("That folder already has files in it. Choose an empty folder for the project.");
|
|
11623
|
-
}
|
|
11624
|
-
} catch (e) {
|
|
11625
|
-
if (e.code === "ENOENT")
|
|
11626
|
-
return;
|
|
11627
|
-
throw e;
|
|
11628
|
-
}
|
|
11629
|
-
}
|
|
11630
|
-
function friendlyCloneError(e) {
|
|
11631
|
-
if (isInsecureTransportError(e)) {
|
|
11632
|
-
return new Error("That repository address isn't secure, so the saved connection wasn't sent — connections are never sent over an insecure address. Use a secure address (starting with https), or a local loopback address for a server on this computer.", { cause: e });
|
|
11633
|
-
}
|
|
11634
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
11635
|
-
if (/401|403|auth|credential/i.test(msg)) {
|
|
11636
|
-
return new Error("GitHub didn't accept the connection for this repository. Reconnect GitHub and try again.", { cause: e });
|
|
11637
|
-
}
|
|
11638
|
-
if (/404|not found/i.test(msg)) {
|
|
11639
|
-
return new Error("That repository couldn't be found. It may have been renamed or you may no longer have access.", { cause: e });
|
|
11640
|
-
}
|
|
11641
|
-
if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|network|fetch failed/i.test(msg)) {
|
|
11642
|
-
return new Error(OFFLINE_MESSAGE, { cause: e });
|
|
11643
|
-
}
|
|
11644
|
-
if (e instanceof Error && /already has files/i.test(e.message)) {
|
|
11645
|
-
return e;
|
|
11646
|
-
}
|
|
11647
|
-
return new Error("The project couldn't be downloaded from GitHub. Please try again.", { cause: e });
|
|
11648
|
-
}
|
|
11649
|
-
async function cloneRepository(options) {
|
|
11650
|
-
const { dir, onProgress, tokenStore, provenance } = options;
|
|
11651
|
-
if (!path23.isAbsolute(dir)) {
|
|
11652
|
-
throw new Error("cloneRepository requires an absolute destination path.");
|
|
11653
|
-
}
|
|
11654
|
-
let parsed;
|
|
11655
|
-
try {
|
|
11656
|
-
parsed = new URL(options.url);
|
|
11657
|
-
} catch {
|
|
11658
|
-
throw new Error("That repository address is not a valid web URL.");
|
|
11659
|
-
}
|
|
11660
|
-
if (!/^https?:$/.test(parsed.protocol)) {
|
|
11661
|
-
throw new Error("Only HTTPS repository addresses are supported. SSH addresses (git@…) can't be used here.");
|
|
11662
|
-
}
|
|
11663
|
-
const { cleanUrl, credential: urlCredential } = extractUrlCredential(options.url);
|
|
11664
|
-
const credential = options.credential ?? urlCredential;
|
|
11665
|
-
if (urlCredential && tokenStore && !await tokenStore.get(urlCredential.host)) {
|
|
11666
|
-
await tokenStore.set(urlCredential.host, urlCredential);
|
|
11667
|
-
}
|
|
11668
|
-
return withRepoLock(dir, async () => {
|
|
11669
|
-
await assertCloneTarget(dir);
|
|
11670
|
-
const dirExistedBefore = fs6.existsSync(dir);
|
|
11671
|
-
await mkdir11(dir, { recursive: true });
|
|
11672
|
-
try {
|
|
11673
|
-
await git5.clone({
|
|
11674
|
-
fs: fs6,
|
|
11675
|
-
http: options.httpClient ?? defaultGitHttp,
|
|
11676
|
-
dir,
|
|
11677
|
-
url: cleanUrl,
|
|
11678
|
-
singleBranch: true,
|
|
11679
|
-
...options.branch ? { ref: options.branch } : {},
|
|
11680
|
-
...options.depth ? { depth: options.depth } : {},
|
|
11681
|
-
...onAuthFor(credential),
|
|
11682
|
-
...onProgress ? {
|
|
11683
|
-
onProgress: (p) => onProgress({
|
|
11684
|
-
phase: p.phase,
|
|
11685
|
-
loaded: p.loaded,
|
|
11686
|
-
...p.total ? { total: p.total } : {}
|
|
11687
|
-
})
|
|
11688
|
-
} : {}
|
|
11689
|
-
});
|
|
11690
|
-
} catch (e) {
|
|
11691
|
-
const friendly = friendlyCloneError(e);
|
|
11692
|
-
if (!dirExistedBefore) {
|
|
11693
|
-
try {
|
|
11694
|
-
await rm5(dir, { recursive: true, force: true });
|
|
11695
|
-
} catch {
|
|
11696
|
-
throw new Error(`${friendly.message} A partial download was left at ${dir} — delete that folder before trying again.`, { cause: e });
|
|
11697
|
-
}
|
|
11698
|
-
}
|
|
11699
|
-
throw friendly;
|
|
11700
|
-
}
|
|
11701
|
-
if (provenance) {
|
|
11702
|
-
try {
|
|
11703
|
-
await writeFile10(provenancePath(dir), JSON.stringify(provenance, null, 2), "utf8");
|
|
11704
|
-
} catch {}
|
|
11705
|
-
}
|
|
11706
|
-
let branch;
|
|
11707
|
-
try {
|
|
11708
|
-
branch = await git5.currentBranch({ fs: fs6, dir }) ?? undefined;
|
|
11709
|
-
} catch {
|
|
11710
|
-
branch = undefined;
|
|
11711
|
-
}
|
|
11712
|
-
return { projectDir: dir, ...branch ? { branch } : {} };
|
|
11713
|
-
});
|
|
11714
|
-
}
|
|
11715
|
-
|
|
11716
|
-
// src/lib/remote-auth/recovery/repair.ts
|
|
11717
|
-
var INTERRUPTED_STATE = [
|
|
11718
|
-
"MERGE_HEAD",
|
|
11719
|
-
"MERGE_MSG",
|
|
11720
|
-
"MERGE_MODE",
|
|
11721
|
-
"CHERRY_PICK_HEAD",
|
|
11722
|
-
"REVERT_HEAD",
|
|
11723
|
-
"sequencer",
|
|
11724
|
-
"rebase-merge",
|
|
11725
|
-
"rebase-apply"
|
|
11726
|
-
];
|
|
11727
|
-
var MSG_REPAIRED = "Your project's version history is working again. Your files were not changed.";
|
|
11728
|
-
var MSG_RETRY = "Another program may be using this project's history right now — try again in a moment.";
|
|
11729
|
-
var MSG_FAILED = "Your project's history couldn't be repaired automatically, but your files are safe and untouched.";
|
|
11730
|
-
function stamp() {
|
|
11731
|
-
return new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
11732
|
-
}
|
|
11733
|
-
async function indexReadable(dir) {
|
|
11734
|
-
try {
|
|
11735
|
-
await git6.listFiles({ fs: fs7, dir });
|
|
11736
|
-
return true;
|
|
11737
|
-
} catch {
|
|
11738
|
-
return false;
|
|
11739
|
-
}
|
|
11740
|
-
}
|
|
11741
|
-
async function rebuildIndexFromHead(dir) {
|
|
11742
|
-
await rm6(path24.join(gitDirFor(dir), "index"), { force: true });
|
|
11743
|
-
const cache2 = {};
|
|
11744
|
-
let files;
|
|
11745
|
-
try {
|
|
11746
|
-
files = await git6.listFiles({ fs: fs7, dir, ref: "HEAD" });
|
|
11747
|
-
} catch {
|
|
11748
|
-
return;
|
|
11749
|
-
}
|
|
11750
|
-
for (const filepath of files) {
|
|
11751
|
-
await git6.resetIndex({ fs: fs7, dir, cache: cache2, filepath, ref: "HEAD" });
|
|
11752
|
-
}
|
|
11753
|
-
}
|
|
11754
|
-
function readDamagedBranchTips(damagedGitDir) {
|
|
11755
|
-
const tips = new Map;
|
|
11756
|
-
const OID = /^[0-9a-f]{40}$/;
|
|
11757
|
-
try {
|
|
11758
|
-
const packed = fs7.readFileSync(path24.join(damagedGitDir, "packed-refs"), "utf8");
|
|
11759
|
-
for (const line of packed.split(`
|
|
11760
|
-
`)) {
|
|
11761
|
-
const m = /^([0-9a-f]{40}) refs\/heads\/(.+)$/.exec(line.trim());
|
|
11762
|
-
if (m)
|
|
11763
|
-
tips.set(m[2], m[1]);
|
|
11764
|
-
}
|
|
11765
|
-
} catch {}
|
|
11766
|
-
const headsDir = path24.join(damagedGitDir, "refs", "heads");
|
|
11767
|
-
const walk = (dir, prefix) => {
|
|
11768
|
-
let entries;
|
|
11769
|
-
try {
|
|
11770
|
-
entries = fs7.readdirSync(dir, { withFileTypes: true });
|
|
11771
|
-
} catch {
|
|
11772
|
-
return;
|
|
11773
|
-
}
|
|
11774
|
-
for (const e of entries) {
|
|
11775
|
-
const name = prefix ? `${prefix}/${e.name}` : e.name;
|
|
11776
|
-
if (e.isDirectory())
|
|
11777
|
-
walk(path24.join(dir, e.name), name);
|
|
11778
|
-
else if (e.isFile()) {
|
|
11779
|
-
try {
|
|
11780
|
-
const oid = fs7.readFileSync(path24.join(dir, e.name), "utf8").trim();
|
|
11781
|
-
if (OID.test(oid))
|
|
11782
|
-
tips.set(name, oid);
|
|
11783
|
-
} catch {}
|
|
11784
|
-
}
|
|
11785
|
-
}
|
|
11786
|
-
};
|
|
11787
|
-
walk(headsDir, "");
|
|
11788
|
-
return [...tips].map(([name, oid]) => ({ name, oid }));
|
|
11789
|
-
}
|
|
11790
|
-
async function looseCommitTips(dir, cache2) {
|
|
11791
|
-
const objectsDir = path24.join(gitDirFor(dir), "objects");
|
|
11792
|
-
const oids = [];
|
|
11793
|
-
let fanouts;
|
|
11794
|
-
try {
|
|
11795
|
-
fanouts = fs7.readdirSync(objectsDir).filter((e) => /^[0-9a-f]{2}$/.test(e));
|
|
11796
|
-
} catch {
|
|
11797
|
-
return [];
|
|
11798
|
-
}
|
|
11799
|
-
for (const fan of fanouts) {
|
|
11800
|
-
let files;
|
|
11801
|
-
try {
|
|
11802
|
-
files = fs7.readdirSync(path24.join(objectsDir, fan));
|
|
11803
|
-
} catch {
|
|
11804
|
-
continue;
|
|
11805
|
-
}
|
|
11806
|
-
for (const f of files) {
|
|
11807
|
-
if (/^[0-9a-f]{38}$/.test(f))
|
|
11808
|
-
oids.push(fan + f);
|
|
11809
|
-
}
|
|
11810
|
-
}
|
|
11811
|
-
const commits = new Map;
|
|
11812
|
-
for (const oid of oids) {
|
|
11813
|
-
try {
|
|
11814
|
-
const obj = await git6.readObject({ fs: fs7, dir, cache: cache2, oid, format: "parsed" });
|
|
11815
|
-
if (obj.type === "commit") {
|
|
11816
|
-
commits.set(oid, obj.object.parent ?? []);
|
|
11817
|
-
}
|
|
11818
|
-
} catch {}
|
|
11819
|
-
}
|
|
11820
|
-
const isParent = new Set;
|
|
11821
|
-
for (const parents of commits.values())
|
|
11822
|
-
for (const p of parents)
|
|
11823
|
-
isParent.add(p);
|
|
11824
|
-
return [...commits.keys()].filter((oid) => !isParent.has(oid));
|
|
11825
|
-
}
|
|
11826
|
-
async function targetBranch(dir) {
|
|
11827
|
-
try {
|
|
11828
|
-
const branches = await git6.listBranches({ fs: fs7, dir });
|
|
11829
|
-
if (branches.includes("main"))
|
|
11830
|
-
return "main";
|
|
11831
|
-
if (branches.includes("master"))
|
|
11832
|
-
return "master";
|
|
11833
|
-
if (branches.length > 0)
|
|
11834
|
-
return branches[0];
|
|
11835
|
-
} catch {}
|
|
11836
|
-
return "main";
|
|
11837
|
-
}
|
|
11838
|
-
async function repairRepo(options) {
|
|
11839
|
-
const dir = await repoDirFor(options.projectDir);
|
|
11840
|
-
return withRepoLock(dir, () => repairLocked(dir, options));
|
|
11841
|
-
}
|
|
11842
|
-
async function repairLocked(dir, options) {
|
|
11843
|
-
const logger = resolveLogger(options.logFile, "repair");
|
|
11844
|
-
const gitDir = gitDirFor(dir);
|
|
11845
|
-
const actions = [];
|
|
11846
|
-
const snapshotFiles = async (message) => {
|
|
11847
|
-
try {
|
|
11848
|
-
if (await hasPendingChanges(dir)) {
|
|
11849
|
-
await snapshotWorkingTreeUnlocked({
|
|
11850
|
-
projectDir: dir,
|
|
11851
|
-
repoRoot: dir,
|
|
11852
|
-
message,
|
|
11853
|
-
authorName: options.authorName,
|
|
11854
|
-
authorEmail: options.authorEmail
|
|
11855
|
-
});
|
|
11856
|
-
}
|
|
11857
|
-
} catch (e) {
|
|
11858
|
-
if (!isNoChangesError(e))
|
|
11859
|
-
throw e;
|
|
11860
|
-
}
|
|
11861
|
-
};
|
|
11862
|
-
try {
|
|
11863
|
-
if (fs7.existsSync(gitDir)) {
|
|
11864
|
-
const sweep = await sweepStaleLocks(gitDir);
|
|
11865
|
-
if (sweep === "fresh") {
|
|
11866
|
-
logger.info("repair", "fresh lock present — deferring");
|
|
11867
|
-
return { status: "retry_later", message: MSG_RETRY, actions, retryAfterMs: 30000 };
|
|
11868
|
-
}
|
|
11869
|
-
if (sweep === "swept") {
|
|
11870
|
-
actions.push("Removed leftover lock files from an interrupted operation.");
|
|
11871
|
-
logger.info("repair", "swept stale locks");
|
|
11872
|
-
}
|
|
11873
|
-
}
|
|
11874
|
-
if (fs7.existsSync(gitDir)) {
|
|
11875
|
-
let removedState = false;
|
|
11876
|
-
for (const name of INTERRUPTED_STATE) {
|
|
11877
|
-
const p = path24.join(gitDir, name);
|
|
11878
|
-
if (fs7.existsSync(p)) {
|
|
11879
|
-
await rm6(p, { recursive: true, force: true });
|
|
11880
|
-
removedState = true;
|
|
11881
|
-
}
|
|
11882
|
-
}
|
|
11883
|
-
if (removedState) {
|
|
11884
|
-
actions.push("Cleared an unfinished operation another program left behind.");
|
|
11885
|
-
logger.info("repair", "removed interrupted-operation state");
|
|
11886
|
-
await rebuildIndexFromHead(dir);
|
|
11887
|
-
} else if (!await indexReadable(dir)) {
|
|
11888
|
-
await rebuildIndexFromHead(dir);
|
|
11889
|
-
actions.push("Rebuilt the project's tracking information.");
|
|
11890
|
-
logger.info("repair", "rebuilt corrupt index");
|
|
11891
|
-
}
|
|
11892
|
-
}
|
|
11893
|
-
let health = await inspectRepo({ repoDir: dir }, { checkLocalChanges: false });
|
|
11894
|
-
if (health.hasGitDir && !health.headUnreadable && health.isDetachedHead) {
|
|
11895
|
-
await snapshotFiles("Saved your latest changes");
|
|
11896
|
-
const headOid = await git6.resolveRef({ fs: fs7, dir, ref: "HEAD" });
|
|
11897
|
-
const rescueRef = `refs/heads/recovered-${stamp()}`;
|
|
11898
|
-
await git6.writeRef({ fs: fs7, dir, ref: rescueRef, value: headOid, force: true });
|
|
11899
|
-
const branch = await targetBranch(dir);
|
|
11900
|
-
const branchExists = (await git6.listBranches({ fs: fs7, dir })).includes(branch);
|
|
11901
|
-
if (!branchExists) {
|
|
11902
|
-
await git6.writeRef({ fs: fs7, dir, ref: `refs/heads/${branch}`, value: headOid, force: true });
|
|
11903
|
-
}
|
|
11904
|
-
await git6.writeRef({
|
|
11905
|
-
fs: fs7,
|
|
11906
|
-
dir,
|
|
11907
|
-
ref: "HEAD",
|
|
11908
|
-
value: `refs/heads/${branch}`,
|
|
11909
|
-
symbolic: true,
|
|
11910
|
-
force: true
|
|
11911
|
-
});
|
|
11912
|
-
const cache2 = {};
|
|
11913
|
-
await git6.checkout({ fs: fs7, dir, cache: cache2, ref: branch, force: true });
|
|
11914
|
-
const branchTip = await git6.resolveRef({ fs: fs7, dir, ref: branch });
|
|
11915
|
-
if (branchTip !== headOid) {
|
|
11916
|
-
await convergeMerge({
|
|
11917
|
-
dir,
|
|
11918
|
-
cache: cache2,
|
|
11919
|
-
branch,
|
|
11920
|
-
theirs: headOid,
|
|
11921
|
-
author: await resolveGitAuthor(dir, options.authorName, options.authorEmail),
|
|
11922
|
-
authorName: options.authorName,
|
|
11923
|
-
authorEmail: options.authorEmail,
|
|
11924
|
-
allowUnrelatedHistories: true
|
|
11925
|
-
});
|
|
11926
|
-
}
|
|
11927
|
-
actions.push("Reconnected your work to the project's version line.");
|
|
11928
|
-
logger.info("repair", "reattached detached HEAD", { rescueRef });
|
|
11929
|
-
}
|
|
11930
|
-
health = await inspectRepo({ repoDir: dir }, { checkLocalChanges: false });
|
|
11931
|
-
if (classifyFromHealth(health, { minLockAgeMs: 0 }) === null) {
|
|
11932
|
-
await snapshotFiles("Saved your latest changes");
|
|
11933
|
-
logger.info("repair", "repaired in place", { actions: actions.length });
|
|
11934
|
-
return { status: "repaired", message: MSG_REPAIRED, actions };
|
|
11935
|
-
}
|
|
11936
|
-
return await recloneWithSalvage(dir, options, actions, logger, snapshotFiles);
|
|
11937
|
-
} catch (e) {
|
|
11938
|
-
logger.error("repair", "repair failed", { error: e instanceof Error ? e.message : String(e) });
|
|
11939
|
-
return { status: "failed", message: MSG_FAILED, actions };
|
|
11940
|
-
}
|
|
11941
|
-
}
|
|
11942
|
-
async function recloneWithSalvage(dir, options, actions, logger, snapshotFiles) {
|
|
11943
|
-
const gitDir = gitDirFor(dir);
|
|
11944
|
-
let remoteUrl;
|
|
11945
|
-
let credential;
|
|
11946
|
-
try {
|
|
11947
|
-
const transport = await resolveTransport(dir, {
|
|
11948
|
-
credential: options.credential,
|
|
11949
|
-
tokenStore: options.tokenStore
|
|
11950
|
-
});
|
|
11951
|
-
remoteUrl = transport.url;
|
|
11952
|
-
credential = transport.credential;
|
|
11953
|
-
} catch {
|
|
11954
|
-
remoteUrl = undefined;
|
|
11955
|
-
}
|
|
11956
|
-
let freshGitDir;
|
|
11957
|
-
let tempDir;
|
|
11958
|
-
if (remoteUrl) {
|
|
11959
|
-
tempDir = await mkdtemp3(path24.join(os3.tmpdir(), "gutterpress-repair-"));
|
|
11960
|
-
const cloneDir = path24.join(tempDir, "clone");
|
|
11961
|
-
try {
|
|
11962
|
-
await cloneRepository({
|
|
11963
|
-
url: remoteUrl,
|
|
11964
|
-
dir: cloneDir,
|
|
11965
|
-
...credential ? { credential } : {}
|
|
11966
|
-
});
|
|
11967
|
-
} catch (e) {
|
|
11968
|
-
await rm6(tempDir, { recursive: true, force: true });
|
|
11969
|
-
logger.warn("repair", "re-clone failed — deferring", {
|
|
11970
|
-
error: e instanceof Error ? e.message : String(e)
|
|
11971
|
-
});
|
|
11972
|
-
return {
|
|
11973
|
-
status: "retry_later",
|
|
11974
|
-
message: "The online copy couldn't be reached to finish the repair. Your files are safe — it will be tried again.",
|
|
11975
|
-
actions,
|
|
11976
|
-
retryAfterMs: 60000
|
|
11977
|
-
};
|
|
11978
|
-
}
|
|
11979
|
-
freshGitDir = path24.join(cloneDir, ".git");
|
|
11980
|
-
} else {
|
|
11981
|
-
tempDir = await mkdtemp3(path24.join(os3.tmpdir(), "gutterpress-repair-"));
|
|
11982
|
-
const initDir = path24.join(tempDir, "init");
|
|
11983
|
-
fs7.mkdirSync(initDir, { recursive: true });
|
|
11984
|
-
await git6.init({ fs: fs7, dir: initDir, defaultBranch: "main" });
|
|
11985
|
-
freshGitDir = path24.join(initDir, ".git");
|
|
11986
|
-
}
|
|
11987
|
-
let damagedBackup;
|
|
11988
|
-
if (fs7.existsSync(gitDir)) {
|
|
11989
|
-
damagedBackup = path24.join(dir, `.git-damaged-${stamp()}`);
|
|
11990
|
-
await rename3(gitDir, damagedBackup);
|
|
11991
|
-
actions.push("Kept the old history folder on disk as a backup.");
|
|
11992
|
-
}
|
|
11993
|
-
await rename3(freshGitDir, gitDir);
|
|
11994
|
-
if (tempDir)
|
|
11995
|
-
await rm6(tempDir, { recursive: true, force: true });
|
|
11996
|
-
actions.push(remoteUrl ? "Rebuilt the project's history from the online copy." : "Started a fresh history for the project (no online copy is connected).");
|
|
11997
|
-
logger.info("repair", "replaced .git", { fromRemote: Boolean(remoteUrl), damagedBackup });
|
|
11998
|
-
if (damagedBackup) {
|
|
11999
|
-
const from = path24.join(damagedBackup, "objects");
|
|
12000
|
-
if (fs7.existsSync(from)) {
|
|
12001
|
-
await cp3(from, path24.join(gitDir, "objects"), {
|
|
12002
|
-
recursive: true,
|
|
12003
|
-
force: false,
|
|
12004
|
-
errorOnExist: false
|
|
12005
|
-
}).catch(() => {});
|
|
12006
|
-
}
|
|
12007
|
-
const branch = await targetBranch(dir);
|
|
12008
|
-
let branchExists = (await git6.listBranches({ fs: fs7, dir })).includes(branch);
|
|
12009
|
-
const scanCache = {};
|
|
12010
|
-
const tips = new Map;
|
|
12011
|
-
for (const tip of readDamagedBranchTips(damagedBackup)) {
|
|
12012
|
-
tips.set(tip.oid, tip.name);
|
|
12013
|
-
}
|
|
12014
|
-
for (const oid of await looseCommitTips(dir, scanCache)) {
|
|
12015
|
-
if (!tips.has(oid))
|
|
12016
|
-
tips.set(oid, "recovered-work");
|
|
12017
|
-
}
|
|
12018
|
-
for (const [oid, name] of tips) {
|
|
12019
|
-
const cache2 = {};
|
|
12020
|
-
try {
|
|
12021
|
-
await git6.readCommit({ fs: fs7, dir, cache: cache2, oid });
|
|
12022
|
-
} catch {
|
|
12023
|
-
logger.warn("repair", "old tip unreadable — left in the backup", { tip: name });
|
|
12024
|
-
continue;
|
|
12025
|
-
}
|
|
12026
|
-
if (!branchExists) {
|
|
12027
|
-
await git6.writeRef({ fs: fs7, dir, ref: `refs/heads/${branch}`, value: oid, force: true });
|
|
12028
|
-
await git6.writeRef({
|
|
12029
|
-
fs: fs7,
|
|
12030
|
-
dir,
|
|
12031
|
-
ref: "HEAD",
|
|
12032
|
-
value: `refs/heads/${branch}`,
|
|
12033
|
-
symbolic: true,
|
|
12034
|
-
force: true
|
|
12035
|
-
});
|
|
12036
|
-
await rebuildIndexFromHead(dir);
|
|
12037
|
-
branchExists = true;
|
|
12038
|
-
actions.push("Restored your saved history from the backup.");
|
|
12039
|
-
continue;
|
|
12040
|
-
}
|
|
12041
|
-
try {
|
|
12042
|
-
const branchTip = await git6.resolveRef({ fs: fs7, dir, ref: branch });
|
|
12043
|
-
if (branchTip === oid)
|
|
12044
|
-
continue;
|
|
12045
|
-
await convergeMerge({
|
|
12046
|
-
dir,
|
|
12047
|
-
cache: cache2,
|
|
12048
|
-
branch,
|
|
12049
|
-
theirs: oid,
|
|
12050
|
-
author: await resolveGitAuthor(dir, options.authorName, options.authorEmail),
|
|
12051
|
-
authorName: options.authorName,
|
|
12052
|
-
authorEmail: options.authorEmail,
|
|
12053
|
-
allowUnrelatedHistories: true
|
|
12054
|
-
});
|
|
12055
|
-
actions.push("Brought your earlier saved versions back into the history.");
|
|
12056
|
-
} catch (e) {
|
|
12057
|
-
logger.warn("repair", "old tip could not be merged — pinned as a recovery branch", {
|
|
12058
|
-
tip: name,
|
|
12059
|
-
error: e instanceof Error ? e.message : String(e)
|
|
12060
|
-
});
|
|
12061
|
-
await git6.writeRef({
|
|
12062
|
-
fs: fs7,
|
|
12063
|
-
dir,
|
|
12064
|
-
ref: `refs/heads/recovered-${stamp()}-${name.replace(/[^A-Za-z0-9_-]/g, "_")}`,
|
|
12065
|
-
value: oid,
|
|
12066
|
-
force: true
|
|
12067
|
-
}).catch(() => {});
|
|
12068
|
-
}
|
|
12069
|
-
}
|
|
12070
|
-
}
|
|
12071
|
-
await snapshotFiles("Saved your files after repairing the project");
|
|
12072
|
-
return {
|
|
12073
|
-
status: "repaired",
|
|
12074
|
-
message: MSG_REPAIRED,
|
|
12075
|
-
actions,
|
|
12076
|
-
...damagedBackup ? { damagedGitBackupPath: damagedBackup } : {}
|
|
12077
|
-
};
|
|
12078
|
-
}
|
|
12079
|
-
// src/lib/theme-manager.ts
|
|
12080
|
-
import { isSeq as isSeq4, Scalar as Scalar2 } from "yaml";
|
|
12081
|
-
var THEMES_DIR = "themes";
|
|
12082
|
-
var THEME_HREF_RE = new RegExp(`^${THEMES_DIR}/([^/]+)/theme\\.css$`);
|
|
12083
|
-
// src/lib/theme-import.ts
|
|
12084
|
-
import { unzipSync as unzipSync2 } from "fflate";
|
|
12085
|
-
var MAX_THEME_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
12086
|
-
var MAX_THEME_UNZIPPED_BYTES = 25 * 1024 * 1024;
|
|
12087
|
-
var KNOWN_THEME_FILES = new Set(["theme.css", "theme.json"]);
|
|
12088
|
-
var ALLOWED_ASSET_EXTS = new Set([
|
|
12089
|
-
".woff",
|
|
12090
|
-
".woff2",
|
|
12091
|
-
".ttf",
|
|
12092
|
-
".otf",
|
|
12093
|
-
".eot",
|
|
12094
|
-
".png",
|
|
12095
|
-
".jpg",
|
|
12096
|
-
".jpeg",
|
|
12097
|
-
".gif",
|
|
12098
|
-
".svg",
|
|
12099
|
-
".webp",
|
|
12100
|
-
".avif",
|
|
12101
|
-
".css"
|
|
12102
|
-
]);
|
|
12103
|
-
// src/lib/manifest-config.ts
|
|
12104
|
-
import { isSeq as isSeq5, isMap as isMap3, YAMLSeq as YAMLSeq3, Scalar as Scalar3 } from "yaml";
|
|
12105
|
-
// src/lib/host-policy.ts
|
|
12106
|
-
var AUTO_SNAPSHOT_MAX_MINUTES = 24 * 60;
|
|
12107
|
-
var AUTO_SYNC_MAX_MINUTES = 24 * 60;
|
|
12108
|
-
// src/lib/remote-auth/github-repos.ts
|
|
12109
|
-
var MANIFEST_NAMES = new Set(MANIFEST_FILENAMES);
|
|
11227
|
+
// src/lib/remote-auth/transport.ts
|
|
11228
|
+
import git2 from "isomorphic-git";
|
|
12110
11229
|
// src/lib/remote-auth/test-access.ts
|
|
12111
|
-
import
|
|
11230
|
+
import git4 from "isomorphic-git";
|
|
12112
11231
|
import httpNode2 from "isomorphic-git/http/node";
|
|
12113
11232
|
// src/lib/remote-auth/sync.ts
|
|
12114
|
-
import
|
|
12115
|
-
|
|
12116
|
-
|
|
11233
|
+
import git6 from "isomorphic-git";
|
|
11234
|
+
|
|
11235
|
+
// src/lib/remote-auth/converge-merge.ts
|
|
11236
|
+
import git5 from "isomorphic-git";
|
|
11237
|
+
import diff3Merge from "diff3";
|
|
12117
11238
|
// src/lib/publish/selections.ts
|
|
12118
|
-
import { chmod as chmod2, mkdir as
|
|
12119
|
-
import
|
|
11239
|
+
import { chmod as chmod2, mkdir as mkdir10, readFile as readFile25, writeFile as writeFile8 } from "node:fs/promises";
|
|
11240
|
+
import path20 from "node:path";
|
|
12120
11241
|
class PublishSelectionsStore {
|
|
12121
11242
|
filePath;
|
|
12122
11243
|
queue = Promise.resolve();
|
|
12123
11244
|
constructor(filePath) {
|
|
12124
|
-
this.filePath = filePath ??
|
|
11245
|
+
this.filePath = filePath ?? path20.join(defaultConfigDir(), "publish-selections.json");
|
|
12125
11246
|
}
|
|
12126
11247
|
async read() {
|
|
12127
11248
|
try {
|
|
12128
|
-
const raw = await
|
|
11249
|
+
const raw = await readFile25(this.filePath, "utf8");
|
|
12129
11250
|
const parsed = JSON.parse(raw);
|
|
12130
11251
|
if (parsed && typeof parsed === "object") {
|
|
12131
11252
|
return {
|
|
@@ -12138,8 +11259,8 @@ class PublishSelectionsStore {
|
|
|
12138
11259
|
return { version: 1, global: {}, projects: {} };
|
|
12139
11260
|
}
|
|
12140
11261
|
async write(data) {
|
|
12141
|
-
await
|
|
12142
|
-
await
|
|
11262
|
+
await mkdir10(path20.dirname(this.filePath), { recursive: true });
|
|
11263
|
+
await writeFile8(this.filePath, JSON.stringify(data, null, 2), {
|
|
12143
11264
|
encoding: "utf8",
|
|
12144
11265
|
mode: 384
|
|
12145
11266
|
});
|
|
@@ -12204,4 +11325,4 @@ function normalizeProjectKey(projectDir) {
|
|
|
12204
11325
|
}
|
|
12205
11326
|
// src/lib/publish/manifest-publish.ts
|
|
12206
11327
|
import { isMap as isMap4 } from "yaml";
|
|
12207
|
-
export { PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, parseNpmPluginSpec, runLint, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, BUILT_IN_TEMPLATE_IDS, addNpmPlugin,
|
|
11328
|
+
export { PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, MANIFEST_FILENAMES, parseNpmPluginSpec, runLint, reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, BUILT_IN_TEMPLATE_IDS, addNpmPlugin, publishCredentialKey, publishConnectionStatus, listPublishAccounts, listPublishProviders, publishProviderFor, runPublish, connectPublishProvider };
|