@tokenoftrust/cli 2.2.4 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/validate.mjs +42 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/cli",
3
- "version": "2.2.4",
3
+ "version": "2.2.5",
4
4
  "description": "Token of Trust developer CLI — clone a tenant store, run it locally with save→reload, and submit it for preview. Installs the `tot` command.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Token of Trust",
package/src/validate.mjs CHANGED
@@ -653,6 +653,24 @@ export const TRACKING_ID_STEP_NAME = "assign stable tracking ids";
653
653
  export const CTA_MISSING_ID_FIX =
654
654
  "assign stable tracking ids: GET/POST /api/admin/tracking-writeback proposes a data-tot-el for every element that's missing one, as a reviewable candidate PR (GET first for a dry-run preview) — or run `tot validate` to see the full list before you submit.";
655
655
 
656
+ // `public/**` is git-tracked and wholesale-ingested into R2 on every publish
657
+ // with no content-hash dedup (ingestAssetVersion re-copies every file under a
658
+ // new version key, changed or not) — see docs/versioned-assets-contract.md.
659
+ // A large binary there works, but it compounds: every future publish re-pays
660
+ // its storage cost, and the tenant's Gitea repo keeps that blob permanently.
661
+ // WARN only — never blocks a submit. Idea tracked for a built-in fix (auto
662
+ // relocation to a deduped, content-addressed store): fb-1790956035713-snpovm.
663
+ export const LARGE_PUBLIC_ASSET_BYTES = 100 * 1024;
664
+ export const LARGE_PUBLIC_ASSET_IDEA_ID = "fb-1790956035713-snpovm";
665
+ export const LARGE_PUBLIC_ASSET_FIX =
666
+ `storing it under public/ will work, but every publish re-copies it into R2 and ` +
667
+ `your Gitea repo keeps it forever — on a tenant with many of these, expect ` +
668
+ `slower publishes and slower clones over time. Today, host it on a shared drive ` +
669
+ `you control and tell us the URL so we can pull it in and serve it from R2 directly, ` +
670
+ `rather than committing it to the tenant repo. We're tracking a built-in fix ` +
671
+ `(automatic relocation to deduped storage, idea ${LARGE_PUBLIC_ASSET_IDEA_ID}) — ` +
672
+ `vote for it if you'd like this handled for you sooner.`;
673
+
656
674
  /**
657
675
  * The advisory rules LOUD enough to call out by NAME at `tot validate` / `tot
658
676
  * submit` / `tot preview` — none of them block, but each ships a page that
@@ -682,6 +700,11 @@ export const LOUD_ADVISORY_RULES = [
682
700
  `actionable element with no governed identity (data-tot-el) — needs to ${TRACKING_ID_STEP_NAME}`,
683
701
  `It renders and works fine, but it's invisible to interaction tracking — nothing about it is ever recorded. ${CTA_MISSING_ID_FIX}`,
684
702
  ],
703
+ [
704
+ "large-public-asset",
705
+ `a public/ asset is ${(LARGE_PUBLIC_ASSET_BYTES / 1024).toFixed(0)}KB or larger`,
706
+ LARGE_PUBLIC_ASSET_FIX,
707
+ ],
685
708
  [
686
709
  "cta-id-drift",
687
710
  "a previously-declared data-tot-el changed value",
@@ -1208,6 +1231,9 @@ export function validateTenant(tenantDir, opts = {}) {
1208
1231
  }
1209
1232
  }
1210
1233
 
1234
+ // large public assets — advisory (never blocks); see LARGE_PUBLIC_ASSET_FIX.
1235
+ findings.push(...findLargePublicAssets(publicDir, rel));
1236
+
1211
1237
  const ok = !findings.some((f) => f.level === ERROR);
1212
1238
  return { ok, findings };
1213
1239
  }
@@ -1276,4 +1302,20 @@ function checkAsset(src, file, publicDir, tenantId) {
1276
1302
  return null;
1277
1303
  }
1278
1304
 
1305
+ /**
1306
+ * Every file under `public/**` at or above `LARGE_PUBLIC_ASSET_BYTES`. WARN only —
1307
+ * the file works fine, this is a cost/perf nudge toward the hosted-upload path
1308
+ * (see LARGE_PUBLIC_ASSET_FIX for why: no R2 dedup, permanent Gitea growth).
1309
+ */
1310
+ function findLargePublicAssets(publicDir, rel) {
1311
+ const out = [];
1312
+ for (const p of walk(publicDir, () => true)) {
1313
+ const size = statSync(p).size;
1314
+ if (size >= LARGE_PUBLIC_ASSET_BYTES) {
1315
+ out.push(mk(WARN, "large-public-asset", rel(p), `${(size / 1024).toFixed(0)}KB`));
1316
+ }
1317
+ }
1318
+ return out;
1319
+ }
1320
+
1279
1321
  const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");