@yojahny/wp-design-library 1.0.0 → 1.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yojahny/wp-design-library",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Design reference corpus and MCP server for claude-wp-builder",
5
5
  "repository": {
6
6
  "type": "git",
package/src/mcp/http.mjs CHANGED
@@ -23,8 +23,23 @@ async function handle(req, res, p, token, embedder) {
23
23
  if (db.vec && db.prepare('SELECT count(*) c FROM vec_text').get().c > 0) arms = ['fts', 'vec'];
24
24
  db.close();
25
25
  } catch {}
26
+ // A host with a full index and no reachable artifacts serves 31 grey boxes and reported
27
+ // ok:true, which is how a missing R2_PUBLIC_BASE_URL stayed invisible through two rounds of
28
+ // "it still is not showing images". Name it here, where anyone can see it without log access.
29
+ // It is not part of `ok`: the MCP API answers perfectly well on text alone, and failing the
30
+ // health check would take a working server down over missing pictures.
31
+ // "configured", never "ok": this reads the environment, it does not fetch an object, so it
32
+ // cannot claim the bucket is reachable. It catches the failure actually hit — the variable
33
+ // being absent — and says only what it checked.
34
+ let artifacts;
35
+ try {
36
+ const { readConfig } = await import('../r2.mjs');
37
+ artifacts = `configured: ${readConfig().base}`;
38
+ } catch (err) {
39
+ artifacts = `unconfigured: ${err.message}`;
40
+ }
26
41
  res.writeHead(200, { 'content-type': 'application/json' });
27
- return res.end(JSON.stringify({ ok: true, entries, arms, version: p.version }));
42
+ return res.end(JSON.stringify({ ok: true, entries, arms, artifacts, version: p.version }));
28
43
  }
29
44
  if (url.pathname === '/mcp') {
30
45
  if (!authorized(req.headers.authorization, token)) { res.writeHead(401); return res.end('unauthorized'); }
package/src/ui/build.mjs CHANGED
@@ -156,6 +156,7 @@ export async function buildUi({ indexPath, entriesDir, outDir, previewMediaDir,
156
156
  fs.mkdirSync(path.join(outDir, 'thumbnails'), { recursive: true });
157
157
 
158
158
  const rows = [];
159
+ const unresolved = [];
159
160
  for (const dir of listEntries(entriesDir)) {
160
161
  let entry;
161
162
  try { entry = parseEntry(dir); } catch (err) { process.stderr.write(`ui: skipping ${path.basename(dir)}: ${err.message}\n`); continue; }
@@ -169,11 +170,14 @@ export async function buildUi({ indexPath, entriesDir, outDir, previewMediaDir,
169
170
  const strip = (entry.fm.captured?.captures?.[0]?.artifacts ?? []).find((a) => a?.key?.endsWith('/strip.webp'));
170
171
  // One unreachable object must not take the gallery down with it: the entry renders without a
171
172
  // thumbnail, exactly as a draft does, and the build continues.
172
- if (strip) { try { stripSrc = await artifactFile(p, strip); } catch { stripSrc = null; } }
173
+ // Degrading silently is what made a misconfigured host indistinguishable from a broken one:
174
+ // a gallery of 31 grey boxes, `ok: true` from healthz, and not one line saying why. Swallow
175
+ // the failure, still — but count it and keep the first reason.
176
+ if (strip) { try { stripSrc = await artifactFile(p, strip); } catch (err) { stripSrc = null; unresolved.push(err); } }
173
177
  // Same treatment for measure.json: a record to resolve, never a file next to entry.md.
174
178
  let measureSrc = null;
175
179
  const measured = (entry.fm.captured?.captures?.[0]?.artifacts ?? []).find((a) => a?.key?.endsWith('/measure.json'));
176
- if (measured) { try { measureSrc = await artifactFile(p, measured); } catch { measureSrc = null; } }
180
+ if (measured) { try { measureSrc = await artifactFile(p, measured); } catch (err) { measureSrc = null; unresolved.push(err); } }
177
181
  const hasStrip = stripSrc !== null;
178
182
  if (hasStrip) {
179
183
  fs.copyFileSync(stripSrc, path.join(outDir, 'strips', `${slug}.webp`));
@@ -212,5 +216,11 @@ export async function buildUi({ indexPath, entriesDir, outDir, previewMediaDir,
212
216
  fs.writeFileSync(path.join(outDir, 'index.html'), render(tpl, { FILTERS: filters, GRID: rows.map(card).join('\n'), NOTICE: notice }));
213
217
  fs.copyFileSync(path.join(DIR, 'app.js'), path.join(outDir, 'app.js'));
214
218
 
215
- return { entries: rows.length - drafts, drafts };
219
+ // One line, not one per artifact: 31 entries failing for the same missing variable is one
220
+ // fact, and printing it 62 times would bury it. The first reason is the one that matters —
221
+ // they are identical when the cause is configuration, which is the case worth naming.
222
+ if (unresolved.length) {
223
+ process.stderr.write(`ui: ${unresolved.length} artifact(s) could not be resolved, so those entries render without a thumbnail; first reason: ${unresolved[0].message}\n`);
224
+ }
225
+ return { entries: rows.length - drafts, drafts, unresolved: unresolved.length };
216
226
  }