gutterpress 0.11.18 → 0.11.19-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.js +2 -2
- package/dist/{build-67bj810g.js → build-wbfzx37g.js} +6 -6
- package/dist/{cdp-smdx6mr3.js → cdp-1hn3xsk2.js} +2 -2
- package/dist/{cdp-jg95s674.js → cdp-h0wa42cz.js} +1 -1
- package/dist/checks/source/character-table.d.ts +63 -0
- package/dist/checks/source/characters.d.ts +1 -0
- package/dist/checks/source/index.d.ts +1 -0
- package/dist/{cli-6q7trt7z.js → cli-55jd2t8p.js} +341 -163
- package/dist/{cli-7fkhgtfn.js → cli-asap4t1z.js} +4 -3
- package/dist/{cli-kkzka31p.js → cli-eyyggect.js} +1 -1
- package/dist/{cli-xe29z84b.js → cli-r7czdzyz.js} +74 -5
- package/dist/{cli-rg8jq04a.js → cli-xf9b7ptc.js} +1 -1
- package/dist/{cli-ptez7zcc.js → cli-xx94a1k6.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-gcqb6xtj.js → doctor-mkhpss2t.js} +3 -3
- package/dist/{engine-wtnakejg.js → engine-fe0n7v9p.js} +3 -3
- package/dist/{engine-zy6fzs5a.js → engine-zehw6tkv.js} +2 -2
- package/dist/{ext-pkaghr3f.js → ext-vw89b9sm.js} +6 -6
- package/dist/{index-n8vs6pss.js → index-b866g2ph.js} +1 -1
- package/dist/{index-rzm1phg0.js → index-bt0qws5c.js} +4 -3
- package/dist/{index-e1hbmb5a.js → index-eqxs5118.js} +412 -167
- package/dist/index.js +3 -3
- package/dist/lib/exec.d.ts +2 -1
- package/dist/lib/ghostscript.d.ts +20 -0
- package/dist/{new-rnnavkc8.js → new-3g19kf7m.js} +6 -6
- package/dist/{preflight-4769yg8n.js → preflight-62r2cnyx.js} +6 -6
- package/dist/{preview-f9csmejj.js → preview-sjq8afwc.js} +6 -6
- package/dist/{publish-40yvnb62.js → publish-fq6gmn4e.js} +6 -6
- package/dist/{validate-pt921n11.js → validate-2zsqvbtk.js} +6 -6
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
debug,
|
|
6
6
|
defaultConfigDir,
|
|
7
7
|
error,
|
|
8
|
+
findPostScriptShadingPages,
|
|
8
9
|
hasLiveTransparency,
|
|
9
10
|
info,
|
|
10
11
|
log,
|
|
@@ -13,12 +14,12 @@ import {
|
|
|
13
14
|
stampCreator,
|
|
14
15
|
stripAnnotations,
|
|
15
16
|
warn
|
|
16
|
-
} from "./cli-
|
|
17
|
+
} from "./cli-r7czdzyz.js";
|
|
17
18
|
import {
|
|
18
19
|
DEBOUNCE,
|
|
19
20
|
UsageError,
|
|
20
21
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
22
|
+
} from "./cli-xf9b7ptc.js";
|
|
22
23
|
import {
|
|
23
24
|
blackTextToGray
|
|
24
25
|
} from "./cli-ht1bcqdd.js";
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
getAssetPath,
|
|
28
29
|
getAssetsDir,
|
|
29
30
|
lastPageMode
|
|
30
|
-
} from "./cli-
|
|
31
|
+
} from "./cli-xx94a1k6.js";
|
|
31
32
|
import {
|
|
32
33
|
BuildError,
|
|
33
34
|
EXIT_CODES
|
|
@@ -39,7 +40,7 @@ import {
|
|
|
39
40
|
requireChromiumExecutable,
|
|
40
41
|
resolveChromiumExecutable,
|
|
41
42
|
spawnCapture
|
|
42
|
-
} from "./cli-
|
|
43
|
+
} from "./cli-asap4t1z.js";
|
|
43
44
|
import {
|
|
44
45
|
PLUGINS_DIR,
|
|
45
46
|
VENDORED_NPM_DIR,
|
|
@@ -6289,6 +6290,9 @@ function isDroppedPageGradient(decl) {
|
|
|
6289
6290
|
return false;
|
|
6290
6291
|
return isInPageBox(decl);
|
|
6291
6292
|
}
|
|
6293
|
+
function isPostScriptShadingGradient(decl) {
|
|
6294
|
+
return /(repeating-(linear|radial|conic)|conic)-gradient\(/i.test(decl.value);
|
|
6295
|
+
}
|
|
6292
6296
|
function isInPageMarginBox(decl) {
|
|
6293
6297
|
const box = decl.parent;
|
|
6294
6298
|
if (!box || box.type !== "atrule")
|
|
@@ -6537,6 +6541,13 @@ function checkCssRaw(css, from) {
|
|
|
6537
6541
|
message: `"${decl.prop}: ${decl.value}" on @page paints nothing in print — Chromium drops a gradient in the @page box's own background, with no error and a valid-looking PDF. A solid colour there paints the whole sheet; the same gradient also works on "html" (it covers the content area, not the sheet) or in a margin box.`,
|
|
6538
6542
|
...nodeLoc(decl)
|
|
6539
6543
|
});
|
|
6544
|
+
} else if (isPostScriptShadingGradient(decl)) {
|
|
6545
|
+
warnings.push({
|
|
6546
|
+
rule: ruleRiskyProps,
|
|
6547
|
+
severity: "warning",
|
|
6548
|
+
message: `"${decl.prop}: ${decl.value}" prints blank in PDF/X exports (--format pdfx): Chromium emits repeating-*-gradient() and conic-gradient() in a form Ghostscript cannot convert. Plain PDF output is unaffected. Use linear-gradient() or radial-gradient() instead; for 0deg/90deg stripes write "linear-gradient(90deg, #b33 0 6px, #c8c5bf 6px 10px)" with "background-size: 10px 100%", and for anything else a small tiled image.`,
|
|
6549
|
+
...nodeLoc(decl)
|
|
6550
|
+
});
|
|
6540
6551
|
} else if (isUnpairedFitContentWidth(decl)) {
|
|
6541
6552
|
warnings.push({
|
|
6542
6553
|
rule: ruleRiskyProps,
|
|
@@ -7997,11 +8008,174 @@ var check27 = {
|
|
|
7997
8008
|
};
|
|
7998
8009
|
registerCheck(check27);
|
|
7999
8010
|
|
|
8011
|
+
// src/checks/source/characters.ts
|
|
8012
|
+
import { readFile as readFile20 } from "node:fs/promises";
|
|
8013
|
+
|
|
8014
|
+
// src/checks/source/character-table.ts
|
|
8015
|
+
var CHARACTER_PATTERN = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u00A0\\u00AD\\u00B4\\u02BC" + "\\u061C\\u2000-\\u2015\\u2018\\u2019\\u201B-\\u201D\\u201F\\u2022\\u2026" + "\\u2028-\\u202F\\u2032\\u2033\\u205F\\u2060\\u2066-\\u2069\\u2190\\u2192" + "\\u2194\\u21D2\\u2212\\u25AA\\u25E6\\u2605\\u2713\\u2714\\u2717\\u3000" + "\\uE000-\\uF8FF\\uFEFF\\uFF02\\uFF07\\uFFFD]" + "|\\p{Emoji_Presentation}|\\p{Extended_Pictographic}(?=\\uFE0F)", "gu");
|
|
8016
|
+
var NAMED = {
|
|
8017
|
+
8212: ["Em dash", "typographer", "---"],
|
|
8018
|
+
8211: ["En dash", "typographer", "--"],
|
|
8019
|
+
8220: ["Opening curly quote", "typographer", '"'],
|
|
8020
|
+
8221: ["Closing curly quote", "typographer", '"'],
|
|
8021
|
+
8216: ["Opening curly single quote", "typographer", "'"],
|
|
8022
|
+
8217: ["Curly apostrophe", "typographer", "'"],
|
|
8023
|
+
8230: ["Ellipsis character", "typographer", "..."],
|
|
8024
|
+
700: ["Modifier-letter apostrophe", "lookalike", "'"],
|
|
8025
|
+
180: ["Acute accent", "lookalike", "'"],
|
|
8026
|
+
8242: ["Prime", "lookalike", "'"],
|
|
8027
|
+
8243: ["Double prime", "lookalike", '"'],
|
|
8028
|
+
8219: ["Reversed single quote", "lookalike", "'"],
|
|
8029
|
+
8223: ["Reversed double quote", "lookalike", '"'],
|
|
8030
|
+
65282: ["Full-width quote", "lookalike", '"'],
|
|
8031
|
+
65287: ["Full-width apostrophe", "lookalike", "'"],
|
|
8032
|
+
8208: ["Unicode hyphen", "lookalike", "-"],
|
|
8033
|
+
8209: ["Non-breaking hyphen", "lookalike", "-"],
|
|
8034
|
+
8210: ["Figure dash", "lookalike", "-"],
|
|
8035
|
+
8213: ["Horizontal bar", "lookalike", "---"],
|
|
8036
|
+
8722: ["Minus sign", "lookalike", "-"],
|
|
8037
|
+
8226: ["Bullet", "symbol", "•"],
|
|
8038
|
+
9702: ["White bullet", "symbol", "◦"],
|
|
8039
|
+
9642: ["Small square", "symbol", "▪"],
|
|
8040
|
+
8594: ["Right arrow", "symbol", "→"],
|
|
8041
|
+
8592: ["Left arrow", "symbol", "←"],
|
|
8042
|
+
8596: ["Left-right arrow", "symbol", "↔"],
|
|
8043
|
+
8658: ["Double right arrow", "symbol", "⇒"],
|
|
8044
|
+
10003: ["Check mark", "symbol", "✓"],
|
|
8045
|
+
10004: ["Heavy check mark", "symbol", "✔"],
|
|
8046
|
+
10007: ["Ballot X", "symbol", "✗"],
|
|
8047
|
+
9733: ["Star", "symbol", "★"],
|
|
8048
|
+
8203: ["Zero-width space", "invisible", "​"],
|
|
8049
|
+
8288: ["Word joiner", "invisible", "⁠"],
|
|
8050
|
+
65279: ["Byte-order mark", "invisible"],
|
|
8051
|
+
173: ["Soft hyphen", "invisible", "­"],
|
|
8052
|
+
8204: ["Zero-width non-joiner", "invisible", "‌"],
|
|
8053
|
+
8205: ["Zero-width joiner", "invisible", "‍"],
|
|
8054
|
+
8206: ["Left-to-right mark", "invisible", "‎"],
|
|
8055
|
+
8207: ["Right-to-left mark", "invisible", "‏"],
|
|
8056
|
+
1564: ["Arabic letter mark", "invisible", "؜"],
|
|
8057
|
+
8234: ["Left-to-right embedding", "invisible", "‪"],
|
|
8058
|
+
8235: ["Right-to-left embedding", "invisible", "‫"],
|
|
8059
|
+
8236: ["Pop directional formatting", "invisible", "‬"],
|
|
8060
|
+
8237: ["Left-to-right override", "invisible", "‭"],
|
|
8061
|
+
8238: ["Right-to-left override", "invisible", "‮"],
|
|
8062
|
+
8294: ["Left-to-right isolate", "invisible", "⁦"],
|
|
8063
|
+
8295: ["Right-to-left isolate", "invisible", "⁧"],
|
|
8064
|
+
8296: ["First-strong isolate", "invisible", "⁨"],
|
|
8065
|
+
8297: ["Pop directional isolate", "invisible", "⁩"],
|
|
8066
|
+
8232: ["Line separator", "invisible"],
|
|
8067
|
+
8233: ["Paragraph separator", "invisible"],
|
|
8068
|
+
160: ["Non-breaking space", "whitespace", " "],
|
|
8069
|
+
8192: ["En quad", "whitespace", " "],
|
|
8070
|
+
8193: ["Em quad", "whitespace", " "],
|
|
8071
|
+
8194: ["En space", "whitespace", " "],
|
|
8072
|
+
8195: ["Em space", "whitespace", " "],
|
|
8073
|
+
8196: ["Three-per-em space", "whitespace", " "],
|
|
8074
|
+
8197: ["Four-per-em space", "whitespace", " "],
|
|
8075
|
+
8198: ["Six-per-em space", "whitespace", " "],
|
|
8076
|
+
8199: ["Figure space", "whitespace", " "],
|
|
8077
|
+
8200: ["Punctuation space", "whitespace", " "],
|
|
8078
|
+
8201: ["Thin space", "whitespace", " "],
|
|
8079
|
+
8202: ["Hair space", "whitespace", " "],
|
|
8080
|
+
8239: ["Narrow non-breaking space", "whitespace", " "],
|
|
8081
|
+
8287: ["Medium mathematical space", "whitespace", " "],
|
|
8082
|
+
12288: ["Ideographic space", "whitespace", " "],
|
|
8083
|
+
65533: ["Replacement character", "replacement"]
|
|
8084
|
+
};
|
|
8085
|
+
function fixFor(code, spelling) {
|
|
8086
|
+
switch (code) {
|
|
8087
|
+
case "typographer":
|
|
8088
|
+
return `Type ${spelling} instead; it prints the same.`;
|
|
8089
|
+
case "lookalike":
|
|
8090
|
+
return `Type ${spelling} instead.`;
|
|
8091
|
+
case "whitespace":
|
|
8092
|
+
return `Type a space, or ${spelling} if you meant it.`;
|
|
8093
|
+
case "symbol":
|
|
8094
|
+
case "invisible":
|
|
8095
|
+
return spelling ? `Delete it, or type ${spelling} if you meant it.` : "Delete it.";
|
|
8096
|
+
case "control":
|
|
8097
|
+
return "Delete it; it does not print.";
|
|
8098
|
+
case "replacement":
|
|
8099
|
+
return "Retype the character that was lost here.";
|
|
8100
|
+
case "private-use":
|
|
8101
|
+
return "Replace it with the character you meant; this one only shows in the font it came from.";
|
|
8102
|
+
}
|
|
8103
|
+
}
|
|
8104
|
+
function info2(name, code, spelling) {
|
|
8105
|
+
return { name, code, fix: fixFor(code, spelling) };
|
|
8106
|
+
}
|
|
8107
|
+
function describeCharacter(char) {
|
|
8108
|
+
const cp2 = char.codePointAt(0);
|
|
8109
|
+
const named = NAMED[cp2];
|
|
8110
|
+
if (named)
|
|
8111
|
+
return info2(...named);
|
|
8112
|
+
if (cp2 <= 159)
|
|
8113
|
+
return info2("Control character", "control");
|
|
8114
|
+
if (cp2 >= 57344 && cp2 <= 63743)
|
|
8115
|
+
return info2("Private-use character", "private-use");
|
|
8116
|
+
return info2("Emoji", "symbol", `&#x${cp2.toString(16).toUpperCase()};`);
|
|
8117
|
+
}
|
|
8118
|
+
function codePointLabel(char) {
|
|
8119
|
+
return `U+${char.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
|
|
8120
|
+
}
|
|
8121
|
+
|
|
8122
|
+
// src/checks/source/characters.ts
|
|
8123
|
+
var PROBE = new RegExp(CHARACTER_PATTERN.source, "u");
|
|
8124
|
+
var check28 = {
|
|
8125
|
+
id: "source.characters",
|
|
8126
|
+
name: "Unusual Characters",
|
|
8127
|
+
description: "Finds invisible and control characters, odd spaces, and pasted typography (em dashes, curly quotes, emoji) in markdown",
|
|
8128
|
+
category: "source",
|
|
8129
|
+
phase: "pre-build",
|
|
8130
|
+
async run(ctx) {
|
|
8131
|
+
const results = [];
|
|
8132
|
+
for (const file of (ctx.markdownFiles ?? []).slice().sort()) {
|
|
8133
|
+
let content;
|
|
8134
|
+
try {
|
|
8135
|
+
content = await readFile20(file, "utf8");
|
|
8136
|
+
} catch {
|
|
8137
|
+
results.push(inspectionFailed(check28.id, `Could not read source file: ${file}`, { file }));
|
|
8138
|
+
continue;
|
|
8139
|
+
}
|
|
8140
|
+
if (!PROBE.test(content))
|
|
8141
|
+
continue;
|
|
8142
|
+
const lines = content.split(`
|
|
8143
|
+
`);
|
|
8144
|
+
let silencedLine = -1;
|
|
8145
|
+
for (let i = 0;i < lines.length; i++) {
|
|
8146
|
+
const line = lines[i];
|
|
8147
|
+
const comment = /^\s*<!--(.*)-->\s*$/.exec(line);
|
|
8148
|
+
if (comment && parseDisableDirective(comment[1])?.includes(check28.id)) {
|
|
8149
|
+
silencedLine = i + 1;
|
|
8150
|
+
continue;
|
|
8151
|
+
}
|
|
8152
|
+
if (i === silencedLine)
|
|
8153
|
+
continue;
|
|
8154
|
+
for (const match of line.matchAll(CHARACTER_PATTERN)) {
|
|
8155
|
+
const char = match[0];
|
|
8156
|
+
const { name, code, fix } = describeCharacter(char);
|
|
8157
|
+
results.push(finding(check28.id, {
|
|
8158
|
+
severity: "warning",
|
|
8159
|
+
code,
|
|
8160
|
+
message: `${name} (${codePointLabel(char)}). ${fix}`,
|
|
8161
|
+
file,
|
|
8162
|
+
line: i + 1,
|
|
8163
|
+
column: match.index + 1,
|
|
8164
|
+
data: { codePoint: char.codePointAt(0) }
|
|
8165
|
+
}));
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
}
|
|
8169
|
+
return results;
|
|
8170
|
+
}
|
|
8171
|
+
};
|
|
8172
|
+
registerCheck(check28);
|
|
8173
|
+
|
|
8000
8174
|
// src/checks/asset/image-file-size.ts
|
|
8001
8175
|
import { stat as stat4 } from "node:fs/promises";
|
|
8002
8176
|
|
|
8003
8177
|
// src/lib/image-inspect.ts
|
|
8004
|
-
import { stat as stat3, readFile as
|
|
8178
|
+
import { stat as stat3, readFile as readFile21 } from "node:fs/promises";
|
|
8005
8179
|
var DEFAULT_DPI = 72;
|
|
8006
8180
|
var cache = new Map;
|
|
8007
8181
|
async function inspectImage(path10) {
|
|
@@ -8014,15 +8188,15 @@ async function inspectImage(path10) {
|
|
|
8014
8188
|
const hit = cache.get(path10);
|
|
8015
8189
|
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size)
|
|
8016
8190
|
return hit.info;
|
|
8017
|
-
let
|
|
8191
|
+
let info3 = null;
|
|
8018
8192
|
try {
|
|
8019
|
-
const buf = await
|
|
8020
|
-
|
|
8193
|
+
const buf = await readFile21(path10);
|
|
8194
|
+
info3 = parseImage(buf);
|
|
8021
8195
|
} catch {
|
|
8022
|
-
|
|
8196
|
+
info3 = null;
|
|
8023
8197
|
}
|
|
8024
|
-
cache.set(path10, { mtimeMs: st.mtimeMs, size: st.size, info:
|
|
8025
|
-
return
|
|
8198
|
+
cache.set(path10, { mtimeMs: st.mtimeMs, size: st.size, info: info3 });
|
|
8199
|
+
return info3;
|
|
8026
8200
|
}
|
|
8027
8201
|
function parseImage(b) {
|
|
8028
8202
|
if (b.length > 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) {
|
|
@@ -8221,7 +8395,7 @@ async function collectImageFiles(dirs, exts, ignore2 = ASSET_SCAN_IGNORE_GLOBS)
|
|
|
8221
8395
|
}
|
|
8222
8396
|
|
|
8223
8397
|
// src/checks/asset/image-file-size.ts
|
|
8224
|
-
var
|
|
8398
|
+
var check29 = {
|
|
8225
8399
|
id: "asset.image.file-size",
|
|
8226
8400
|
name: "Image File Size",
|
|
8227
8401
|
description: "Checks that image files do not exceed the maximum size limit",
|
|
@@ -8238,18 +8412,18 @@ var check28 = {
|
|
|
8238
8412
|
const results = [];
|
|
8239
8413
|
for (const file of files) {
|
|
8240
8414
|
try {
|
|
8241
|
-
const
|
|
8242
|
-
if (
|
|
8243
|
-
const sizeMb = (
|
|
8415
|
+
const info3 = await stat4(file);
|
|
8416
|
+
if (info3.size > maxSize) {
|
|
8417
|
+
const sizeMb = (info3.size / 1e6).toFixed(1);
|
|
8244
8418
|
const maxMb = (maxSize / 1e6).toFixed(1);
|
|
8245
|
-
results.push(finding(
|
|
8419
|
+
results.push(finding(check29.id, {
|
|
8246
8420
|
severity: "warning",
|
|
8247
8421
|
message: `Image file too large: ${sizeMb}MB (max ${maxMb}MB)`,
|
|
8248
8422
|
file
|
|
8249
8423
|
}));
|
|
8250
8424
|
}
|
|
8251
8425
|
} catch {
|
|
8252
|
-
results.push(inspectionFailed(
|
|
8426
|
+
results.push(inspectionFailed(check29.id, `Could not stat image file: ${file}`, {
|
|
8253
8427
|
file
|
|
8254
8428
|
}));
|
|
8255
8429
|
}
|
|
@@ -8257,10 +8431,10 @@ var check28 = {
|
|
|
8257
8431
|
return results;
|
|
8258
8432
|
}
|
|
8259
8433
|
};
|
|
8260
|
-
registerCheck(
|
|
8434
|
+
registerCheck(check29);
|
|
8261
8435
|
|
|
8262
8436
|
// src/checks/asset/image-resolution.ts
|
|
8263
|
-
var
|
|
8437
|
+
var check30 = {
|
|
8264
8438
|
id: "asset.image.resolution",
|
|
8265
8439
|
name: "Image Resolution",
|
|
8266
8440
|
description: "Checks source image DPI from embedded density metadata",
|
|
@@ -8276,13 +8450,13 @@ var check29 = {
|
|
|
8276
8450
|
return [];
|
|
8277
8451
|
const results = [];
|
|
8278
8452
|
for (const file of files) {
|
|
8279
|
-
const
|
|
8280
|
-
if (!
|
|
8453
|
+
const info3 = await inspectImage(file);
|
|
8454
|
+
if (!info3)
|
|
8281
8455
|
continue;
|
|
8282
|
-
const { xDpi, yDpi } =
|
|
8456
|
+
const { xDpi, yDpi } = info3;
|
|
8283
8457
|
if (xDpi > 0 && yDpi > 0 && (xDpi < minDpi || yDpi < minDpi)) {
|
|
8284
8458
|
results.push({
|
|
8285
|
-
checkId:
|
|
8459
|
+
checkId: check30.id,
|
|
8286
8460
|
severity: "warning",
|
|
8287
8461
|
message: `Image resolution too low: ${xDpi}x${yDpi} DPI (minimum ${minDpi} DPI)`,
|
|
8288
8462
|
file
|
|
@@ -8292,7 +8466,7 @@ var check29 = {
|
|
|
8292
8466
|
return results;
|
|
8293
8467
|
}
|
|
8294
8468
|
};
|
|
8295
|
-
registerCheck(
|
|
8469
|
+
registerCheck(check30);
|
|
8296
8470
|
|
|
8297
8471
|
// src/checks/asset/image-color-space.ts
|
|
8298
8472
|
var LABEL = {
|
|
@@ -8300,7 +8474,7 @@ var LABEL = {
|
|
|
8300
8474
|
gray: "Gray",
|
|
8301
8475
|
cmyk: "CMYK"
|
|
8302
8476
|
};
|
|
8303
|
-
var
|
|
8477
|
+
var check31 = {
|
|
8304
8478
|
id: "asset.image.color-space",
|
|
8305
8479
|
name: "Image Color Space",
|
|
8306
8480
|
description: "Validates image color spaces against allowed list",
|
|
@@ -8317,11 +8491,11 @@ var check30 = {
|
|
|
8317
8491
|
const allowedLower = new Set(allowed.map((s) => s.toLowerCase()));
|
|
8318
8492
|
const results = [];
|
|
8319
8493
|
for (const file of files) {
|
|
8320
|
-
const
|
|
8321
|
-
const cs =
|
|
8494
|
+
const info3 = await inspectImage(file);
|
|
8495
|
+
const cs = info3?.colorSpace;
|
|
8322
8496
|
if (cs && !allowedLower.has(cs)) {
|
|
8323
8497
|
results.push({
|
|
8324
|
-
checkId:
|
|
8498
|
+
checkId: check31.id,
|
|
8325
8499
|
severity: "warning",
|
|
8326
8500
|
message: `Image uses ${LABEL[cs] ?? cs} color space (allowed: ${allowed.join(", ")})`,
|
|
8327
8501
|
file
|
|
@@ -8331,10 +8505,10 @@ var check30 = {
|
|
|
8331
8505
|
return results;
|
|
8332
8506
|
}
|
|
8333
8507
|
};
|
|
8334
|
-
registerCheck(
|
|
8508
|
+
registerCheck(check31);
|
|
8335
8509
|
|
|
8336
8510
|
// src/checks/asset/image-alpha.ts
|
|
8337
|
-
var
|
|
8511
|
+
var check32 = {
|
|
8338
8512
|
id: "asset.image.alpha-channel",
|
|
8339
8513
|
name: "Image Alpha Channel",
|
|
8340
8514
|
description: "Checks for alpha channels in PNG/TIFF images",
|
|
@@ -8349,10 +8523,10 @@ var check31 = {
|
|
|
8349
8523
|
return [];
|
|
8350
8524
|
const results = [];
|
|
8351
8525
|
for (const file of files) {
|
|
8352
|
-
const
|
|
8353
|
-
if (
|
|
8526
|
+
const info3 = await inspectImage(file);
|
|
8527
|
+
if (info3?.hasAlpha) {
|
|
8354
8528
|
results.push({
|
|
8355
|
-
checkId:
|
|
8529
|
+
checkId: check32.id,
|
|
8356
8530
|
severity: "warning",
|
|
8357
8531
|
message: "Image contains alpha channel, which may cause print issues",
|
|
8358
8532
|
file
|
|
@@ -8362,10 +8536,10 @@ var check31 = {
|
|
|
8362
8536
|
return results;
|
|
8363
8537
|
}
|
|
8364
8538
|
};
|
|
8365
|
-
registerCheck(
|
|
8539
|
+
registerCheck(check32);
|
|
8366
8540
|
|
|
8367
8541
|
// src/checks/asset/image-tac.ts
|
|
8368
|
-
var
|
|
8542
|
+
var check33 = {
|
|
8369
8543
|
id: "asset.image.tac-raster",
|
|
8370
8544
|
name: "Image TAC (Raster)",
|
|
8371
8545
|
description: "Rasterizes and checks TAC per image using Ghostscript",
|
|
@@ -8384,7 +8558,7 @@ var check32 = {
|
|
|
8384
8558
|
const ghostscript = await resolveGhostscript();
|
|
8385
8559
|
if (!ghostscript) {
|
|
8386
8560
|
return [
|
|
8387
|
-
inspectionFailed(
|
|
8561
|
+
inspectionFailed(check33.id, "Could not inspect image ink coverage: Ghostscript executable not found")
|
|
8388
8562
|
];
|
|
8389
8563
|
}
|
|
8390
8564
|
for (const file of files) {
|
|
@@ -8402,7 +8576,7 @@ var check32 = {
|
|
|
8402
8576
|
if (nums.length === 4 && nums.every((n) => Number.isFinite(n))) {
|
|
8403
8577
|
const tac = (nums[0] + nums[1] + nums[2] + nums[3]) * 100;
|
|
8404
8578
|
if (tac > maxTac) {
|
|
8405
|
-
results.push(finding(
|
|
8579
|
+
results.push(finding(check33.id, {
|
|
8406
8580
|
severity: "warning",
|
|
8407
8581
|
message: `Image TAC exceeds limit: ${tac.toFixed(1)}% (max ${maxTac}%)`,
|
|
8408
8582
|
file,
|
|
@@ -8414,16 +8588,16 @@ var check32 = {
|
|
|
8414
8588
|
}
|
|
8415
8589
|
}
|
|
8416
8590
|
} catch {
|
|
8417
|
-
results.push(inspectionFailed(
|
|
8591
|
+
results.push(inspectionFailed(check33.id, `Could not inspect image ink coverage: ${file}`, { file }));
|
|
8418
8592
|
}
|
|
8419
8593
|
}
|
|
8420
8594
|
return results;
|
|
8421
8595
|
}
|
|
8422
8596
|
};
|
|
8423
|
-
registerCheck(
|
|
8597
|
+
registerCheck(check33);
|
|
8424
8598
|
|
|
8425
8599
|
// src/checks/asset/approved-fonts.ts
|
|
8426
|
-
var
|
|
8600
|
+
var check34 = {
|
|
8427
8601
|
id: "asset.font.approved-files",
|
|
8428
8602
|
name: "Approved Font Files",
|
|
8429
8603
|
description: "Checks font files against the approved file patterns",
|
|
@@ -8462,7 +8636,7 @@ var check33 = {
|
|
|
8462
8636
|
for (const font of allFonts) {
|
|
8463
8637
|
if (!approvedFonts.has(font)) {
|
|
8464
8638
|
results.push({
|
|
8465
|
-
checkId:
|
|
8639
|
+
checkId: check34.id,
|
|
8466
8640
|
severity: "warning",
|
|
8467
8641
|
message: "Font file not in approved list",
|
|
8468
8642
|
file: font
|
|
@@ -8472,7 +8646,7 @@ var check33 = {
|
|
|
8472
8646
|
return results;
|
|
8473
8647
|
}
|
|
8474
8648
|
};
|
|
8475
|
-
registerCheck(
|
|
8649
|
+
registerCheck(check34);
|
|
8476
8650
|
|
|
8477
8651
|
// src/checks/asset/font-license.ts
|
|
8478
8652
|
import { existsSync as existsSync10 } from "node:fs";
|
|
@@ -8487,7 +8661,7 @@ var LICENSE_NAMES = [
|
|
|
8487
8661
|
"OFL-1.1.txt",
|
|
8488
8662
|
"COPYING"
|
|
8489
8663
|
];
|
|
8490
|
-
var
|
|
8664
|
+
var check35 = {
|
|
8491
8665
|
id: "asset.font.license",
|
|
8492
8666
|
name: "Font License",
|
|
8493
8667
|
description: "Checks for license files in font directories",
|
|
@@ -8514,7 +8688,7 @@ var check34 = {
|
|
|
8514
8688
|
const hasLicense = LICENSE_NAMES.some((name) => existsSync10(resolve6(fontDir, name)));
|
|
8515
8689
|
if (!hasLicense) {
|
|
8516
8690
|
results.push({
|
|
8517
|
-
checkId:
|
|
8691
|
+
checkId: check35.id,
|
|
8518
8692
|
severity: "warning",
|
|
8519
8693
|
message: `No font license file found in directory`,
|
|
8520
8694
|
file: fontDir
|
|
@@ -8524,10 +8698,10 @@ var check34 = {
|
|
|
8524
8698
|
return results;
|
|
8525
8699
|
}
|
|
8526
8700
|
};
|
|
8527
|
-
registerCheck(
|
|
8701
|
+
registerCheck(check35);
|
|
8528
8702
|
|
|
8529
8703
|
// src/checks/heuristic/text-density.ts
|
|
8530
|
-
var
|
|
8704
|
+
var check36 = {
|
|
8531
8705
|
id: "heuristic.whitespace.text-density",
|
|
8532
8706
|
name: "Text Density",
|
|
8533
8707
|
description: "Checks characters-per-page ratio",
|
|
@@ -8556,7 +8730,7 @@ var check35 = {
|
|
|
8556
8730
|
});
|
|
8557
8731
|
if (lowPages.length > 0) {
|
|
8558
8732
|
results.push({
|
|
8559
|
-
checkId:
|
|
8733
|
+
checkId: check36.id,
|
|
8560
8734
|
severity: "info",
|
|
8561
8735
|
message: `Low text density on pages: ${lowPages.join(", ")} (below ${range.min} chars)`,
|
|
8562
8736
|
file: ctx.pdfPath
|
|
@@ -8564,7 +8738,7 @@ var check35 = {
|
|
|
8564
8738
|
}
|
|
8565
8739
|
if (highPages.length > 0) {
|
|
8566
8740
|
results.push({
|
|
8567
|
-
checkId:
|
|
8741
|
+
checkId: check36.id,
|
|
8568
8742
|
severity: "info",
|
|
8569
8743
|
message: `High text density on pages: ${highPages.join(", ")} (above ${range.max} chars)`,
|
|
8570
8744
|
file: ctx.pdfPath
|
|
@@ -8573,11 +8747,11 @@ var check35 = {
|
|
|
8573
8747
|
return results;
|
|
8574
8748
|
}
|
|
8575
8749
|
};
|
|
8576
|
-
registerCheck(
|
|
8750
|
+
registerCheck(check36);
|
|
8577
8751
|
|
|
8578
8752
|
// src/checks/heuristic/section-density.ts
|
|
8579
|
-
import { readFile as
|
|
8580
|
-
var
|
|
8753
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
8754
|
+
var check37 = {
|
|
8581
8755
|
id: "heuristic.chunking.section-density",
|
|
8582
8756
|
name: "Section Density",
|
|
8583
8757
|
description: "Checks heading/paragraph/callout density from source Markdown",
|
|
@@ -8593,7 +8767,7 @@ var check36 = {
|
|
|
8593
8767
|
const results = [];
|
|
8594
8768
|
for (const file of files) {
|
|
8595
8769
|
try {
|
|
8596
|
-
const content = await
|
|
8770
|
+
const content = await readFile22(file, "utf8");
|
|
8597
8771
|
const lines = content.split(`
|
|
8598
8772
|
`);
|
|
8599
8773
|
let currentSectionStart = 0;
|
|
@@ -8603,7 +8777,7 @@ var check36 = {
|
|
|
8603
8777
|
const line = lines[i];
|
|
8604
8778
|
if (/^#{1,6}\s/.test(line)) {
|
|
8605
8779
|
if (paragraphCount > maxParas) {
|
|
8606
|
-
results.push(finding(
|
|
8780
|
+
results.push(finding(check37.id, {
|
|
8607
8781
|
severity: "info",
|
|
8608
8782
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8609
8783
|
file,
|
|
@@ -8623,7 +8797,7 @@ var check36 = {
|
|
|
8623
8797
|
}
|
|
8624
8798
|
}
|
|
8625
8799
|
if (paragraphCount > maxParas) {
|
|
8626
|
-
results.push(finding(
|
|
8800
|
+
results.push(finding(check37.id, {
|
|
8627
8801
|
severity: "info",
|
|
8628
8802
|
message: `Section has ${paragraphCount} paragraphs (max recommended: ${maxParas})`,
|
|
8629
8803
|
file,
|
|
@@ -8631,7 +8805,7 @@ var check36 = {
|
|
|
8631
8805
|
}));
|
|
8632
8806
|
}
|
|
8633
8807
|
} catch {
|
|
8634
|
-
results.push(inspectionFailed(
|
|
8808
|
+
results.push(inspectionFailed(check37.id, `Could not read source file: ${file}`, {
|
|
8635
8809
|
file
|
|
8636
8810
|
}));
|
|
8637
8811
|
}
|
|
@@ -8639,10 +8813,10 @@ var check36 = {
|
|
|
8639
8813
|
return results;
|
|
8640
8814
|
}
|
|
8641
8815
|
};
|
|
8642
|
-
registerCheck(
|
|
8816
|
+
registerCheck(check37);
|
|
8643
8817
|
|
|
8644
8818
|
// src/checks/heuristic/layer-count.ts
|
|
8645
|
-
var
|
|
8819
|
+
var check38 = {
|
|
8646
8820
|
id: "heuristic.decoration.layer-count",
|
|
8647
8821
|
name: "Layer Count",
|
|
8648
8822
|
description: "Counts image objects per page",
|
|
@@ -8667,7 +8841,7 @@ var check37 = {
|
|
|
8667
8841
|
heavyPages.sort((a, b) => a - b);
|
|
8668
8842
|
return [
|
|
8669
8843
|
{
|
|
8670
|
-
checkId:
|
|
8844
|
+
checkId: check38.id,
|
|
8671
8845
|
severity: "info",
|
|
8672
8846
|
message: `Pages with many image layers (>${maxLayers}): ${heavyPages.join(", ")}`,
|
|
8673
8847
|
file: ctx.pdfPath
|
|
@@ -8677,10 +8851,10 @@ var check37 = {
|
|
|
8677
8851
|
return [];
|
|
8678
8852
|
}
|
|
8679
8853
|
};
|
|
8680
|
-
registerCheck(
|
|
8854
|
+
registerCheck(check38);
|
|
8681
8855
|
|
|
8682
8856
|
// src/checks/heuristic/placement-variance.ts
|
|
8683
|
-
var
|
|
8857
|
+
var check39 = {
|
|
8684
8858
|
id: "heuristic.layout.placement-variance",
|
|
8685
8859
|
name: "Placement Variance",
|
|
8686
8860
|
description: "Analyzes text baseline coordinates for layout consistency",
|
|
@@ -8698,7 +8872,7 @@ var check38 = {
|
|
|
8698
8872
|
const uniqueX = new Set(positions.map((p) => Math.round(p.x)));
|
|
8699
8873
|
return [
|
|
8700
8874
|
{
|
|
8701
|
-
checkId:
|
|
8875
|
+
checkId: check39.id,
|
|
8702
8876
|
severity: "info",
|
|
8703
8877
|
message: `Layout analysis: ${uniqueX.size} unique horizontal text positions across ${positions.length} text blocks.`,
|
|
8704
8878
|
file: ctx.pdfPath
|
|
@@ -8706,21 +8880,21 @@ var check38 = {
|
|
|
8706
8880
|
];
|
|
8707
8881
|
}
|
|
8708
8882
|
};
|
|
8709
|
-
registerCheck(
|
|
8883
|
+
registerCheck(check39);
|
|
8710
8884
|
|
|
8711
8885
|
// src/checks/tool-check.ts
|
|
8712
8886
|
async function checkToolAvailability(config, opts = {}) {
|
|
8713
8887
|
const { checks: checks2 } = selectChecks(opts, config);
|
|
8714
8888
|
const toolToChecks = new Map;
|
|
8715
|
-
for (const
|
|
8716
|
-
if (!
|
|
8889
|
+
for (const check40 of checks2) {
|
|
8890
|
+
if (!check40.requiredTools?.length)
|
|
8717
8891
|
continue;
|
|
8718
|
-
for (const tool of
|
|
8892
|
+
for (const tool of check40.requiredTools) {
|
|
8719
8893
|
const existing = toolToChecks.get(tool);
|
|
8720
8894
|
if (existing) {
|
|
8721
|
-
existing.push(
|
|
8895
|
+
existing.push(check40.id);
|
|
8722
8896
|
} else {
|
|
8723
|
-
toolToChecks.set(tool, [
|
|
8897
|
+
toolToChecks.set(tool, [check40.id]);
|
|
8724
8898
|
}
|
|
8725
8899
|
}
|
|
8726
8900
|
}
|
|
@@ -8736,11 +8910,11 @@ async function checkToolAvailability(config, opts = {}) {
|
|
|
8736
8910
|
const missing = results.filter((r) => !r.found).map((r) => r.tool);
|
|
8737
8911
|
const missingSet = new Set(missing);
|
|
8738
8912
|
const skippedChecks = [];
|
|
8739
|
-
for (const
|
|
8740
|
-
if (!
|
|
8913
|
+
for (const check40 of checks2) {
|
|
8914
|
+
if (!check40.requiredTools?.length)
|
|
8741
8915
|
continue;
|
|
8742
|
-
if (
|
|
8743
|
-
skippedChecks.push(
|
|
8916
|
+
if (check40.requiredTools.some((t) => missingSet.has(t))) {
|
|
8917
|
+
skippedChecks.push(check40.id);
|
|
8744
8918
|
}
|
|
8745
8919
|
}
|
|
8746
8920
|
return { available, missing, skippedChecks, toolToChecks };
|
|
@@ -8897,7 +9071,7 @@ async function restoreForCommand(dir, options) {
|
|
|
8897
9071
|
}
|
|
8898
9072
|
|
|
8899
9073
|
// src/lib/markdown/index.ts
|
|
8900
|
-
import { readdir as readdir3, readFile as
|
|
9074
|
+
import { readdir as readdir3, readFile as readFile23, writeFile as writeFile2, mkdir as mkdir4 } from "node:fs/promises";
|
|
8901
9075
|
|
|
8902
9076
|
// src/lib/remote-auth/sync-messages.ts
|
|
8903
9077
|
function isOnlineSibling(filepath) {
|
|
@@ -9039,7 +9213,7 @@ ${css.trim()}
|
|
|
9039
9213
|
}
|
|
9040
9214
|
return assembleBookHtml({
|
|
9041
9215
|
files,
|
|
9042
|
-
readText: (relPath) =>
|
|
9216
|
+
readText: (relPath) => readFile23(join2(inputDir, canonicalChapterId(relPath)), "utf-8"),
|
|
9043
9217
|
projectCss: inlined.css,
|
|
9044
9218
|
preloadImages: cssAssetCopies.map((c) => c.to),
|
|
9045
9219
|
title: opts.title,
|
|
@@ -9071,7 +9245,7 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
|
|
|
9071
9245
|
}
|
|
9072
9246
|
|
|
9073
9247
|
// src/lib/markdownlint-fix.ts
|
|
9074
|
-
import { readFile as
|
|
9248
|
+
import { readFile as readFile24, writeFile as writeFile3 } from "node:fs/promises";
|
|
9075
9249
|
import { applyFixes } from "markdownlint";
|
|
9076
9250
|
import { lint as lint2 } from "markdownlint/sync";
|
|
9077
9251
|
async function uncommittedAmong(inputDir, files) {
|
|
@@ -9111,7 +9285,7 @@ async function applyMarkdownlintFixes(ctx) {
|
|
|
9111
9285
|
for (const [file, violations] of Object.entries(results)) {
|
|
9112
9286
|
if (!Array.isArray(violations) || violations.length === 0)
|
|
9113
9287
|
continue;
|
|
9114
|
-
const before = await
|
|
9288
|
+
const before = await readFile24(file, "utf8");
|
|
9115
9289
|
const after = applyFixes(before, violations);
|
|
9116
9290
|
if (after !== before)
|
|
9117
9291
|
pending.push({ file, content: after });
|
|
@@ -9220,16 +9394,16 @@ async function runChecks(ctx, opts = {}) {
|
|
|
9220
9394
|
}
|
|
9221
9395
|
const release = retainPdfCache();
|
|
9222
9396
|
try {
|
|
9223
|
-
for (const
|
|
9397
|
+
for (const check40 of checks2) {
|
|
9224
9398
|
try {
|
|
9225
|
-
let results = await
|
|
9226
|
-
const severityOverride = getCheckSeverityOverride(
|
|
9399
|
+
let results = await check40.run(ctx);
|
|
9400
|
+
const severityOverride = getCheckSeverityOverride(check40.id, ctx.config);
|
|
9227
9401
|
if (severityOverride) {
|
|
9228
9402
|
for (const r of results) {
|
|
9229
9403
|
r.severity = severityOverride;
|
|
9230
9404
|
}
|
|
9231
9405
|
}
|
|
9232
|
-
const lintLevel = ruleLevel(ctx.config.lint.rules,
|
|
9406
|
+
const lintLevel = ruleLevel(ctx.config.lint.rules, check40.id);
|
|
9233
9407
|
if (lintLevel === "off")
|
|
9234
9408
|
results = [];
|
|
9235
9409
|
else if (lintLevel) {
|
|
@@ -9237,15 +9411,15 @@ async function runChecks(ctx, opts = {}) {
|
|
|
9237
9411
|
r.severity = lintLevel === "warn" ? "warning" : "error";
|
|
9238
9412
|
}
|
|
9239
9413
|
if (results.length === 0) {
|
|
9240
|
-
passed.push(
|
|
9414
|
+
passed.push(check40.id);
|
|
9241
9415
|
} else {
|
|
9242
9416
|
allResults.push(...results);
|
|
9243
9417
|
}
|
|
9244
9418
|
} catch (err) {
|
|
9245
9419
|
allResults.push({
|
|
9246
|
-
checkId:
|
|
9420
|
+
checkId: check40.id,
|
|
9247
9421
|
severity: "error",
|
|
9248
|
-
message: `Check "${
|
|
9422
|
+
message: `Check "${check40.id}" threw: ${err instanceof Error ? err.message : String(err)}`
|
|
9249
9423
|
});
|
|
9250
9424
|
}
|
|
9251
9425
|
}
|
|
@@ -9537,7 +9711,7 @@ async function executeValidation(args) {
|
|
|
9537
9711
|
};
|
|
9538
9712
|
if (args.fix) {
|
|
9539
9713
|
const selected = selectChecks(runnerOptions, config).checks;
|
|
9540
|
-
if (selected.some((
|
|
9714
|
+
if (selected.some((check40) => check40.id === MARKDOWNLINT_CHECK_ID)) {
|
|
9541
9715
|
await applyMarkdownlintFixes(context);
|
|
9542
9716
|
} else {
|
|
9543
9717
|
log.warn(`--fix: ${MARKDOWNLINT_CHECK_ID} is not part of this run — no files were fixed.`);
|
|
@@ -9624,7 +9798,7 @@ import { randomBytes } from "node:crypto";
|
|
|
9624
9798
|
|
|
9625
9799
|
// src/lib/build-fingerprint.ts
|
|
9626
9800
|
import * as fs from "node:fs";
|
|
9627
|
-
import { mkdir as mkdir5, readFile as
|
|
9801
|
+
import { mkdir as mkdir5, readFile as readFile25, writeFile as writeFile4 } from "node:fs/promises";
|
|
9628
9802
|
import { createHash as createHash4 } from "node:crypto";
|
|
9629
9803
|
import path12 from "node:path";
|
|
9630
9804
|
import git from "isomorphic-git";
|
|
@@ -9711,8 +9885,8 @@ async function getEngineBundleHash() {
|
|
|
9711
9885
|
getAssetPath("engine/gutterpress-agent.js")
|
|
9712
9886
|
]);
|
|
9713
9887
|
const [viewerBytes, agentBytes] = await Promise.all([
|
|
9714
|
-
|
|
9715
|
-
|
|
9888
|
+
readFile25(viewerPath),
|
|
9889
|
+
readFile25(agentPath)
|
|
9716
9890
|
]);
|
|
9717
9891
|
const hash = createHash4("sha256");
|
|
9718
9892
|
hash.update(viewerBytes);
|
|
@@ -10082,7 +10256,7 @@ class PdfOutput {
|
|
|
10082
10256
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
10083
10257
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
10084
10258
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
10085
|
-
const { buildNativePdf } = await import("./engine-
|
|
10259
|
+
const { buildNativePdf } = await import("./engine-fe0n7v9p.js");
|
|
10086
10260
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
10087
10261
|
title: config.title,
|
|
10088
10262
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -10107,6 +10281,10 @@ class PdfOutput {
|
|
|
10107
10281
|
if (await hasLiveTransparency(await fsp2.readFile(rawPdf))) {
|
|
10108
10282
|
log.warn("This book uses transparency — an image with an alpha channel, or a CSS " + `rule like \`opacity\` or \`mix-blend-mode\`. PDF/${pdfxMode === "x1a" ? "X-1a" : "X-3"} has no way to represent that (both are based on PDF 1.3, which predates PDF transparency), so Ghostscript will flatten every page that uses it into a single raster ` + "image — that page loses its embedded fonts and searchable text in the " + "PDF/X output. To keep vector text, remove the `opacity`/blend rule and flatten any alpha-channel image against its intended background before including it (export it with no alpha channel), or build --format pdf instead of pdfx if this book doesn't need print-ready CMYK.");
|
|
10109
10283
|
}
|
|
10284
|
+
const blankGradientPages = await findPostScriptShadingPages(await fsp2.readFile(rawPdf));
|
|
10285
|
+
if (blankGradientPages.length > 0) {
|
|
10286
|
+
log.warn(`Page${blankGradientPages.length === 1 ? "" : "s"} ${blankGradientPages.join(", ")}: ` + "a repeating-*-gradient() or conic-gradient() will print blank — " + "Ghostscript cannot convert it to PDF/X. Replace it with a plain linear-gradient() or radial-gradient(), or a small tiled image. For 0deg/90deg stripes, `linear-gradient(90deg, #b33 0 6px, #c8c5bf 6px 10px)` with `background-size: 10px 100%` converts correctly.");
|
|
10287
|
+
}
|
|
10110
10288
|
const shouldStrip = opts.stripAnnotations ?? config.pdfx.stripAnnotations;
|
|
10111
10289
|
shouldStripAnnotations = shouldStrip;
|
|
10112
10290
|
if (shouldStrip) {
|
|
@@ -10161,7 +10339,7 @@ async function runBuild(opts) {
|
|
|
10161
10339
|
if (ctx.format !== "html") {
|
|
10162
10340
|
await preflightBuildTools(ctx.format, opts, ctx.config);
|
|
10163
10341
|
}
|
|
10164
|
-
const launch = opts.engineBrowser ?? (async () => (await import("./cdp-
|
|
10342
|
+
const launch = opts.engineBrowser ?? (async () => (await import("./cdp-1hn3xsk2.js")).launchChromium());
|
|
10165
10343
|
const browserPromise = ctx.format === "html" ? null : launch();
|
|
10166
10344
|
browserPromise?.catch(() => {});
|
|
10167
10345
|
try {
|
|
@@ -10217,7 +10395,7 @@ function openPath(filePath) {
|
|
|
10217
10395
|
import path16 from "path";
|
|
10218
10396
|
import { tmpdir } from "os";
|
|
10219
10397
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
10220
|
-
import { readdir as readdir4, readFile as
|
|
10398
|
+
import { readdir as readdir4, readFile as readFile26, writeFile as writeFile5 } from "node:fs/promises";
|
|
10221
10399
|
|
|
10222
10400
|
// src/utils/file-utils.ts
|
|
10223
10401
|
import { promises as fs3 } from "fs";
|
|
@@ -10769,7 +10947,7 @@ async function cleanupOrphanTempDirs() {
|
|
|
10769
10947
|
const dir = path16.join(TEMP_DIR_BASE, entry);
|
|
10770
10948
|
const pidFile = path16.join(dir, PID_FILE_NAME);
|
|
10771
10949
|
try {
|
|
10772
|
-
const raw = await
|
|
10950
|
+
const raw = await readFile26(pidFile, "utf8");
|
|
10773
10951
|
const pid = parseInt(raw.trim(), 10);
|
|
10774
10952
|
if (!isProcessAlive(pid)) {
|
|
10775
10953
|
debug(`Removing orphan temp dir ${dir} (pid ${pid} not alive)`);
|
|
@@ -10876,7 +11054,7 @@ function createServerState(inputPath, tempDir, config, options) {
|
|
|
10876
11054
|
import http from "node:http";
|
|
10877
11055
|
import net from "node:net";
|
|
10878
11056
|
import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
|
|
10879
|
-
import { readFile as
|
|
11057
|
+
import { readFile as readFile27, stat as stat5 } from "node:fs/promises";
|
|
10880
11058
|
import path17 from "path";
|
|
10881
11059
|
import { WebSocket, WebSocketServer } from "ws";
|
|
10882
11060
|
var HMR_PATH = "/__gutterpress-hmr";
|
|
@@ -10945,7 +11123,7 @@ async function serveStatic(absPath, res, cacheControl = "no-store", extraHeaders
|
|
|
10945
11123
|
}
|
|
10946
11124
|
let data;
|
|
10947
11125
|
try {
|
|
10948
|
-
data = await
|
|
11126
|
+
data = await readFile27(filePath);
|
|
10949
11127
|
} catch {
|
|
10950
11128
|
res.writeHead(404);
|
|
10951
11129
|
res.end("Not Found");
|
|
@@ -10994,7 +11172,7 @@ async function serveRevalidatedStatic(req, absPath, res, validators) {
|
|
|
10994
11172
|
let data;
|
|
10995
11173
|
if (!validator || validator.stamp !== stamp) {
|
|
10996
11174
|
try {
|
|
10997
|
-
data = await
|
|
11175
|
+
data = await readFile27(absPath);
|
|
10998
11176
|
} catch {
|
|
10999
11177
|
await serveStatic(absPath, res);
|
|
11000
11178
|
return;
|
|
@@ -11304,7 +11482,7 @@ async function startPreviewServer(options) {
|
|
|
11304
11482
|
}
|
|
11305
11483
|
|
|
11306
11484
|
// src/lib/project-scaffold.ts
|
|
11307
|
-
import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir5, readFile as
|
|
11485
|
+
import { access, copyFile, cp as cp2, mkdir as mkdir7, readdir as readdir5, readFile as readFile28, stat as stat6, writeFile as writeFile6 } from "node:fs/promises";
|
|
11308
11486
|
import { constants as FS, existsSync as existsSync12 } from "node:fs";
|
|
11309
11487
|
import path18 from "node:path";
|
|
11310
11488
|
|
|
@@ -11503,7 +11681,7 @@ async function scaffoldProject(options) {
|
|
|
11503
11681
|
async function firstSourceFile(manifestPath) {
|
|
11504
11682
|
let text;
|
|
11505
11683
|
try {
|
|
11506
|
-
text = await
|
|
11684
|
+
text = await readFile28(manifestPath, "utf8");
|
|
11507
11685
|
} catch {
|
|
11508
11686
|
return;
|
|
11509
11687
|
}
|
|
@@ -11515,7 +11693,7 @@ async function firstSourceFile(manifestPath) {
|
|
|
11515
11693
|
return m ? m[1].trim() : undefined;
|
|
11516
11694
|
}
|
|
11517
11695
|
async function fillTemplateFile(filePath, substitutions) {
|
|
11518
|
-
let text = await
|
|
11696
|
+
let text = await readFile28(filePath, "utf8");
|
|
11519
11697
|
for (const [token, value] of Object.entries(substitutions)) {
|
|
11520
11698
|
text = text.split(token).join(value);
|
|
11521
11699
|
}
|
|
@@ -11523,7 +11701,7 @@ async function fillTemplateFile(filePath, substitutions) {
|
|
|
11523
11701
|
}
|
|
11524
11702
|
|
|
11525
11703
|
// src/lib/extension-scaffold.ts
|
|
11526
|
-
import { access as access2, mkdir as mkdir8, readFile as
|
|
11704
|
+
import { access as access2, mkdir as mkdir8, readFile as readFile29, writeFile as writeFile7 } from "node:fs/promises";
|
|
11527
11705
|
import { constants as FS2 } from "node:fs";
|
|
11528
11706
|
import path19 from "node:path";
|
|
11529
11707
|
var EXTENSION_KINDS = ["plugin", "theme"];
|
|
@@ -11640,7 +11818,7 @@ async function scaffoldExtension(options) {
|
|
|
11640
11818
|
const src = await getAssetPath(`extension-templates/${kind}/${assetName}`);
|
|
11641
11819
|
const dest = path19.join(extensionDir, relDest);
|
|
11642
11820
|
await mkdir8(path19.dirname(dest), { recursive: true });
|
|
11643
|
-
const text = await
|
|
11821
|
+
const text = await readFile29(src, "utf8");
|
|
11644
11822
|
const table = relDest.endsWith(".json") ? jsonSafe : plain;
|
|
11645
11823
|
await writeFile7(dest, substitute(text, table), "utf8");
|
|
11646
11824
|
written.push(relDest);
|
|
@@ -11685,7 +11863,7 @@ var SKIP_ENTRIES = new Set([
|
|
|
11685
11863
|
|
|
11686
11864
|
// src/lib/extension-import.ts
|
|
11687
11865
|
import { existsSync as existsSync13 } from "node:fs";
|
|
11688
|
-
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as
|
|
11866
|
+
import { cp as cp3, mkdir as mkdir9, mkdtemp as mkdtemp2, readFile as readFile30, rm as rm4, writeFile as writeFile8 } from "node:fs/promises";
|
|
11689
11867
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
11690
11868
|
import path20 from "node:path";
|
|
11691
11869
|
import { unzipSync } from "fflate";
|
|
@@ -11758,7 +11936,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11758
11936
|
if (!existsSync13(cssPath)) {
|
|
11759
11937
|
throw new Error("A theme must contain a theme.css file.");
|
|
11760
11938
|
}
|
|
11761
|
-
const css = await
|
|
11939
|
+
const css = await readFile30(cssPath, "utf8");
|
|
11762
11940
|
const { reject, warnings: printFindings } = classifyThemeCssFindings(checkCss(css, "theme.css"));
|
|
11763
11941
|
if (reject) {
|
|
11764
11942
|
throw new Error(`theme.css could not be parsed — ${reject.message}`);
|
|
@@ -11786,7 +11964,7 @@ async function finalizeExtensionImport(projectDir, sourceDir, relPaths, fallback
|
|
|
11786
11964
|
for (let i = 0;i < extraSheets.length; i++) {
|
|
11787
11965
|
const rel = extraSheets[i];
|
|
11788
11966
|
const sheetPath = extraSheetPaths[i];
|
|
11789
|
-
const sheetCss = await
|
|
11967
|
+
const sheetCss = await readFile30(sheetPath, "utf8");
|
|
11790
11968
|
const { reject: sheetReject, warnings: sheetFindings } = classifyThemeCssFindings(checkCss(sheetCss, rel));
|
|
11791
11969
|
if (sheetReject) {
|
|
11792
11970
|
throw new Error(`${rel} could not be parsed — ${sheetReject.message}`);
|
|
@@ -11819,7 +11997,7 @@ async function completeLandedManifest(dest, id, meta, metaExists) {
|
|
|
11819
11997
|
let pkg = {};
|
|
11820
11998
|
if (metaExists) {
|
|
11821
11999
|
try {
|
|
11822
|
-
const parsed = JSON.parse(await
|
|
12000
|
+
const parsed = JSON.parse(await readFile30(file, "utf8"));
|
|
11823
12001
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11824
12002
|
pkg = parsed;
|
|
11825
12003
|
}
|
|
@@ -11921,11 +12099,11 @@ async function importExtensionFromCssText(projectDir, css, name) {
|
|
|
11921
12099
|
async function importExtensionFromFile(projectDir, filePath) {
|
|
11922
12100
|
const ext = path20.extname(filePath).toLowerCase();
|
|
11923
12101
|
if (ext === ".zip") {
|
|
11924
|
-
const buf = await
|
|
12102
|
+
const buf = await readFile30(filePath);
|
|
11925
12103
|
return importExtensionFromZip(projectDir, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
11926
12104
|
}
|
|
11927
12105
|
if (ext === ".css") {
|
|
11928
|
-
const css = await
|
|
12106
|
+
const css = await readFile30(filePath, "utf8");
|
|
11929
12107
|
return importExtensionFromCssText(projectDir, css, prettify(path20.basename(filePath, ".css")));
|
|
11930
12108
|
}
|
|
11931
12109
|
throw new Error("Choose a .zip extension package or a .css stylesheet.");
|
|
@@ -12129,13 +12307,13 @@ function publishCredentialKey(host, account) {
|
|
|
12129
12307
|
const a = (account ?? "").trim();
|
|
12130
12308
|
return a ? `${host}#${a}` : host;
|
|
12131
12309
|
}
|
|
12132
|
-
async function resolvePublishCredential(
|
|
12310
|
+
async function resolvePublishCredential(info3, deps, account = deps.credentialAccount) {
|
|
12133
12311
|
const env = deps.env ?? process.env;
|
|
12134
|
-
const fromEnv =
|
|
12312
|
+
const fromEnv = info3.credential.envVar ? env[info3.credential.envVar]?.trim() : undefined;
|
|
12135
12313
|
if (fromEnv) {
|
|
12136
12314
|
return {
|
|
12137
12315
|
credential: {
|
|
12138
|
-
host:
|
|
12316
|
+
host: info3.credential.host,
|
|
12139
12317
|
kind: "token",
|
|
12140
12318
|
token: fromEnv,
|
|
12141
12319
|
createdAt: 0
|
|
@@ -12143,21 +12321,21 @@ async function resolvePublishCredential(info2, deps, account = deps.credentialAc
|
|
|
12143
12321
|
source: "env"
|
|
12144
12322
|
};
|
|
12145
12323
|
}
|
|
12146
|
-
const stored = await deps.tokenStore.get(publishCredentialKey(
|
|
12324
|
+
const stored = await deps.tokenStore.get(publishCredentialKey(info3.credential.host, account));
|
|
12147
12325
|
return stored ? { credential: stored, source: "store" } : null;
|
|
12148
12326
|
}
|
|
12149
|
-
async function publishConnectionStatus(
|
|
12150
|
-
if (!
|
|
12327
|
+
async function publishConnectionStatus(info3, deps, account = deps.credentialAccount) {
|
|
12328
|
+
if (!info3.credential.required)
|
|
12151
12329
|
return { connected: true };
|
|
12152
|
-
const resolved = await resolvePublishCredential(
|
|
12330
|
+
const resolved = await resolvePublishCredential(info3, deps, account);
|
|
12153
12331
|
return resolved ? { connected: true, source: resolved.source } : { connected: false };
|
|
12154
12332
|
}
|
|
12155
|
-
async function listPublishAccounts(
|
|
12156
|
-
const wantHost =
|
|
12333
|
+
async function listPublishAccounts(info3, deps) {
|
|
12334
|
+
const wantHost = info3.credential.host.trim().toLowerCase();
|
|
12157
12335
|
const all = await deps.tokenStore.list();
|
|
12158
12336
|
return all.filter((c) => c.host.trim().toLowerCase() === wantHost).map((c) => ({
|
|
12159
12337
|
account: (c.username ?? "").trim(),
|
|
12160
|
-
label: c.label ?? (c.username ||
|
|
12338
|
+
label: c.label ?? (c.username || info3.label),
|
|
12161
12339
|
createdAt: c.createdAt
|
|
12162
12340
|
}));
|
|
12163
12341
|
}
|
|
@@ -12188,8 +12366,8 @@ async function readGoogleApiError(res) {
|
|
|
12188
12366
|
}
|
|
12189
12367
|
var RATE_LIMIT_REASONS = /^(dailyLimitExceeded|userRateLimitExceeded|rateLimitExceeded|quotaExceeded)$/i;
|
|
12190
12368
|
var PERMISSION_REASONS = /^(insufficientPermissions|insufficientFilePermissions|forbidden|PERMISSION_DENIED)$/i;
|
|
12191
|
-
function hintFor(
|
|
12192
|
-
const reason =
|
|
12369
|
+
function hintFor(info3) {
|
|
12370
|
+
const reason = info3.reason ?? "";
|
|
12193
12371
|
if (/^accessNotConfigured$/i.test(reason)) {
|
|
12194
12372
|
return "Google Drive publishing isn't fully set up on this build: the Google Drive API isn't enabled for the app's Google Cloud project, so a maintainer needs to enable it (the link is in Google's message).";
|
|
12195
12373
|
}
|
|
@@ -12199,18 +12377,18 @@ function hintFor(info2) {
|
|
|
12199
12377
|
if (RATE_LIMIT_REASONS.test(reason)) {
|
|
12200
12378
|
return "Google Drive is rate-limiting requests right now. Wait a minute and try again.";
|
|
12201
12379
|
}
|
|
12202
|
-
if (PERMISSION_REASONS.test(reason) ||
|
|
12380
|
+
if (PERMISSION_REASONS.test(reason) || info3.status === 401) {
|
|
12203
12381
|
return "This Google Drive connection doesn't have permission for that — disconnect and connect Google Drive again to re-approve access.";
|
|
12204
12382
|
}
|
|
12205
12383
|
return;
|
|
12206
12384
|
}
|
|
12207
|
-
function googleApiFailure(what,
|
|
12208
|
-
const parts = [`${what} (HTTP ${
|
|
12209
|
-
const hint = hintFor(
|
|
12385
|
+
function googleApiFailure(what, info3) {
|
|
12386
|
+
const parts = [`${what} (HTTP ${info3.status}${info3.reason ? `, ${info3.reason}` : ""}).`];
|
|
12387
|
+
const hint = hintFor(info3);
|
|
12210
12388
|
if (hint)
|
|
12211
12389
|
parts.push(hint);
|
|
12212
|
-
if (
|
|
12213
|
-
parts.push(`Google said: "${
|
|
12390
|
+
if (info3.message)
|
|
12391
|
+
parts.push(`Google said: "${info3.message}"`);
|
|
12214
12392
|
let text = parts.join(" ");
|
|
12215
12393
|
if (!/\bgoogle\b/i.test(text))
|
|
12216
12394
|
text = `Google Drive: ${text}`;
|
|
@@ -12538,7 +12716,7 @@ async function commandExists(cmd, runCommand = defaultCommandRunner, env) {
|
|
|
12538
12716
|
|
|
12539
12717
|
// src/lib/publish/providers/azure-swa.ts
|
|
12540
12718
|
var AZURE_SWA_HOST = "azure-swa";
|
|
12541
|
-
var
|
|
12719
|
+
var info3 = {
|
|
12542
12720
|
id: "azure-swa",
|
|
12543
12721
|
label: "Azure Static Web Apps",
|
|
12544
12722
|
kind: "api",
|
|
@@ -12563,9 +12741,9 @@ async function resolveSwaCommand(req) {
|
|
|
12563
12741
|
}
|
|
12564
12742
|
var SWA_INSTALL_HINT = "Install the Azure SWA CLI first: `npm install -g @azure/static-web-apps-cli` " + "(needs Node.js), or set SWA_CLI_PATH to the swa binary.";
|
|
12565
12743
|
var azureSwaProvider = {
|
|
12566
|
-
info:
|
|
12744
|
+
info: info3,
|
|
12567
12745
|
async authenticate(req) {
|
|
12568
|
-
const resolved = await resolvePublishCredential(
|
|
12746
|
+
const resolved = await resolvePublishCredential(info3, req.deps);
|
|
12569
12747
|
if (!resolved) {
|
|
12570
12748
|
return {
|
|
12571
12749
|
ok: false,
|
|
@@ -12596,7 +12774,7 @@ var azureSwaProvider = {
|
|
|
12596
12774
|
return issues;
|
|
12597
12775
|
},
|
|
12598
12776
|
async upload(req) {
|
|
12599
|
-
const resolved = await resolvePublishCredential(
|
|
12777
|
+
const resolved = await resolvePublishCredential(info3, req.deps);
|
|
12600
12778
|
if (!resolved) {
|
|
12601
12779
|
throw new Error("No Azure deployment token found. Connect Azure Static Web Apps (or set SWA_CLI_DEPLOYMENT_TOKEN) first.");
|
|
12602
12780
|
}
|
|
@@ -12652,7 +12830,7 @@ async function stageGuidedPackage(req, providerId, listingLines) {
|
|
|
12652
12830
|
|
|
12653
12831
|
// src/lib/publish/providers/drivethrurpg.ts
|
|
12654
12832
|
var PUBLISHER_HUB_URL = "https://www.drivethrurpg.com/publisher_hub";
|
|
12655
|
-
var
|
|
12833
|
+
var info4 = {
|
|
12656
12834
|
id: "drivethrurpg",
|
|
12657
12835
|
label: "DriveThruRPG",
|
|
12658
12836
|
kind: "guided",
|
|
@@ -12668,7 +12846,7 @@ var info3 = {
|
|
|
12668
12846
|
credential: { required: false, host: "drivethrurpg.com" }
|
|
12669
12847
|
};
|
|
12670
12848
|
var drivethrurpgProvider = {
|
|
12671
|
-
info:
|
|
12849
|
+
info: info4,
|
|
12672
12850
|
async authenticate() {
|
|
12673
12851
|
return { ok: true };
|
|
12674
12852
|
},
|
|
@@ -12709,7 +12887,7 @@ var drivethrurpgProvider = {
|
|
|
12709
12887
|
};
|
|
12710
12888
|
|
|
12711
12889
|
// src/lib/publish/providers/gdrive.ts
|
|
12712
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
12890
|
+
import { mkdtemp as mkdtemp3, readFile as readFile31, readdir as readdir6, rm as rm5, stat as stat7, writeFile as writeFile10 } from "node:fs/promises";
|
|
12713
12891
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
12714
12892
|
import path22 from "node:path";
|
|
12715
12893
|
import { Zip, ZipDeflate } from "fflate";
|
|
@@ -12977,7 +13155,7 @@ async function resumableUpload(fetchImpl, accessToken, opts) {
|
|
|
12977
13155
|
// src/lib/publish/providers/gdrive.ts
|
|
12978
13156
|
var DEFAULT_FOLDER_NAME = "Gutterpress";
|
|
12979
13157
|
var NOT_CONNECTED_MESSAGE = "Google Drive isn't connected. Run `gutterpress publish --provider gdrive --connect` (or set GDRIVE_REFRESH_TOKEN) first.";
|
|
12980
|
-
var
|
|
13158
|
+
var info5 = {
|
|
12981
13159
|
id: "gdrive",
|
|
12982
13160
|
label: "Google Drive",
|
|
12983
13161
|
kind: "api",
|
|
@@ -13016,7 +13194,7 @@ async function mintAccessToken(resolved, fetchImpl) {
|
|
|
13016
13194
|
return accessToken;
|
|
13017
13195
|
}
|
|
13018
13196
|
async function getAccessToken(req) {
|
|
13019
|
-
const resolved = await resolvePublishCredential(
|
|
13197
|
+
const resolved = await resolvePublishCredential(info5, req.deps);
|
|
13020
13198
|
if (!resolved)
|
|
13021
13199
|
throw new Error(NOT_CONNECTED_MESSAGE);
|
|
13022
13200
|
const accessToken = await mintAccessToken(resolved, req.deps.fetch ?? globalThis.fetch);
|
|
@@ -13101,12 +13279,12 @@ async function collectZipEntries(root, dir, out, onWarn) {
|
|
|
13101
13279
|
await collectZipEntries(root, full, out, onWarn);
|
|
13102
13280
|
} else if (entry.isFile()) {
|
|
13103
13281
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
13104
|
-
out[rel] = await
|
|
13282
|
+
out[rel] = await readFile31(full);
|
|
13105
13283
|
} else if (entry.isSymbolicLink()) {
|
|
13106
13284
|
const resolved = await stat7(full).catch(() => null);
|
|
13107
13285
|
if (resolved?.isFile()) {
|
|
13108
13286
|
const rel = path22.relative(root, full).split(path22.sep).join("/");
|
|
13109
|
-
out[rel] = await
|
|
13287
|
+
out[rel] = await readFile31(full);
|
|
13110
13288
|
} else if (resolved?.isDirectory()) {
|
|
13111
13289
|
await collectZipEntries(root, full, out, onWarn);
|
|
13112
13290
|
} else {
|
|
@@ -13138,9 +13316,9 @@ async function zipHtmlExport(exportDir, title, onWarn, writeArchiveImpl = writeF
|
|
|
13138
13316
|
}
|
|
13139
13317
|
}
|
|
13140
13318
|
var gdriveProvider = {
|
|
13141
|
-
info:
|
|
13319
|
+
info: info5,
|
|
13142
13320
|
async authenticate(req) {
|
|
13143
|
-
const resolved = await resolvePublishCredential(
|
|
13321
|
+
const resolved = await resolvePublishCredential(info5, req.deps);
|
|
13144
13322
|
if (!resolved)
|
|
13145
13323
|
return { ok: false, message: NOT_CONNECTED_MESSAGE };
|
|
13146
13324
|
try {
|
|
@@ -13305,7 +13483,7 @@ async function ensureButler(deps) {
|
|
|
13305
13483
|
var ITCH_HOST = "itch.io";
|
|
13306
13484
|
var TARGET_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
13307
13485
|
var ITCH_API_TIMEOUT_MS = 15000;
|
|
13308
|
-
var
|
|
13486
|
+
var info6 = {
|
|
13309
13487
|
id: "itch",
|
|
13310
13488
|
label: "itch.io",
|
|
13311
13489
|
kind: "api",
|
|
@@ -13335,9 +13513,9 @@ function itchProjectUrl(target) {
|
|
|
13335
13513
|
return `https://${user}.itch.io/${game}`;
|
|
13336
13514
|
}
|
|
13337
13515
|
var itchProvider = {
|
|
13338
|
-
info:
|
|
13516
|
+
info: info6,
|
|
13339
13517
|
async authenticate(req) {
|
|
13340
|
-
const resolved = await resolvePublishCredential(
|
|
13518
|
+
const resolved = await resolvePublishCredential(info6, req.deps);
|
|
13341
13519
|
if (!resolved) {
|
|
13342
13520
|
return {
|
|
13343
13521
|
ok: false,
|
|
@@ -13405,7 +13583,7 @@ var itchProvider = {
|
|
|
13405
13583
|
},
|
|
13406
13584
|
async upload(req) {
|
|
13407
13585
|
const { target, channel } = readConfig2(req);
|
|
13408
|
-
const resolved = await resolvePublishCredential(
|
|
13586
|
+
const resolved = await resolvePublishCredential(info6, req.deps);
|
|
13409
13587
|
if (!resolved) {
|
|
13410
13588
|
throw new Error("No itch.io API key found. Connect itch.io (or set BUTLER_API_KEY) first.");
|
|
13411
13589
|
}
|
|
@@ -13433,7 +13611,7 @@ ${tail}` : ""}`);
|
|
|
13433
13611
|
|
|
13434
13612
|
// src/lib/publish/providers/kdp.ts
|
|
13435
13613
|
var KDP_URL = "https://kdp.amazon.com/en_US/bookshelf";
|
|
13436
|
-
var
|
|
13614
|
+
var info7 = {
|
|
13437
13615
|
id: "kdp",
|
|
13438
13616
|
label: "Amazon KDP",
|
|
13439
13617
|
kind: "guided",
|
|
@@ -13443,7 +13621,7 @@ var info6 = {
|
|
|
13443
13621
|
credential: { required: false, host: "kdp.amazon.com" }
|
|
13444
13622
|
};
|
|
13445
13623
|
var kdpProvider = {
|
|
13446
|
-
info:
|
|
13624
|
+
info: info7,
|
|
13447
13625
|
async authenticate() {
|
|
13448
13626
|
return { ok: true };
|
|
13449
13627
|
},
|
|
@@ -13488,7 +13666,7 @@ var kdpProvider = {
|
|
|
13488
13666
|
import { cp as cp4, mkdir as mkdir12, stat as stat9 } from "node:fs/promises";
|
|
13489
13667
|
import path24 from "node:path";
|
|
13490
13668
|
var LOCAL_PUBLISH_DEFAULT_DIR = "dist";
|
|
13491
|
-
var
|
|
13669
|
+
var info8 = {
|
|
13492
13670
|
id: "local",
|
|
13493
13671
|
label: "A folder on this computer",
|
|
13494
13672
|
kind: "api",
|
|
@@ -13505,7 +13683,7 @@ function localPublishDir(req) {
|
|
|
13505
13683
|
return path24.resolve(req.project.projectDir, dir);
|
|
13506
13684
|
}
|
|
13507
13685
|
var localFolderProvider = {
|
|
13508
|
-
info:
|
|
13686
|
+
info: info8,
|
|
13509
13687
|
async authenticate() {
|
|
13510
13688
|
return { ok: true };
|
|
13511
13689
|
},
|
|
@@ -13546,7 +13724,7 @@ function requireValidShop(shop) {
|
|
|
13546
13724
|
}
|
|
13547
13725
|
return shop;
|
|
13548
13726
|
}
|
|
13549
|
-
var
|
|
13727
|
+
var info9 = {
|
|
13550
13728
|
id: "shopify",
|
|
13551
13729
|
label: "Shopify",
|
|
13552
13730
|
kind: "api",
|
|
@@ -13580,7 +13758,7 @@ function readConfig3(req) {
|
|
|
13580
13758
|
async function adminGraphQL(req, query, variables = {}) {
|
|
13581
13759
|
const { shop: rawShop, apiVersion } = readConfig3(req);
|
|
13582
13760
|
const shop = requireValidShop(rawShop);
|
|
13583
|
-
const resolved = await resolvePublishCredential(
|
|
13761
|
+
const resolved = await resolvePublishCredential(info9, req.deps);
|
|
13584
13762
|
if (!resolved) {
|
|
13585
13763
|
throw new Error("No Shopify access token found. Connect Shopify (or set SHOPIFY_ADMIN_TOKEN) first.");
|
|
13586
13764
|
}
|
|
@@ -13631,9 +13809,9 @@ function userErrorsToMessage(result) {
|
|
|
13631
13809
|
return errs?.length ? errs[0].message : null;
|
|
13632
13810
|
}
|
|
13633
13811
|
var shopifyProvider = {
|
|
13634
|
-
info:
|
|
13812
|
+
info: info9,
|
|
13635
13813
|
async authenticate(req) {
|
|
13636
|
-
const resolved = await resolvePublishCredential(
|
|
13814
|
+
const resolved = await resolvePublishCredential(info9, req.deps);
|
|
13637
13815
|
if (!resolved) {
|
|
13638
13816
|
return {
|
|
13639
13817
|
ok: false,
|
|
@@ -13769,14 +13947,14 @@ function publishProviderFor(id) {
|
|
|
13769
13947
|
// src/lib/publish/run-publish.ts
|
|
13770
13948
|
import { readdir as readdir7, stat as stat10 } from "node:fs/promises";
|
|
13771
13949
|
import path25 from "node:path";
|
|
13772
|
-
function resolvePublishFormat(
|
|
13773
|
-
if (
|
|
13950
|
+
function resolvePublishFormat(info10, providerConfig) {
|
|
13951
|
+
if (info10.formats && info10.formats.length > 0) {
|
|
13774
13952
|
const configured = typeof providerConfig.format === "string" ? providerConfig.format.trim() : "";
|
|
13775
|
-
if (configured &&
|
|
13953
|
+
if (configured && info10.formats.includes(configured)) {
|
|
13776
13954
|
return configured;
|
|
13777
13955
|
}
|
|
13778
13956
|
}
|
|
13779
|
-
return
|
|
13957
|
+
return info10.format;
|
|
13780
13958
|
}
|
|
13781
13959
|
async function resolvePublishRequest(options, deps) {
|
|
13782
13960
|
const provider = publishProviderFor(options.providerId);
|
|
@@ -13926,17 +14104,17 @@ function overlayStore(inner, key, candidate) {
|
|
|
13926
14104
|
}
|
|
13927
14105
|
async function connectPublishProvider(options, deps) {
|
|
13928
14106
|
const provider = publishProviderFor(options.providerId);
|
|
13929
|
-
const
|
|
13930
|
-
if (!
|
|
13931
|
-
throw new Error(`${
|
|
14107
|
+
const info10 = provider.info;
|
|
14108
|
+
if (!info10.credential.required) {
|
|
14109
|
+
throw new Error(`${info10.label} needs no API key — just publish when you're ready.`);
|
|
13932
14110
|
}
|
|
13933
|
-
if (
|
|
13934
|
-
throw new Error(`${
|
|
14111
|
+
if (info10.credential.connect === "oauth") {
|
|
14112
|
+
throw new Error(`${info10.label} connects through your browser, not a pasted key — run ` + `"gutterpress publish --provider ${info10.id} --connect" or use the desktop app's Connect button.`);
|
|
13935
14113
|
}
|
|
13936
14114
|
const token = options.token.trim();
|
|
13937
14115
|
if (!token)
|
|
13938
14116
|
throw new Error("Paste an API key first.");
|
|
13939
|
-
const host =
|
|
14117
|
+
const host = info10.credential.host;
|
|
13940
14118
|
const account = (options.account ?? "").trim();
|
|
13941
14119
|
const key = publishCredentialKey(host, account);
|
|
13942
14120
|
const candidate = {
|
|
@@ -13944,12 +14122,12 @@ async function connectPublishProvider(options, deps) {
|
|
|
13944
14122
|
kind: "token",
|
|
13945
14123
|
token,
|
|
13946
14124
|
...account ? { username: account } : {},
|
|
13947
|
-
label: account ||
|
|
14125
|
+
label: account || info10.label,
|
|
13948
14126
|
createdAt: Date.now()
|
|
13949
14127
|
};
|
|
13950
14128
|
const req = await resolvePublishRequest({
|
|
13951
14129
|
projectDir: options.projectDir,
|
|
13952
|
-
providerId:
|
|
14130
|
+
providerId: info10.id,
|
|
13953
14131
|
...options.manifestPath ? { manifestPath: options.manifestPath } : {}
|
|
13954
14132
|
}, deps);
|
|
13955
14133
|
const trialDeps = {
|
|
@@ -13960,10 +14138,10 @@ async function connectPublishProvider(options, deps) {
|
|
|
13960
14138
|
};
|
|
13961
14139
|
const auth = await provider.authenticate({ ...req, deps: trialDeps });
|
|
13962
14140
|
if (!auth.ok) {
|
|
13963
|
-
throw new Error(auth.message ?? `${
|
|
14141
|
+
throw new Error(auth.message ?? `${info10.label} didn't accept that key.`);
|
|
13964
14142
|
}
|
|
13965
14143
|
await deps.tokenStore.set(key, candidate);
|
|
13966
|
-
return { connected: true, providerId:
|
|
14144
|
+
return { connected: true, providerId: info10.id };
|
|
13967
14145
|
}
|
|
13968
14146
|
async function disconnectPublishCredential(key, deps, options = {}) {
|
|
13969
14147
|
const existing = await deps.tokenStore.get(key);
|
|
@@ -14093,7 +14271,7 @@ import git8 from "isomorphic-git";
|
|
|
14093
14271
|
// src/lib/remote-auth/scorched-earth.ts
|
|
14094
14272
|
import git9 from "isomorphic-git";
|
|
14095
14273
|
// src/lib/publish/selections.ts
|
|
14096
|
-
import { chmod as chmod2, mkdir as mkdir13, readFile as
|
|
14274
|
+
import { chmod as chmod2, mkdir as mkdir13, readFile as readFile32, writeFile as writeFile12 } from "node:fs/promises";
|
|
14097
14275
|
import path26 from "node:path";
|
|
14098
14276
|
class PublishSelectionsStore {
|
|
14099
14277
|
filePath;
|
|
@@ -14103,7 +14281,7 @@ class PublishSelectionsStore {
|
|
|
14103
14281
|
}
|
|
14104
14282
|
async read() {
|
|
14105
14283
|
try {
|
|
14106
|
-
const raw = await
|
|
14284
|
+
const raw = await readFile32(this.filePath, "utf8");
|
|
14107
14285
|
const parsed = JSON.parse(raw);
|
|
14108
14286
|
if (parsed && typeof parsed === "object") {
|
|
14109
14287
|
return {
|