@koda-sl/baker-cli 0.290.1 → 0.290.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{chunk-V7BIRLPU.js → chunk-NNQDWCFG.js} +57 -11
- package/dist/chunk-NNQDWCFG.js.map +1 -0
- package/dist/cli.js +19 -5
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-V7BIRLPU.js.map +0 -1
package/README.md
CHANGED
|
@@ -5870,6 +5870,8 @@ baker landing critique spring-offer summer-offer claude # score three landings
|
|
|
5870
5870
|
|
|
5871
5871
|
- **Positioning integrity is scored too, and it is the one copy rule that blocks on a single sentence.** `competitor-concession` fires when the page ranks somebody else above the client, or says the client does not lead at what the page sells ("su constructor es mejor que el nuestro", "we're not the best"). Every other prose rule waits for a cluster because one AI tell proves nothing; this one is the whole defect on its own — the page is paid traffic, and it is arguing the visitor should leave. Two shapes that read identically are exempt by design: a concession about **price**, in either direction ("we are not the cheapest / the most expensive, and here is why" is premium positioning), and a superlative flipped by a negation ("ningún equipo trabaja más rápido que el nuestro"), which is the strongest claim *for* the client. Its warn-tier sibling `competitor-named-in-comparison` fires when a name from `src/content/competitors/` appears in a comparative sentence outside a `<table>` — a comparison table is a section the landing skill teaches, but naming a rival anywhere else on paid traffic is the client's call, not the build's. Spanish, Portuguese and English.
|
|
5872
5872
|
|
|
5873
|
+
- **Comments are not markup, and no rule reads one.** Astro expression comments (`{/* … */}`) and HTML comments (`<!-- … -->`) are blanked before detection, offsets preserved so `file:line` stays exact. A shared scaffold component explaining its `preconnect` in prose that spelled `<img>` twice was read as two broken images — block-tier — so every landing rendering a video failed the publish gate on a file the client cannot edit. Comment prose is also where a component's reasoning lives, em-dashes and buzzwords included, so reading it inflated the count-threshold tells too.
|
|
5874
|
+
|
|
5873
5875
|
- **Copy held in component frontmatter is read like markup copy.** Astro landings keep FAQ pairs, testimonial quotes, feature cards and pricing rows as a `const items = [{ q, a }]` array above the `---` fence and render them in a `.map()`, so a critic that blanked the whole block was blind to a large share of every page's actual words. Prose string literals in that block now feed every copy rule; imports, class strings, URLs and config do not, and the bar is deliberately high in that direction — a Tailwind class list in front of the copy rules is worse than one missed tell.
|
|
5874
5876
|
|
|
5875
5877
|
Output is the standard envelope `{ ok, data, hints }` with `data = { advisory, slug, overall, counts, dimensions, findings }`. `dimensions` scores seven design families (typography, color, borders_depth, motion, spacing, copy, integrity) 0–1 (higher is better); `counts` is the block/warn/advisory tally.
|
|
@@ -6022,9 +6022,8 @@ async function ingestImageUrl(url, ctx) {
|
|
|
6022
6022
|
} catch (e) {
|
|
6023
6023
|
throw localExecError(ctx, `${url}: ${e.message}`);
|
|
6024
6024
|
}
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
}
|
|
6025
|
+
const note = normalizationNote(normalized);
|
|
6026
|
+
if (note) ctx.log(`ingest: ${url} ${note}`);
|
|
6028
6027
|
return uploadAndIngest({
|
|
6029
6028
|
bytes: normalized.bytes,
|
|
6030
6029
|
kind: "image",
|
|
@@ -6134,23 +6133,69 @@ async function rasterizeSvgToPng(bytes) {
|
|
|
6134
6133
|
return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();
|
|
6135
6134
|
}
|
|
6136
6135
|
var MODEL_SAFE_IMAGE_MIMES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
|
|
6137
|
-
|
|
6136
|
+
var MAX_MODEL_REFERENCE_BYTES = 30 * 1024 * 1024;
|
|
6137
|
+
async function fitWithinReferenceLimit(bytes, mime, maxBytes) {
|
|
6138
|
+
if (bytes.byteLength <= maxBytes) return { bytes, mime };
|
|
6139
|
+
const { default: sharp } = await import("sharp");
|
|
6140
|
+
const read = () => sharp(bytes, { animated: true });
|
|
6141
|
+
const meta = await read().metadata();
|
|
6142
|
+
const animated = (meta.pages ?? 1) > 1;
|
|
6143
|
+
const keepAlpha = meta.hasAlpha === true;
|
|
6144
|
+
const width = meta.width ?? 0;
|
|
6145
|
+
const encode = async (scale2) => {
|
|
6146
|
+
let pipeline2 = read();
|
|
6147
|
+
if (scale2 < 1 && width > 0) pipeline2 = pipeline2.resize({ width: Math.max(256, Math.round(width * scale2)) });
|
|
6148
|
+
if (animated && mime === "image/gif") return { bytes: await pipeline2.gif().toBuffer(), mime: "image/gif" };
|
|
6149
|
+
if (animated || keepAlpha) return { bytes: await pipeline2.webp({ quality: 90 }).toBuffer(), mime: "image/webp" };
|
|
6150
|
+
return { bytes: await pipeline2.jpeg({ quality: 90, mozjpeg: true }).toBuffer(), mime: "image/jpeg" };
|
|
6151
|
+
};
|
|
6152
|
+
let out = await encode(1);
|
|
6153
|
+
let scale = 1;
|
|
6154
|
+
for (let pass = 0; pass < 4 && out.bytes.byteLength > maxBytes; pass++) {
|
|
6155
|
+
scale *= Math.min(0.9, Math.sqrt(maxBytes / out.bytes.byteLength));
|
|
6156
|
+
out = await encode(scale);
|
|
6157
|
+
}
|
|
6158
|
+
return out;
|
|
6159
|
+
}
|
|
6160
|
+
async function toModelSafeImage(bytes, opts) {
|
|
6161
|
+
const maxBytes = opts?.maxBytes ?? MAX_MODEL_REFERENCE_BYTES;
|
|
6162
|
+
const fit = async (candidate, mime, rasterizedFrom) => {
|
|
6163
|
+
const fitted = await fitWithinReferenceLimit(candidate, mime, maxBytes);
|
|
6164
|
+
return {
|
|
6165
|
+
bytes: fitted.bytes,
|
|
6166
|
+
mime: fitted.mime,
|
|
6167
|
+
...rasterizedFrom ? { rasterizedFrom } : {},
|
|
6168
|
+
...fitted.bytes === candidate ? {} : { shrunkFromBytes: candidate.byteLength }
|
|
6169
|
+
};
|
|
6170
|
+
};
|
|
6138
6171
|
const safe = sniffImageMime(bytes);
|
|
6139
6172
|
if (safe && MODEL_SAFE_IMAGE_MIMES.has(safe)) {
|
|
6140
|
-
return
|
|
6173
|
+
return await fit(bytes, safe);
|
|
6141
6174
|
}
|
|
6142
6175
|
if (sniffSvg(bytes)) {
|
|
6143
|
-
return
|
|
6176
|
+
return await fit(await rasterizeSvgToPng(bytes), "image/png", "svg");
|
|
6144
6177
|
}
|
|
6145
6178
|
const { default: sharp } = await import("sharp");
|
|
6179
|
+
let png;
|
|
6180
|
+
let format;
|
|
6146
6181
|
try {
|
|
6147
6182
|
const img = sharp(bytes);
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
return { bytes: png, mime: "image/png", rasterizedFrom: format ?? "unknown" };
|
|
6183
|
+
format = (await img.metadata()).format ?? "unknown";
|
|
6184
|
+
png = await img.png({ force: true }).toBuffer();
|
|
6151
6185
|
} catch (e) {
|
|
6152
6186
|
throw new Error(`bytes are not a decodable image (${e.message})`);
|
|
6153
6187
|
}
|
|
6188
|
+
return await fit(png, "image/png", format);
|
|
6189
|
+
}
|
|
6190
|
+
function normalizationNote(image) {
|
|
6191
|
+
const parts = [];
|
|
6192
|
+
if (image.rasterizedFrom) parts.push(`normalized ${image.rasterizedFrom} -> ${image.mime}`);
|
|
6193
|
+
if (image.shrunkFromBytes !== void 0) {
|
|
6194
|
+
parts.push(
|
|
6195
|
+
`fitted ${image.shrunkFromBytes}B -> ${image.bytes.length}B as ${image.mime} (providers refuse a reference over ${MAX_MODEL_REFERENCE_BYTES}B)`
|
|
6196
|
+
);
|
|
6197
|
+
}
|
|
6198
|
+
return parts.length > 0 ? parts.join("; ") : null;
|
|
6154
6199
|
}
|
|
6155
6200
|
function hasAscii(buf, offset, sig) {
|
|
6156
6201
|
return buf.length >= offset + sig.length && buf.toString("ascii", offset, offset + sig.length) === sig;
|
|
@@ -6280,7 +6325,8 @@ async function execLocalFile(params, ctx) {
|
|
|
6280
6325
|
outBytes = normalized.bytes;
|
|
6281
6326
|
outMime = normalized.mime;
|
|
6282
6327
|
rasterizedFrom = normalized.rasterizedFrom;
|
|
6283
|
-
|
|
6328
|
+
const note = normalizationNote(normalized);
|
|
6329
|
+
if (note) ctx.log(`ingest: ${note}`);
|
|
6284
6330
|
}
|
|
6285
6331
|
const durationMs = probeVideoDurationMs(params.expect, outBytes, ctx);
|
|
6286
6332
|
const ref = await uploadAndIngest({
|
|
@@ -9153,4 +9199,4 @@ export {
|
|
|
9153
9199
|
defaultRegistry,
|
|
9154
9200
|
createEngineFromEnv
|
|
9155
9201
|
};
|
|
9156
|
-
//# sourceMappingURL=chunk-
|
|
9202
|
+
//# sourceMappingURL=chunk-NNQDWCFG.js.map
|