@wix/himalaya-cli 0.1043.0 → 0.1045.0

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/cli.mjs CHANGED
@@ -1357,7 +1357,18 @@ function idSegOf(name) {
1357
1357
  function substituteIdentity(body, name, slug = name) {
1358
1358
  const idSeg = idSegOf(slug);
1359
1359
  const cmsPrefix = slug.replace(/[^a-z0-9]+/gi, "_").replace(/^_+|_+$/g, "").toLowerCase() || "app";
1360
- return body.replaceAll("__HIMI_APP_NAME__", name).replaceAll("__HIMI_APP_SLUG__", slug).replaceAll("__HIMI_APP_DOTID__", `com.example.${idSeg}`).replaceAll("__HIMI_APP_CMS_PREFIX__", cmsPrefix);
1360
+ const values = {
1361
+ __HIMI_APP_NAME__: name,
1362
+ __HIMI_APP_SLUG__: slug,
1363
+ __HIMI_APP_DOTID__: `com.example.${idSeg}`,
1364
+ __HIMI_APP_CMS_PREFIX__: cmsPrefix
1365
+ };
1366
+ return body.replace(sentinelPattern(), (m) => values[m] ?? m);
1367
+ }
1368
+ function sentinelPattern() {
1369
+ SENTINEL_RE ??= new RegExp(IDENTITY_SENTINELS.map((m) => escapeRe(m)).join("|"), "g");
1370
+ SENTINEL_RE.lastIndex = 0;
1371
+ return SENTINEL_RE;
1361
1372
  }
1362
1373
  function sentineledFiles(files) {
1363
1374
  return Object.entries(files).filter(([, body]) => IDENTITY_SENTINELS.some((s) => body.includes(s))).map(([rel]) => rel).sort();
@@ -1699,7 +1710,7 @@ A per-app \`npm i @wix/himalaya\` is optional and only buys your editor local ty
1699
1710
  );
1700
1711
  return finishScaffold({ dir, files, ...kept.length ? { kept: kept.sort() } : {} });
1701
1712
  }
1702
- var PLAN_FILES, PACKAGE_GITIGNORE, MODULE_SOURCE_FILE, TEMPLATES_DIR, SHOWCASE_TEMPLATES_DIR, SHOWCASE_TEMPLATE_NAMES, TEMPLATE_META_FILE, TEMPLATE_ICONS_DIR, HEX_COLOR, PALETTE_KEYS, IDENTITY_SENTINELS, escapeRe, NAME_FIELDS, IDENTITY_FILES, DOTID_RE, BINARY_EXTENSIONS, extOf;
1713
+ var PLAN_FILES, PACKAGE_GITIGNORE, MODULE_SOURCE_FILE, TEMPLATES_DIR, SHOWCASE_TEMPLATES_DIR, SHOWCASE_TEMPLATE_NAMES, TEMPLATE_META_FILE, TEMPLATE_ICONS_DIR, HEX_COLOR, PALETTE_KEYS, IDENTITY_SENTINELS, SENTINEL_RE, escapeRe, NAME_FIELDS, IDENTITY_FILES, DOTID_RE, BINARY_EXTENSIONS, extOf;
1703
1714
  var init_scaffold = __esm({
1704
1715
  "src/scaffold.ts"() {
1705
1716
  init_source_deny();
@@ -1728,6 +1739,181 @@ var init_scaffold = __esm({
1728
1739
  }
1729
1740
  });
1730
1741
 
1742
+ // ../../core/serve-storage/src/sha256.ts
1743
+ function sha256(input) {
1744
+ const bytes2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
1745
+ const bitLen = bytes2.length * 8;
1746
+ const padded = new Uint8Array(((bytes2.length + 9 + 63) / 64 | 0) * 64);
1747
+ padded.set(bytes2);
1748
+ padded[bytes2.length] = 128;
1749
+ const view = new DataView(padded.buffer);
1750
+ view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
1751
+ view.setUint32(padded.length - 4, bitLen >>> 0);
1752
+ let h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762;
1753
+ let h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225;
1754
+ const w = new Uint32Array(64);
1755
+ for (let off = 0; off < padded.length; off += 64) {
1756
+ for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
1757
+ for (let i = 16; i < 64; i++) {
1758
+ const a0 = w[i - 15];
1759
+ const a1 = w[i - 2];
1760
+ const s0 = (rotr(a0, 7) ^ rotr(a0, 18) ^ a0 >>> 3) >>> 0;
1761
+ const s1 = (rotr(a1, 17) ^ rotr(a1, 19) ^ a1 >>> 10) >>> 0;
1762
+ w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
1763
+ }
1764
+ let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
1765
+ for (let i = 0; i < 64; i++) {
1766
+ const S1 = (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) >>> 0;
1767
+ const ch = (e & f ^ ~e & g) >>> 0;
1768
+ const t1 = h + S1 + ch + K[i] + w[i] >>> 0;
1769
+ const S0 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) >>> 0;
1770
+ const maj = (a & b ^ a & c ^ b & c) >>> 0;
1771
+ const t2 = S0 + maj >>> 0;
1772
+ h = g;
1773
+ g = f;
1774
+ f = e;
1775
+ e = d + t1 >>> 0;
1776
+ d = c;
1777
+ c = b;
1778
+ b = a;
1779
+ a = t1 + t2 >>> 0;
1780
+ }
1781
+ h0 = h0 + a >>> 0;
1782
+ h1 = h1 + b >>> 0;
1783
+ h2 = h2 + c >>> 0;
1784
+ h3 = h3 + d >>> 0;
1785
+ h4 = h4 + e >>> 0;
1786
+ h5 = h5 + f >>> 0;
1787
+ h6 = h6 + g >>> 0;
1788
+ h7 = h7 + h >>> 0;
1789
+ }
1790
+ const out = new Uint8Array(32);
1791
+ const o = new DataView(out.buffer);
1792
+ o.setUint32(0, h0);
1793
+ o.setUint32(4, h1);
1794
+ o.setUint32(8, h2);
1795
+ o.setUint32(12, h3);
1796
+ o.setUint32(16, h4);
1797
+ o.setUint32(20, h5);
1798
+ o.setUint32(24, h6);
1799
+ o.setUint32(28, h7);
1800
+ return out;
1801
+ }
1802
+ function sha256Hex(input) {
1803
+ return hex(sha256(input));
1804
+ }
1805
+ async function sha256HexAsync(input) {
1806
+ const bytes2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
1807
+ return hex(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes2)));
1808
+ }
1809
+ function hex(bytes2) {
1810
+ let s = "";
1811
+ for (const b of bytes2) s += b.toString(16).padStart(2, "0");
1812
+ return s;
1813
+ }
1814
+ var K, rotr;
1815
+ var init_sha256 = __esm({
1816
+ "../../core/serve-storage/src/sha256.ts"() {
1817
+ K = new Uint32Array([
1818
+ 1116352408,
1819
+ 1899447441,
1820
+ 3049323471,
1821
+ 3921009573,
1822
+ 961987163,
1823
+ 1508970993,
1824
+ 2453635748,
1825
+ 2870763221,
1826
+ 3624381080,
1827
+ 310598401,
1828
+ 607225278,
1829
+ 1426881987,
1830
+ 1925078388,
1831
+ 2162078206,
1832
+ 2614888103,
1833
+ 3248222580,
1834
+ 3835390401,
1835
+ 4022224774,
1836
+ 264347078,
1837
+ 604807628,
1838
+ 770255983,
1839
+ 1249150122,
1840
+ 1555081692,
1841
+ 1996064986,
1842
+ 2554220882,
1843
+ 2821834349,
1844
+ 2952996808,
1845
+ 3210313671,
1846
+ 3336571891,
1847
+ 3584528711,
1848
+ 113926993,
1849
+ 338241895,
1850
+ 666307205,
1851
+ 773529912,
1852
+ 1294757372,
1853
+ 1396182291,
1854
+ 1695183700,
1855
+ 1986661051,
1856
+ 2177026350,
1857
+ 2456956037,
1858
+ 2730485921,
1859
+ 2820302411,
1860
+ 3259730800,
1861
+ 3345764771,
1862
+ 3516065817,
1863
+ 3600352804,
1864
+ 4094571909,
1865
+ 275423344,
1866
+ 430227734,
1867
+ 506948616,
1868
+ 659060556,
1869
+ 883997877,
1870
+ 958139571,
1871
+ 1322822218,
1872
+ 1537002063,
1873
+ 1747873779,
1874
+ 1955562222,
1875
+ 2024104815,
1876
+ 2227730452,
1877
+ 2361852424,
1878
+ 2428436474,
1879
+ 2756734187,
1880
+ 3204031479,
1881
+ 3329325298
1882
+ ]);
1883
+ rotr = (x, n) => (x >>> n | x << 32 - n) >>> 0;
1884
+ }
1885
+ });
1886
+
1887
+ // ../../core/serve-storage/src/contract.ts
1888
+ function strongEtag(body) {
1889
+ return strongEtagForSha256(sha256Hex(body));
1890
+ }
1891
+ function strongEtagForSha256(hex2) {
1892
+ return `"${hex2.slice(0, 32)}"`;
1893
+ }
1894
+ function isSafeSegment(name) {
1895
+ return !name.includes("/") && !name.includes("..") && name.length > 0;
1896
+ }
1897
+ function isValidAppId(app) {
1898
+ return typeof app === "string" && APP_ID_RE.test(app);
1899
+ }
1900
+ function assertAppId(app, where) {
1901
+ if (!isValidAppId(app)) {
1902
+ throw new Error(`invalid app id at ${where}: ${JSON.stringify(app)}`);
1903
+ }
1904
+ return app;
1905
+ }
1906
+ var BUNDLE_CACHE_CONTROL, DOC_CACHE_CONTROL, PINNED_CACHE_CONTROL, APP_ID_RE;
1907
+ var init_contract = __esm({
1908
+ "../../core/serve-storage/src/contract.ts"() {
1909
+ init_sha256();
1910
+ BUNDLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
1911
+ DOC_CACHE_CONTROL = "public, max-age=60, stale-while-revalidate=86400";
1912
+ PINNED_CACHE_CONTROL = "public, max-age=31536000, immutable";
1913
+ APP_ID_RE = /^(?=.{1,63}$)[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)?$/;
1914
+ }
1915
+ });
1916
+
1731
1917
  // ../../core/ts/src/app-link.ts
1732
1918
  function requirePreviewSegment(value, field) {
1733
1919
  if (!PLAYER_PREVIEW_SEGMENT_PATTERN.test(value)) {
@@ -1790,10 +1976,10 @@ function findingId(source, finding) {
1790
1976
  finding.path ?? finding.at,
1791
1977
  finding.ruleCode ?? finding.code ?? finding.invariant ?? finding.pass
1792
1978
  ].map(asText).join("\0");
1793
- return `${source}:${sha256Hex(identity).slice(0, 12)}`;
1979
+ return `${source}:${sha256Hex2(identity).slice(0, 12)}`;
1794
1980
  }
1795
1981
  function gapToFinding(gap) {
1796
- return { ...gap, id: `gap:${gap.kind}:${sha256Hex(gap.key).slice(0, 12)}` };
1982
+ return { ...gap, id: `gap:${gap.kind}:${sha256Hex2(gap.key).slice(0, 12)}` };
1797
1983
  }
1798
1984
  function normalizedFinding(source, finding) {
1799
1985
  return {
@@ -1866,7 +2052,7 @@ function stringField(value) {
1866
2052
  function numberField(value) {
1867
2053
  return typeof value === "number" ? value : void 0;
1868
2054
  }
1869
- function sha256Hex(value) {
2055
+ function sha256Hex2(value) {
1870
2056
  const bytes2 = new TextEncoder().encode(value);
1871
2057
  const K2 = [
1872
2058
  1116352408,
@@ -12759,7 +12945,7 @@ async function fetchUpstreamBytes(url) {
12759
12945
  return new Uint8Array(await res.arrayBuffer());
12760
12946
  }
12761
12947
  function writeCachedFace(bytes2) {
12762
- const hash = sha256Hex2(bytes2);
12948
+ const hash = sha256Hex3(bytes2);
12763
12949
  mkdirSync9(CACHE_DIR, { recursive: true });
12764
12950
  const tmp = join21(CACHE_DIR, `.${hash}.${process.pid}.${Date.now()}.tmp`);
12765
12951
  try {
@@ -12775,7 +12961,7 @@ async function ensureFaceBytes(face, opts = {}) {
12775
12961
  const local = faceSource(face.file, face.sha256);
12776
12962
  if (local) {
12777
12963
  const bytes2 = readFileSync18(local);
12778
- if (local !== join21(CACHE_DIR, face.sha256) || sha256Hex2(bytes2) === face.sha256) {
12964
+ if (local !== join21(CACHE_DIR, face.sha256) || sha256Hex3(bytes2) === face.sha256) {
12779
12965
  return { ok: true, bytes: bytes2, from: "local" };
12780
12966
  }
12781
12967
  }
@@ -12802,7 +12988,7 @@ async function fetchVerifyAndCache(url, sha2565, fetcher) {
12802
12988
  } catch (err) {
12803
12989
  return { ok: false, reason: `fetching ${url} failed: ${err.message}` };
12804
12990
  }
12805
- const got = sha256Hex2(bytes2);
12991
+ const got = sha256Hex3(bytes2);
12806
12992
  if (got !== sha2565) {
12807
12993
  return {
12808
12994
  ok: false,
@@ -12862,14 +13048,14 @@ async function catalogFaceBytes(sha2565, opts = {}) {
12862
13048
  }
12863
13049
  return void 0;
12864
13050
  }
12865
- var REPO_ROOT5, VENDORED_DIR, CACHE_DIR, sha256Hex2, RAW, UPSTREAM_DIR, FETCH_TIMEOUT_MS, FAILURE_MEMO_MS, recentFetches;
13051
+ var REPO_ROOT5, VENDORED_DIR, CACHE_DIR, sha256Hex3, RAW, UPSTREAM_DIR, FETCH_TIMEOUT_MS, FAILURE_MEMO_MS, recentFetches;
12866
13052
  var init_face_source = __esm({
12867
13053
  "../font-bake/face-source.ts"() {
12868
13054
  init_catalog();
12869
13055
  REPO_ROOT5 = fileURLToPath9(new URL("../..", import.meta.url));
12870
13056
  VENDORED_DIR = process.env.HIMI_FONTS_VENDORED_DIR || join21(REPO_ROOT5, "stdlib", "fonts", "vendored", "wix-madefor");
12871
13057
  CACHE_DIR = join21(process.env.XDG_CACHE_HOME || join21(homedir3(), ".cache"), "himi", "fonts");
12872
- sha256Hex2 = (bytes2) => createHash6("sha256").update(bytes2).digest("hex");
13058
+ sha256Hex3 = (bytes2) => createHash6("sha256").update(bytes2).digest("hex");
12873
13059
  RAW = ["https://", "raw.", "githubuser", "content.com"].join("");
12874
13060
  UPSTREAM_DIR = /^(ofl|apache|ufl)\/[a-z0-9_-]+$/;
12875
13061
  FETCH_TIMEOUT_MS = 2e4;
@@ -13011,8 +13197,17 @@ function hexToRgb2(hex2) {
13011
13197
  return [int >> 16 & 255, int >> 8 & 255, int & 255];
13012
13198
  }
13013
13199
  function rgba(color, alpha2) {
13014
- const rgb = hexToRgb2(color);
13015
- return rgb ? `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${clamp(alpha2)})` : color;
13200
+ let h = color.trim().replace(/^#/, "");
13201
+ let own3 = 1;
13202
+ if (/^[\da-f]{4}$/i.test(h) || /^[\da-f]{8}$/i.test(h)) {
13203
+ const a = h.length === 4 ? h[3] + h[3] : h.slice(6, 8);
13204
+ own3 = parseInt(a, 16) / 255;
13205
+ h = h.length === 4 ? h.slice(0, 3) : h.slice(0, 6);
13206
+ }
13207
+ const rgb = hexToRgb2(`#${h}`);
13208
+ if (!rgb) return color;
13209
+ const composed = own3 === 1 ? clamp(alpha2) : Math.round(clamp(alpha2) * own3 * 1e3) / 1e3;
13210
+ return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${composed})`;
13016
13211
  }
13017
13212
  function normalizeSpring(spring, durationMs) {
13018
13213
  return {
@@ -18383,151 +18578,6 @@ var init_wix_gateway = __esm({
18383
18578
  }
18384
18579
  });
18385
18580
 
18386
- // ../../core/serve-storage/src/sha256.ts
18387
- function sha2562(input) {
18388
- const bytes2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
18389
- const bitLen = bytes2.length * 8;
18390
- const padded = new Uint8Array(((bytes2.length + 9 + 63) / 64 | 0) * 64);
18391
- padded.set(bytes2);
18392
- padded[bytes2.length] = 128;
18393
- const view = new DataView(padded.buffer);
18394
- view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
18395
- view.setUint32(padded.length - 4, bitLen >>> 0);
18396
- let h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762;
18397
- let h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225;
18398
- const w = new Uint32Array(64);
18399
- for (let off = 0; off < padded.length; off += 64) {
18400
- for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4);
18401
- for (let i = 16; i < 64; i++) {
18402
- const a0 = w[i - 15];
18403
- const a1 = w[i - 2];
18404
- const s0 = (rotr(a0, 7) ^ rotr(a0, 18) ^ a0 >>> 3) >>> 0;
18405
- const s1 = (rotr(a1, 17) ^ rotr(a1, 19) ^ a1 >>> 10) >>> 0;
18406
- w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
18407
- }
18408
- let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
18409
- for (let i = 0; i < 64; i++) {
18410
- const S1 = (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) >>> 0;
18411
- const ch = (e & f ^ ~e & g) >>> 0;
18412
- const t1 = h + S1 + ch + K[i] + w[i] >>> 0;
18413
- const S0 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) >>> 0;
18414
- const maj = (a & b ^ a & c ^ b & c) >>> 0;
18415
- const t2 = S0 + maj >>> 0;
18416
- h = g;
18417
- g = f;
18418
- f = e;
18419
- e = d + t1 >>> 0;
18420
- d = c;
18421
- c = b;
18422
- b = a;
18423
- a = t1 + t2 >>> 0;
18424
- }
18425
- h0 = h0 + a >>> 0;
18426
- h1 = h1 + b >>> 0;
18427
- h2 = h2 + c >>> 0;
18428
- h3 = h3 + d >>> 0;
18429
- h4 = h4 + e >>> 0;
18430
- h5 = h5 + f >>> 0;
18431
- h6 = h6 + g >>> 0;
18432
- h7 = h7 + h >>> 0;
18433
- }
18434
- const out = new Uint8Array(32);
18435
- const o = new DataView(out.buffer);
18436
- o.setUint32(0, h0);
18437
- o.setUint32(4, h1);
18438
- o.setUint32(8, h2);
18439
- o.setUint32(12, h3);
18440
- o.setUint32(16, h4);
18441
- o.setUint32(20, h5);
18442
- o.setUint32(24, h6);
18443
- o.setUint32(28, h7);
18444
- return out;
18445
- }
18446
- function sha256Hex3(input) {
18447
- return hex(sha2562(input));
18448
- }
18449
- async function sha256HexAsync(input) {
18450
- const bytes2 = typeof input === "string" ? new TextEncoder().encode(input) : input;
18451
- return hex(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes2)));
18452
- }
18453
- function hex(bytes2) {
18454
- let s = "";
18455
- for (const b of bytes2) s += b.toString(16).padStart(2, "0");
18456
- return s;
18457
- }
18458
- var K, rotr;
18459
- var init_sha256 = __esm({
18460
- "../../core/serve-storage/src/sha256.ts"() {
18461
- K = new Uint32Array([
18462
- 1116352408,
18463
- 1899447441,
18464
- 3049323471,
18465
- 3921009573,
18466
- 961987163,
18467
- 1508970993,
18468
- 2453635748,
18469
- 2870763221,
18470
- 3624381080,
18471
- 310598401,
18472
- 607225278,
18473
- 1426881987,
18474
- 1925078388,
18475
- 2162078206,
18476
- 2614888103,
18477
- 3248222580,
18478
- 3835390401,
18479
- 4022224774,
18480
- 264347078,
18481
- 604807628,
18482
- 770255983,
18483
- 1249150122,
18484
- 1555081692,
18485
- 1996064986,
18486
- 2554220882,
18487
- 2821834349,
18488
- 2952996808,
18489
- 3210313671,
18490
- 3336571891,
18491
- 3584528711,
18492
- 113926993,
18493
- 338241895,
18494
- 666307205,
18495
- 773529912,
18496
- 1294757372,
18497
- 1396182291,
18498
- 1695183700,
18499
- 1986661051,
18500
- 2177026350,
18501
- 2456956037,
18502
- 2730485921,
18503
- 2820302411,
18504
- 3259730800,
18505
- 3345764771,
18506
- 3516065817,
18507
- 3600352804,
18508
- 4094571909,
18509
- 275423344,
18510
- 430227734,
18511
- 506948616,
18512
- 659060556,
18513
- 883997877,
18514
- 958139571,
18515
- 1322822218,
18516
- 1537002063,
18517
- 1747873779,
18518
- 1955562222,
18519
- 2024104815,
18520
- 2227730452,
18521
- 2361852424,
18522
- 2428436474,
18523
- 2756734187,
18524
- 3204031479,
18525
- 3329325298
18526
- ]);
18527
- rotr = (x, n) => (x >>> n | x << 32 - n) >>> 0;
18528
- }
18529
- });
18530
-
18531
18581
  // ../../core/serve-runtime/src/wix-bridge.ts
18532
18582
  function absoluteUrlRejection(path2) {
18533
18583
  let host = "";
@@ -18670,7 +18720,7 @@ var init_wix_bridge = __esm({
18670
18720
  * header with no session cookie falls back to itself — callers refuse that case before here. */
18671
18721
  key(msid, cookieHeader) {
18672
18722
  const session = /(?:^|;\s*)wixSession2=([^;]*)/.exec(cookieHeader)?.[1] ?? cookieHeader;
18673
- return sha256Hex3(`${msid}
18723
+ return sha256Hex(`${msid}
18674
18724
  ${session}`);
18675
18725
  }
18676
18726
  async instance(msid, cookieHeader, now = Date.now()) {
@@ -18745,7 +18795,7 @@ ${session}`);
18745
18795
  }
18746
18796
  cache = /* @__PURE__ */ new Map();
18747
18797
  key(cookieHeader, limit) {
18748
- return sha256Hex3(`${limit}
18798
+ return sha256Hex(`${limit}
18749
18799
  ${cookieHeader}`);
18750
18800
  }
18751
18801
  async sites(cookieHeader, limit, now = Date.now()) {
@@ -21250,7 +21300,6 @@ function shouldRevealError(args) {
21250
21300
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
21251
21301
  var init_validation = __esm({
21252
21302
  "../../core/ts/src/validation.ts"() {
21253
- "use strict";
21254
21303
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
21255
21304
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
21256
21305
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -24851,7 +24900,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
24851
24900
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
24852
24901
  var init_worker_subtree = __esm({
24853
24902
  "../../core/ts/src/worker-subtree.ts"() {
24854
- "use strict";
24855
24903
  init_worker_subtree_safe_types_generated();
24856
24904
  WORKER_SUBTREE_CEILINGS = Object.freeze({
24857
24905
  maxEncodedBytes: 65536,
@@ -24927,6 +24975,7 @@ function withTheme(ctx, theme) {
24927
24975
  const base = ctx.tokens;
24928
24976
  const colors = { ...base.colors ?? {} };
24929
24977
  if (theme.colors) for (const [k, v] of Object.entries(theme.colors)) colors[k] = v;
24978
+ if (theme.colors && "accentPrimary" in theme.colors && !("accentText" in theme.colors)) delete colors.accentText;
24930
24979
  const typography = { ...base.typography ?? {} };
24931
24980
  if (theme.typography) for (const [k, v] of Object.entries(theme.typography)) typography[k] = v;
24932
24981
  const kit = { ...base.kit ?? {} };
@@ -26104,6 +26153,9 @@ function actionClick(ctx, action) {
26104
26153
  ctx.onAction(resolveItemDeep2(action, ctx));
26105
26154
  };
26106
26155
  }
26156
+ function accentTextColor(ctx) {
26157
+ return ctx.color("accentText", ctx.color("accentPrimary", "#116DFF"));
26158
+ }
26107
26159
  function actionTextButton(ctx, action, label2, color, extra) {
26108
26160
  if (action == null) return null;
26109
26161
  const onClick = actionClick(ctx, action);
@@ -28994,7 +29046,7 @@ var init_renderNode = __esm({
28994
29046
  e?.stopPropagation?.();
28995
29047
  ctx.onAction(resolveItemDeep2(props.action, ctx));
28996
29048
  } : void 0;
28997
- const fg = primary ? C("textOnAccent", "#fff") : accent;
29049
+ const fg = primary ? C("textOnAccent", "#fff") : accentTextColor(ctx);
28998
29050
  const pillRadius = props.cornerRadius != null ? props.cornerRadius : ctx.radius("pill", 24);
28999
29051
  const vPad = props.iconOnly ? 11 : props.size === "sm" ? 8 : props.size === "lg" ? 16 : 13;
29000
29052
  const hPadBase = props.iconOnly ? 11 : props.size === "sm" ? 13 : props.size === "lg" ? 22 : 16;
@@ -29063,14 +29115,14 @@ var init_renderNode = __esm({
29063
29115
  fontSize: f.remSize,
29064
29116
  background: primary ? accent : "transparent",
29065
29117
  color: fg,
29066
- border: outlined ? `1px solid ${accent}` : "none",
29118
+ border: outlined ? `1px solid ${fg}` : "none",
29067
29119
  cursor: disabled ? "default" : "pointer",
29068
29120
  opacity: disabled ? 0.55 : 1,
29069
29121
  boxSizing: "border-box",
29070
29122
  ...wrap
29071
29123
  }, rowStretch, "fit-content"),
29072
29124
  children: [
29073
- props.loading && /* @__PURE__ */ jsx6("span", { style: { width: glyph, height: glyph, borderRadius: "50%", border: `2px solid ${primary ? "rgba(255,255,255,.4)" : "rgba(17,109,255,.35)"}`, borderTopColor: fg, animation: ctx.empty ? void 0 : "himalaya-spin .7s linear infinite" } }),
29125
+ props.loading && /* @__PURE__ */ jsx6("span", { style: { width: glyph, height: glyph, borderRadius: "50%", border: `2px solid ${rgba(fg, primary ? 0.4 : 0.35)}`, borderTopColor: fg, animation: ctx.empty ? void 0 : "himalaya-spin .7s linear infinite" } }),
29074
29126
  !props.loading && resolvedLeadingIcon && ic(String(resolvedLeadingIcon)),
29075
29127
  !props.iconOnly && (resolvedTitle != null && resolvedTitle !== "" ? String(resolvedTitle) : txt(props.title, ctx) || "Button"),
29076
29128
  !props.loading && !props.iconOnly && resolvedTrailingIcon && ic(String(resolvedTrailingIcon))
@@ -33288,36 +33340,6 @@ var init_errors = __esm({
33288
33340
  }
33289
33341
  });
33290
33342
 
33291
- // ../../core/serve-storage/src/contract.ts
33292
- function strongEtag(body) {
33293
- return strongEtagForSha256(sha256Hex3(body));
33294
- }
33295
- function strongEtagForSha256(hex2) {
33296
- return `"${hex2.slice(0, 32)}"`;
33297
- }
33298
- function isSafeSegment(name) {
33299
- return !name.includes("/") && !name.includes("..") && name.length > 0;
33300
- }
33301
- function isValidAppId(app) {
33302
- return typeof app === "string" && APP_ID_RE.test(app);
33303
- }
33304
- function assertAppId(app, where) {
33305
- if (!isValidAppId(app)) {
33306
- throw new Error(`invalid app id at ${where}: ${JSON.stringify(app)}`);
33307
- }
33308
- return app;
33309
- }
33310
- var BUNDLE_CACHE_CONTROL, DOC_CACHE_CONTROL, PINNED_CACHE_CONTROL, APP_ID_RE;
33311
- var init_contract = __esm({
33312
- "../../core/serve-storage/src/contract.ts"() {
33313
- init_sha256();
33314
- BUNDLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
33315
- DOC_CACHE_CONTROL = "public, max-age=60, stale-while-revalidate=86400";
33316
- PINNED_CACHE_CONTROL = "public, max-age=31536000, immutable";
33317
- APP_ID_RE = /^(?=.{1,63}$)[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)?$/;
33318
- }
33319
- });
33320
-
33321
33343
  // ../../core/serve-control/src/config.ts
33322
33344
  var config_exports2 = {};
33323
33345
  __export(config_exports2, {
@@ -49502,7 +49524,7 @@ function makeServeHandler(opts) {
49502
49524
  }
49503
49525
  const PINNED_MANIFEST_CACHE_CAP = 100;
49504
49526
  const pinnedManifestCache = /* @__PURE__ */ new Map();
49505
- const sha256hex = (b) => sha256Hex3(b);
49527
+ const sha256hex = (b) => sha256Hex(b);
49506
49528
  function resolvePin(request, url) {
49507
49529
  return request.headers.get("x-himalaya-release") || url.searchParams.get("release") || null;
49508
49530
  }
@@ -50819,21 +50841,21 @@ var init_preview_assets_generated = __esm({
50819
50841
  "../../packages/serve/src/preview-assets.generated.ts"() {
50820
50842
  init_preview_assets_format();
50821
50843
  PREVIEW_ASSET_MANIFEST = [
50822
- { key: "assets/index-i1HVt_7j.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1051636, sha256: "cfb282567ed9de1491cb9d91f16c329e6560a6809da79cca4be3b35bc5b4b161" },
50823
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1051644, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
50824
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1056254, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
50825
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1544838, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
50826
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1635762, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
50827
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1726746, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
50828
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1817726, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
50829
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1908598, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
50830
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1999578, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
50831
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2108214, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
50832
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2217298, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
50833
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2326346, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
50834
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2434046, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
50835
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2522154, length: 4947, sha256: "3c696c7f99affdf0305da31e2e6a6355a33d87ce9676a62ddea33babcd95f978" },
50836
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2527101, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
50844
+ { key: "assets/index-C8FaHVmV.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1052014, sha256: "607f0eff78c1f286d1d94f6d6c08868e2deec8d39ace8176b374f0117026998a" },
50845
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1052022, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
50846
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1056632, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
50847
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1545216, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
50848
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1636140, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
50849
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1727124, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
50850
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1818104, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
50851
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1908976, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
50852
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 1999956, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
50853
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2108592, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
50854
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2217676, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
50855
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2326724, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
50856
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2434424, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
50857
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2522532, length: 4947, sha256: "3fd26f396a5b472e603185fb99d5b905f37b98615b2bf95456e4e9b03ae05429" },
50858
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2527479, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
50837
50859
  ];
50838
50860
  }
50839
50861
  });
@@ -54478,6 +54500,35 @@ function tuneHighContrastAccent(brand, surface) {
54478
54500
  }
54479
54501
  return candidate;
54480
54502
  }
54503
+ function deriveAccentText(accent, surfaces) {
54504
+ const all = (typeof surfaces === "string" ? [surfaces] : [...surfaces]).map((s) => s.slice(0, 7));
54505
+ const page = all[0];
54506
+ const worst = (color) => Math.min(...all.map((s) => contrastRatio3(color, s)));
54507
+ const opaque = accent.length === 9 ? mix2(accent.slice(0, 7), page, channel2(accent, 7) / 255) : accent;
54508
+ if (worst(opaque) >= ACCENT_TEXT_MIN_CONTRAST) return opaque;
54509
+ const pole = worst("#FFFFFF") >= worst("#000000") ? "#FFFFFF" : "#000000";
54510
+ for (let step = 1; step <= 100; step++) {
54511
+ const candidate = mix2(pole, opaque, step / 100);
54512
+ if (worst(candidate) >= ACCENT_TEXT_MIN_CONTRAST) return candidate;
54513
+ }
54514
+ if (all.length > 1) {
54515
+ const side2 = (hex2) => relativeLuminance2(hex2) > MID_GREY_LUMINANCE;
54516
+ const sameSide = all.filter((s) => side2(s) === side2(page));
54517
+ return deriveAccentText(accent, sameSide.length < all.length ? sameSide : [page]);
54518
+ }
54519
+ return pole;
54520
+ }
54521
+ function textSurfacesOf(colors) {
54522
+ const out = { light: [], dark: [], highContrast: [] };
54523
+ for (const name of TEXT_BACKDROP_TOKENS) {
54524
+ const value = colors[name];
54525
+ for (const arm of ["light", "dark", "highContrast"]) {
54526
+ const hex2 = typeof value === "string" ? value : value && typeof value === "object" && !Array.isArray(value) ? value[arm] ?? value.light : void 0;
54527
+ if (typeof hex2 === "string" && hex2.startsWith("#") && hex2.length >= 7) out[arm].push(hex2);
54528
+ }
54529
+ }
54530
+ return out;
54531
+ }
54481
54532
  function stripBrandFromIdiom(tokens) {
54482
54533
  const idiom = tokens.idiom;
54483
54534
  if (!idiom || typeof idiom !== "object" || Array.isArray(idiom)) return;
@@ -54498,7 +54549,7 @@ function stripBrandFromIdiom(tokens) {
54498
54549
  }
54499
54550
  }
54500
54551
  }
54501
- function deriveAccentFamily(primary, secondary, surfaces) {
54552
+ function deriveAccentFamily(primary, secondary, surfaces, textSurfaces = {}) {
54502
54553
  const primaryDark = tuneDarkAccent(primary, surfaces.dark, surfaces.light);
54503
54554
  const secondaryDark = tuneDarkAccent(secondary, surfaces.dark, surfaces.light);
54504
54555
  const primaryHigh = tuneHighContrastAccent(primary, surfaces.highContrast);
@@ -54515,10 +54566,15 @@ function deriveAccentFamily(primary, secondary, surfaces) {
54515
54566
  light: textOnArm(primary),
54516
54567
  dark: textOnArm(primaryDark),
54517
54568
  highContrast: textOn(primaryHigh)
54569
+ },
54570
+ accentText: {
54571
+ light: deriveAccentText(primary, [surfaces.light, ...textSurfaces.light ?? []]),
54572
+ dark: deriveAccentText(primaryDark, [surfaces.dark, ...textSurfaces.dark ?? []]),
54573
+ highContrast: deriveAccentText(primaryHigh, [surfaces.highContrast, ...textSurfaces.highContrast ?? []])
54518
54574
  }
54519
54575
  };
54520
54576
  }
54521
- var ACCENT_SURFACE_MIN_DELTA, ACCENT_SURFACE_LIGHT_WEIGHT, SECONDARY_SHADE_WEIGHT, SECONDARY_MIN_LSTAR, DARK_ARM_MIN_LIFT, DARK_ARM_TARGET_CONTRAST, DARK_ARM_FLOOR_CONTRAST, DARK_ARM_LSTAR_CAP, HIGH_CONTRAST_TARGET, BRAND_COLOR_TOKENS, BRAND_GLOW_SHADOW;
54577
+ var ACCENT_SURFACE_MIN_DELTA, ACCENT_SURFACE_LIGHT_WEIGHT, SECONDARY_SHADE_WEIGHT, SECONDARY_MIN_LSTAR, DARK_ARM_MIN_LIFT, DARK_ARM_TARGET_CONTRAST, DARK_ARM_FLOOR_CONTRAST, DARK_ARM_LSTAR_CAP, HIGH_CONTRAST_TARGET, ACCENT_TEXT_MIN_CONTRAST, MID_GREY_LUMINANCE, TEXT_BACKDROP_TOKENS, BRAND_COLOR_TOKENS, BRAND_GLOW_SHADOW;
54522
54578
  var init_brand_color_math = __esm({
54523
54579
  "../../core/ts/src/brand-color-math.ts"() {
54524
54580
  ACCENT_SURFACE_MIN_DELTA = 1.08;
@@ -54530,7 +54586,10 @@ var init_brand_color_math = __esm({
54530
54586
  DARK_ARM_FLOOR_CONTRAST = 3;
54531
54587
  DARK_ARM_LSTAR_CAP = 80;
54532
54588
  HIGH_CONTRAST_TARGET = 8.5;
54533
- BRAND_COLOR_TOKENS = ["accentPrimary", "accentSecondary", "accentSurface", "textOnAccent"];
54589
+ ACCENT_TEXT_MIN_CONTRAST = 4.5;
54590
+ MID_GREY_LUMINANCE = 0.179;
54591
+ TEXT_BACKDROP_TOKENS = ["surfaceSecondary", "surfaceElevated", "surfaceSubtle", "surfaceTertiary"];
54592
+ BRAND_COLOR_TOKENS = ["accentPrimary", "accentSecondary", "accentSurface", "textOnAccent", "accentText"];
54534
54593
  BRAND_GLOW_SHADOW = "glow";
54535
54594
  }
54536
54595
  });
@@ -54566,7 +54625,9 @@ function withDerivedAccents(tokensOverlay, wst) {
54566
54625
  light: lightArm2(surfacePrimary) ?? "#FFFFFF",
54567
54626
  dark: armOf(surfacePrimary, "dark") ?? "#0D1117",
54568
54627
  highContrast: armOf(surfacePrimary, "highContrast") ?? "#000000"
54569
- });
54628
+ // #3275 — the crawl knows only the site's LIGHT surfaces; a light card judged against a dark
54629
+ // arm would fight that arm's page, and `--apply` re-derives every arm after the merge anyway.
54630
+ }, { light: textSurfacesOf(tokensOverlay.colors ?? {}).light });
54570
54631
  const keepCrawledLight = (derived, existing) => {
54571
54632
  const crawled = lightArm2(existing);
54572
54633
  return crawled ? { ...derived, light: crawled } : derived;
@@ -54575,6 +54636,7 @@ function withDerivedAccents(tokensOverlay, wst) {
54575
54636
  colors.accentSecondary = keepCrawledLight(family.accentSecondary, tokensOverlay.colors?.accentSecondary);
54576
54637
  colors.accentSurface = keepCrawledLight(family.accentSurface, tokensOverlay.colors?.accentSurface);
54577
54638
  colors.textOnAccent = keepCrawledLight(family.textOnAccent, tokensOverlay.colors?.textOnAccent);
54639
+ colors.accentText = keepCrawledLight(family.accentText, tokensOverlay.colors?.accentText);
54578
54640
  return { ...tokensOverlay, colors };
54579
54641
  }
54580
54642
  function mergedArm(colors, name, arm, fallback) {
@@ -54597,11 +54659,12 @@ function alignAccentsToSurfaces(mergedColors, overlayColors) {
54597
54659
  light: mergedArm(colors, "surfacePrimary", "light", "#FFFFFF"),
54598
54660
  dark: mergedArm(colors, "surfacePrimary", "dark", "#0D1117"),
54599
54661
  highContrast: mergedArm(colors, "surfacePrimary", "highContrast", "#000000")
54600
- });
54662
+ }, textSurfacesOf(colors));
54601
54663
  colors.accentPrimary = family.accentPrimary;
54602
54664
  colors.accentSecondary = family.accentSecondary;
54603
54665
  colors.accentSurface = family.accentSurface;
54604
54666
  colors.textOnAccent = family.textOnAccent;
54667
+ colors.accentText = family.accentText;
54605
54668
  return colors;
54606
54669
  }
54607
54670
  function deriveBranding(input) {
@@ -63214,6 +63277,7 @@ async function loadReleaseFromDir(app, dir) {
63214
63277
  // src/cli.ts
63215
63278
  init_scaffold();
63216
63279
  init_store();
63280
+ init_contract();
63217
63281
  init_app_link();
63218
63282
  init_himi_report();
63219
63283
 
@@ -63619,7 +63683,7 @@ function screenshotArtifactPath(contentDir2, hash) {
63619
63683
  return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
63620
63684
  }
63621
63685
  var GIT_HASH_MAX_BUFFER = 128 * 1024 * 1024;
63622
- var sha256 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
63686
+ var sha2562 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
63623
63687
  function canonical(value) {
63624
63688
  if (value === null || typeof value !== "object") return JSON.stringify(value);
63625
63689
  if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
@@ -63627,21 +63691,21 @@ function canonical(value) {
63627
63691
  return `{${Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${canonical(record2[key2])}`).join(",")}}`;
63628
63692
  }
63629
63693
  function releasePayloadHash(payload) {
63630
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha256(canonical(payload));
63694
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha2562(canonical(payload));
63631
63695
  const record2 = payload;
63632
63696
  const manifest = record2.manifest;
63633
- if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha256(canonical(payload));
63697
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha2562(canonical(payload));
63634
63698
  const manifestRecord = manifest;
63635
63699
  const widgets = manifestRecord.widgets;
63636
- if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha256(canonical(payload));
63637
- return sha256(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
63700
+ if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha2562(canonical(payload));
63701
+ return sha2562(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
63638
63702
  }
63639
63703
  function withoutSelfHash(attestation) {
63640
63704
  const { selfHash: _selfHash, ...body } = attestation;
63641
63705
  return body;
63642
63706
  }
63643
63707
  function selfHashFor(attestation) {
63644
- return sha256(canonical(withoutSelfHash(attestation)));
63708
+ return sha2562(canonical(withoutSelfHash(attestation)));
63645
63709
  }
63646
63710
  function repoRoot(contentDir2) {
63647
63711
  try {
@@ -63755,7 +63819,7 @@ function readRetainedAttestations(contentDir2) {
63755
63819
  for (const expected of Object.values(run2.screenshots)) {
63756
63820
  const path2 = screenshotArtifactPath(contentDir2, expected);
63757
63821
  if (!existsSync3(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename2(path2)}`);
63758
- else if (sha256(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
63822
+ else if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
63759
63823
  }
63760
63824
  }
63761
63825
  const runs = [];
@@ -63814,11 +63878,11 @@ function writeAttestation(input, opts = {}) {
63814
63878
  const previous = readLatestAttestation(contentDir2);
63815
63879
  const runId = `${(opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()}-${(opts.randomSuffix ?? (() => randomBytes(3).toString("hex")))()}`;
63816
63880
  const descriptorHashes = Object.fromEntries(
63817
- Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha256(bytes2)])
63881
+ Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha2562(bytes2)])
63818
63882
  );
63819
63883
  const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a], [b]) => a.localeCompare(b)).filter(([, path2]) => existsSync3(path2)).map(([screen, path2]) => {
63820
63884
  const bytes2 = readFileSync5(path2);
63821
- return { screen, bytes: bytes2, hash: sha256(bytes2) };
63885
+ return { screen, bytes: bytes2, hash: sha2562(bytes2) };
63822
63886
  });
63823
63887
  const screenshots = Object.fromEntries(screenshotEntries.map(({ screen, hash }) => [screen, hash]));
63824
63888
  for (const { bytes: bytes2, hash } of screenshotEntries) {
@@ -63941,7 +64005,7 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
63941
64005
  errors.push(`missing baked descriptor for ${screen}`);
63942
64006
  continue;
63943
64007
  }
63944
- if (sha256(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
64008
+ if (sha2562(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
63945
64009
  }
63946
64010
  for (const [screen, expected] of Object.entries(attestation.screenshots)) {
63947
64011
  const path2 = screenshotArtifactPath(contentDir2, expected);
@@ -63949,7 +64013,7 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
63949
64013
  errors.push(`missing screenshot for ${screen}: ${basename2(path2)}`);
63950
64014
  continue;
63951
64015
  }
63952
- if (sha256(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
64016
+ if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
63953
64017
  }
63954
64018
  return { ok: errors.length === 0, errors };
63955
64019
  }
@@ -67584,8 +67648,26 @@ function shellShaDivergenceWarnings(jobs) {
67584
67648
  ([release, built]) => `\u26A0 this build compiles the native shell at ${built.slice(0, 12)}, not release ${release.slice(0, 12)} \u2014 the release was pushed from a branch whose HEAD is not on the default branch, so the Mac pool checks out the nearest default-branch ancestor. A native-shell or tools/mobile-dist/** change on that branch is NOT in this binary even if the job reports SUCCESS; re-push from a checkout whose HEAD is on origin/master to validate it.`
67585
67649
  );
67586
67650
  }
67651
+ async function templatePublishName(sourceDir) {
67652
+ const id = await appId(sourceDir);
67653
+ return IDENTITY_SENTINELS.some((s) => id.includes(s)) ? basename14(sourceDir) : id;
67654
+ }
67655
+ function templateNameError(name, from) {
67656
+ if (name.startsWith(TEMPLATE_APP_PREFIX)) {
67657
+ const stripped = name.slice(TEMPLATE_APP_PREFIX.length);
67658
+ const hint = stripped && !stripped.startsWith(TEMPLATE_APP_PREFIX) && !templateNameError(stripped, from) ? `pass --name ${stripped}` : "pass --name <kebab-id>";
67659
+ return `template name "${name}" (${from}) already starts with "${TEMPLATE_APP_PREFIX}", which would publish to "${TEMPLATE_APP_PREFIX}${name}"; ${hint}`;
67660
+ }
67661
+ if (!TEMPLATE_NAME_PATTERN.test(name)) {
67662
+ return `template name "${name}" (${from}) is not a lowercase kebab id (lowercase letters and digits, hyphens only between them); pass --name <kebab-id> (it becomes ${TEMPLATE_APP_PREFIX}<name>)`;
67663
+ }
67664
+ const composed = TEMPLATE_APP_PREFIX + name;
67665
+ return isValidAppId(composed) ? null : `template name "${name}" (${from}) makes app id "${composed}", which is over the serve plane's app-id length limit; pass a shorter --name`;
67666
+ }
67587
67667
  function materializeForPreview(appDir2, slug, displayName) {
67588
67668
  if (!isTemplateSourceTree(appDir2)) return appDir2;
67669
+ const slugError = templateNameError(slug, "preview slug");
67670
+ if (slugError) throw Object.assign(new Error(slugError), { exitCode: 2 });
67589
67671
  const files = readTemplateFiles(appDir2);
67590
67672
  const forBuild = Object.fromEntries(
67591
67673
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
@@ -67613,7 +67695,12 @@ function materializeForPreview(appDir2, slug, displayName) {
67613
67695
  const dest = join60(root, `${slug}-${process.pid}`);
67614
67696
  rmSync14(dest, { recursive: true, force: true });
67615
67697
  mkdirSync31(dest, { recursive: true });
67616
- materializeSource(forBuild, displayName, dest, slug);
67698
+ try {
67699
+ materializeSource(forBuild, displayName, dest, slug);
67700
+ } catch (e) {
67701
+ rmSync14(dest, { recursive: true, force: true });
67702
+ throw e;
67703
+ }
67617
67704
  return dest;
67618
67705
  }
67619
67706
  async function appId(dir) {
@@ -68479,253 +68566,281 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68479
68566
  return outcome.code;
68480
68567
  }
68481
68568
  if (sub === "publish") {
68482
- const appDir2 = resolve47(str6(flags.dir) ?? process.cwd());
68483
- const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
68484
- let meta;
68485
- try {
68486
- meta = JSON.parse(readFileSync75(join60(sourceDir, TEMPLATE_META_FILE), "utf8"));
68487
- } catch {
68488
- io.error(JSON.stringify({ error: `no ${TEMPLATE_META_FILE} in ${sourceDir} \u2014 a template must declare { title, description, tags } to be published`, hint: `add ${TEMPLATE_META_FILE} next to himalaya.content.json (or pass --source-dir <dir>)` }));
68489
- return 2;
68490
- }
68491
- if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
68492
- io.error(JSON.stringify({ error: `${TEMPLATE_META_FILE} must be { title: string, description: string, tags: string[], category?: string }` }));
68493
- return 2;
68494
- }
68569
+ const publishScratch = [];
68495
68570
  try {
68496
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync75(join60(sourceDir, "tokens.json"), "utf8")));
68497
- meta = { ...appearance, ...meta };
68498
- } catch {
68499
- }
68500
- const name = str6(flags.name) ?? await appId(sourceDir);
68501
- const templateAppId = TEMPLATE_APP_PREFIX + name;
68502
- const buildDir = materializeForPreview(appDir2, name, meta.title);
68503
- const app = await appId(buildDir);
68504
- const strict = flags.strict === true;
68505
- const force = flags.force === true;
68506
- const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68507
- const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
68508
- const out = mkdtempSync4(join60(tmpdir5(), "himi-template-"));
68509
- const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68510
- const outcome = await buildReleaseFromContent2(buildDir, out, {
68511
- ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
68512
- collect: { lint: true, strict }
68513
- });
68514
- const scaffoldSourceFindings = resolve47(sourceDir) === resolve47(appDir2) ? [] : [
68515
- ...tokenColorIssues(sourceDir),
68516
- ...surfaceIssues(sourceDir, void 0, strict),
68517
- ...await fontIssues(sourceDir, strict),
68518
- ...storesV1Issues(sourceDir, strict)
68519
- ].map((issue2) => ({
68520
- ...issue2,
68521
- // Say WHICH tree. The same message against two trees means two different fixes,
68522
- // and the scaffold source is the one the author is least likely to be looking at.
68523
- message: `scaffold source (--source-dir ${sourceDir}): ${issue2.message}`
68524
- }));
68525
- const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
68526
- const { report: report2, hasErrors } = summarizeLint(app, bres, outcome, strict, [
68527
- ...await cmsIssues(buildDir, app, strict, { local: true }),
68528
- ...surfaceIssues(buildDir, outcome.surfaceNav, strict),
68529
- ...tokenColorIssues(buildDir),
68530
- ...await fontIssues(buildDir, strict),
68531
- ...storesV1Issues(buildDir, strict),
68532
- ...await navIssues2(buildDir, strict),
68533
- ...scaffoldSourceFindings
68534
- ]);
68535
- if (hasErrors && !force) {
68536
- print({ ...report2, published: false, hint: "lint found errors \u2014 fix them, or re-run with --force to publish anyway" });
68537
- return 1;
68538
- }
68539
- const payload = await loadReleaseFromDir(app, out);
68540
- const previewAuthError = templatePreviewAuthError(payload.appConfig);
68541
- if (previewAuthError) {
68542
- io.error(JSON.stringify({ error: previewAuthError, published: false }));
68543
- return 1;
68544
- }
68545
- const leaked = sentineledFiles({
68546
- ...payload.screens,
68547
- ...payload.appConfig ? { "app-config.json": payload.appConfig } : {},
68548
- ...payload.tokens ? { "tokens.json": payload.tokens } : {}
68549
- });
68550
- if (leaked.length > 0) {
68551
- io.error(JSON.stringify({
68552
- error: `built release still contains __HIMI_APP_*__ scaffold markers in: ${leaked.join(", ")}`,
68553
- hint: "point --dir at a concrete app (e.g. apps/himi-branded-lite), or fix the template so its identity fields are sentineled only in files himi init substitutes"
68554
- }));
68555
- return 1;
68556
- }
68557
- const iconPath = str6(flags.icon) ? resolve47(str6(flags.icon)) : templateIconPath(name);
68558
- let iconHash;
68559
- if (iconPath) {
68571
+ for (const f of ["dir", "source-dir", "name", "icon"]) {
68572
+ if (flags[f] === true) {
68573
+ io.error(JSON.stringify({ error: `--${f} needs a value`, published: false }));
68574
+ return 2;
68575
+ }
68576
+ }
68577
+ const appDir2 = resolve47(str6(flags.dir) ?? process.cwd());
68578
+ const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
68579
+ let meta;
68560
68580
  try {
68561
- const bytes2 = readFileSync75(iconPath);
68562
- payload.icon = bytes2.toString("base64");
68563
- iconHash = createHash27("sha256").update(bytes2).digest("hex");
68581
+ meta = JSON.parse(readFileSync75(join60(sourceDir, TEMPLATE_META_FILE), "utf8"));
68564
68582
  } catch {
68583
+ io.error(JSON.stringify({ error: `no ${TEMPLATE_META_FILE} in ${sourceDir} \u2014 a template must declare { title, description, tags } to be published`, hint: `add ${TEMPLATE_META_FILE} next to himalaya.content.json (or pass --source-dir <dir>)` }));
68584
+ return 2;
68565
68585
  }
68566
- }
68567
- payload.app = templateAppId;
68568
- payload.manifest = { ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} };
68569
- const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
68570
- if (Object.keys(files).length === 0) {
68571
- io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
68572
- return 1;
68573
- }
68574
- payload.source = { files, meta };
68575
- const { collectAppSource: collectAppSource2, secretFindings: secretFindings2, zipAppSource: zipAppSource2, largestEntries: largestEntries2, MAX_APP_SOURCE_BYTES: MAX_APP_SOURCE_BYTES2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
68576
- const templateArchive = collectAppSource2(sourceDir);
68577
- const templateSecrets = secretFindings2(templateArchive.entries);
68578
- if (templateSecrets.length > 0) {
68579
- io.error(JSON.stringify({
68580
- error: `refusing to publish: ${templateSecrets[0].path} looks like it contains ${templateSecrets[0].reason}`,
68581
- file: templateSecrets[0].path,
68582
- hint: "a template's source is served PUBLICLY \u2014 remove it from the tree before publishing"
68583
- }));
68584
- return 1;
68585
- }
68586
- const templateZip = zipAppSource2(templateArchive.entries);
68587
- const templateSourceB64 = templateZip.toString("base64");
68588
- if (templateSourceB64.length > MAX_APP_SOURCE_BYTES2) {
68589
- const templateLargest = largestEntries2(templateArchive.entries);
68590
- io.error(JSON.stringify({
68591
- ok: false,
68592
- template: name,
68593
- app: templateAppId,
68594
- published: false,
68595
- error: "app_source_too_large",
68596
- archivedBytes: templateZip.length,
68597
- encodedBytes: templateSourceB64.length,
68598
- limit: MAX_APP_SOURCE_BYTES2,
68599
- files: templateArchive.entries.length,
68600
- largest: templateLargest,
68601
- // No `--no-source` here, deliberately: a template's archive is not optional the way
68602
- // a push's is. `himi pull` restores templates through it, so a flag that dropped it
68603
- // would publish a template nobody can restore — the failure #2423 exists to prevent.
68604
- hint: `trim the template's source tree \u2014 the largest entries are ${templateLargest.map((e) => `${e.path} (${Math.round(e.compressed / 1024)} KB compressed)`).join(", ")}. Build output and dependencies are already excluded, and a template's archive is not optional, so there is no flag that publishes without it`
68605
- }));
68606
- return 1;
68607
- }
68608
- payload.appSource = templateSourceB64;
68609
- const tplVis = visibilityFlag(flags, io);
68610
- if (!tplVis) return 2;
68611
- const publishMeta = {
68612
- ...str6(flags.label) ? { label: str6(flags.label) } : {},
68613
- ...str6(flags.notes) ? { notes: str6(flags.notes) } : {},
68614
- ...tplVis.value ? { visibility: tplVis.value } : {}
68615
- };
68616
- let sendPayload = payload;
68617
- let templateBodyBytes = publishReleaseBodyBytes(payload, publishMeta);
68618
- let sourceGzChars;
68619
- if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68620
- const { gzipSync: gzipSync2 } = await import("node:zlib");
68621
- const { source: plainSource, ...rest } = payload;
68622
- const sourceGz = gzipSync2(Buffer.from(JSON.stringify(plainSource), "utf8"), { level: 9 }).toString("base64");
68623
- sourceGzChars = sourceGz.length;
68624
- const gzipped = { ...rest, sourceGz };
68625
- const gzBytes = publishReleaseBodyBytes(gzipped, publishMeta);
68626
- if (gzBytes <= MAX_PUBLISH_BODY_BYTES) {
68627
- sendPayload = gzipped;
68628
- templateBodyBytes = gzBytes;
68586
+ if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
68587
+ io.error(JSON.stringify({ error: `${TEMPLATE_META_FILE} must be { title: string, description: string, tags: string[], category?: string }` }));
68588
+ return 2;
68629
68589
  }
68630
- }
68631
- if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68632
- io.error(JSON.stringify({
68633
- ok: false,
68634
- template: name,
68635
- app: templateAppId,
68636
- published: false,
68637
- error: "publish_body_too_large",
68638
- bodyBytes: templateBodyBytes,
68639
- limit: MAX_PUBLISH_BODY_BYTES,
68640
- // Both halves, because "the body is too big" does not say which one to cut, and the
68641
- // two answer to different edits: `sourceChars` shrinks by removing FILES from the
68642
- // tree, `appSourceBytes` by removing bytes (an image, a fixture) that may not be
68643
- // text at all. They overlap by design — the same tree, twice — so they are reported
68644
- // separately rather than summed into a figure that double-counts it.
68645
- sourceChars: JSON.stringify(payload.source).length,
68646
- appSourceBytes: templateSourceB64.length,
68647
- // The catalog view is reported COMPRESSED as well, because "trim the tree" was the
68648
- // wrong instruction and cost real time: the collector already drops binaries,
68649
- // symlinks and dist/node_modules/.git/.himi, so there is rarely anything to trim.
68650
- // What the two figures actually say is whether compression can still save you --
68651
- // `sourceGzChars` is what this publish already tried and what remains after it.
68652
- sourceGzChars,
68653
- hint: "the catalog view is already sent gzipped when the plain body would not fit, and it still does not: the remainder is the release itself (screens, bundles, assets) plus the private archive, which no flag omits. Cut what the app ships, not the source tree \u2014 the collector already excludes binaries, symlinks, dist/, node_modules/, .git/ and .himi/."
68590
+ try {
68591
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync75(join60(sourceDir, "tokens.json"), "utf8")));
68592
+ meta = { ...appearance, ...meta };
68593
+ } catch {
68594
+ }
68595
+ const nameFlag = str6(flags.name);
68596
+ const name = nameFlag ?? await templatePublishName(sourceDir);
68597
+ const nameError = templateNameError(
68598
+ name,
68599
+ nameFlag !== void 0 ? "from --name" : `from ${sourceDir}'s himalaya.content.json, or its directory name when that is a scaffold marker`
68600
+ );
68601
+ if (nameError) {
68602
+ io.error(JSON.stringify({ error: nameError, published: false }));
68603
+ return 2;
68604
+ }
68605
+ const templateAppId = TEMPLATE_APP_PREFIX + name;
68606
+ const buildDir = materializeForPreview(appDir2, name, meta.title);
68607
+ if (buildDir !== appDir2) publishScratch.push(buildDir);
68608
+ const app = await appId(buildDir);
68609
+ const strict = flags.strict === true;
68610
+ const force = flags.force === true;
68611
+ const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68612
+ const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
68613
+ const out = mkdtempSync4(join60(tmpdir5(), "himi-template-"));
68614
+ publishScratch.push(out);
68615
+ const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68616
+ const outcome = await buildReleaseFromContent2(buildDir, out, {
68617
+ ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
68618
+ collect: { lint: true, strict }
68619
+ });
68620
+ const scaffoldSourceFindings = resolve47(sourceDir) === resolve47(appDir2) ? [] : [
68621
+ ...tokenColorIssues(sourceDir),
68622
+ ...surfaceIssues(sourceDir, void 0, strict),
68623
+ ...await fontIssues(sourceDir, strict),
68624
+ ...storesV1Issues(sourceDir, strict)
68625
+ ].map((issue2) => ({
68626
+ ...issue2,
68627
+ // Say WHICH tree. The same message against two trees means two different fixes,
68628
+ // and the scaffold source is the one the author is least likely to be looking at.
68629
+ message: `scaffold source (--source-dir ${sourceDir}): ${issue2.message}`
68654
68630
  }));
68655
- return 1;
68656
- }
68657
- if (sendPayload.sourceGz) {
68658
- const { gzipSync: gz } = await import("node:zlib");
68659
- const probeApp = templateAppId;
68660
- let probeErr;
68631
+ const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
68632
+ const { report: report2, hasErrors } = summarizeLint(app, bres, outcome, strict, [
68633
+ ...await cmsIssues(buildDir, app, strict, { local: true }),
68634
+ ...surfaceIssues(buildDir, outcome.surfaceNav, strict),
68635
+ ...tokenColorIssues(buildDir),
68636
+ ...await fontIssues(buildDir, strict),
68637
+ ...storesV1Issues(buildDir, strict),
68638
+ ...await navIssues2(buildDir, strict),
68639
+ ...scaffoldSourceFindings
68640
+ ]);
68641
+ if (hasErrors && !force) {
68642
+ print({ ...report2, published: false, hint: "lint found errors \u2014 fix them, or re-run with --force to publish anyway" });
68643
+ return 1;
68644
+ }
68645
+ const payload = await loadReleaseFromDir(app, out);
68646
+ const previewAuthError = templatePreviewAuthError(payload.appConfig);
68647
+ if (previewAuthError) {
68648
+ io.error(JSON.stringify({ error: previewAuthError, published: false }));
68649
+ return 1;
68650
+ }
68651
+ const leaked = sentineledFiles({
68652
+ ...payload.screens,
68653
+ ...payload.appConfig ? { "app-config.json": payload.appConfig } : {},
68654
+ ...payload.tokens ? { "tokens.json": payload.tokens } : {}
68655
+ });
68656
+ if (leaked.length > 0) {
68657
+ io.error(JSON.stringify({
68658
+ error: `built release still contains __HIMI_APP_*__ scaffold markers in: ${leaked.join(", ")}`,
68659
+ hint: "point --dir at a concrete app (e.g. apps/himi-branded-lite), or fix the template so its identity fields are sentineled only in files himi init substitutes"
68660
+ }));
68661
+ return 1;
68662
+ }
68663
+ const iconPath = str6(flags.icon) ? resolve47(str6(flags.icon)) : templateIconPath(name);
68664
+ let iconHash;
68665
+ if (iconPath) {
68666
+ try {
68667
+ const bytes2 = readFileSync75(iconPath);
68668
+ payload.icon = bytes2.toString("base64");
68669
+ iconHash = createHash27("sha256").update(bytes2).digest("hex");
68670
+ } catch {
68671
+ }
68672
+ }
68673
+ payload.app = templateAppId;
68674
+ payload.manifest = { ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} };
68675
+ const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
68676
+ if (Object.keys(files).length === 0) {
68677
+ io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
68678
+ return 1;
68679
+ }
68680
+ payload.source = { files, meta };
68681
+ const { collectAppSource: collectAppSource2, secretFindings: secretFindings2, zipAppSource: zipAppSource2, largestEntries: largestEntries2, MAX_APP_SOURCE_BYTES: MAX_APP_SOURCE_BYTES2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
68682
+ const templateArchive = collectAppSource2(sourceDir);
68683
+ const templateSecrets = secretFindings2(templateArchive.entries);
68684
+ if (templateSecrets.length > 0) {
68685
+ io.error(JSON.stringify({
68686
+ error: `refusing to publish: ${templateSecrets[0].path} looks like it contains ${templateSecrets[0].reason}`,
68687
+ file: templateSecrets[0].path,
68688
+ hint: "a template's source is served PUBLICLY \u2014 remove it from the tree before publishing"
68689
+ }));
68690
+ return 1;
68691
+ }
68692
+ const templateZip = zipAppSource2(templateArchive.entries);
68693
+ const templateSourceB64 = templateZip.toString("base64");
68694
+ if (templateSourceB64.length > MAX_APP_SOURCE_BYTES2) {
68695
+ const templateLargest = largestEntries2(templateArchive.entries);
68696
+ io.error(JSON.stringify({
68697
+ ok: false,
68698
+ template: name,
68699
+ app: templateAppId,
68700
+ published: false,
68701
+ error: "app_source_too_large",
68702
+ archivedBytes: templateZip.length,
68703
+ encodedBytes: templateSourceB64.length,
68704
+ limit: MAX_APP_SOURCE_BYTES2,
68705
+ files: templateArchive.entries.length,
68706
+ largest: templateLargest,
68707
+ // No `--no-source` here, deliberately: a template's archive is not optional the way
68708
+ // a push's is. `himi pull` restores templates through it, so a flag that dropped it
68709
+ // would publish a template nobody can restore — the failure #2423 exists to prevent.
68710
+ hint: `trim the template's source tree \u2014 the largest entries are ${templateLargest.map((e) => `${e.path} (${Math.round(e.compressed / 1024)} KB compressed)`).join(", ")}. Build output and dependencies are already excluded, and a template's archive is not optional, so there is no flag that publishes without it`
68711
+ }));
68712
+ return 1;
68713
+ }
68714
+ payload.appSource = templateSourceB64;
68715
+ const tplVis = visibilityFlag(flags, io);
68716
+ if (!tplVis) return 2;
68717
+ const publishMeta = {
68718
+ ...str6(flags.label) ? { label: str6(flags.label) } : {},
68719
+ ...str6(flags.notes) ? { notes: str6(flags.notes) } : {},
68720
+ ...tplVis.value ? { visibility: tplVis.value } : {}
68721
+ };
68722
+ let sendPayload = payload;
68723
+ let templateBodyBytes = publishReleaseBodyBytes(payload, publishMeta);
68724
+ let sourceGzChars;
68725
+ if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68726
+ const { gzipSync: gzipSync2 } = await import("node:zlib");
68727
+ const { source: plainSource, ...rest } = payload;
68728
+ const sourceGz = gzipSync2(Buffer.from(JSON.stringify(plainSource), "utf8"), { level: 9 }).toString("base64");
68729
+ sourceGzChars = sourceGz.length;
68730
+ const gzipped = { ...rest, sourceGz };
68731
+ const gzBytes = publishReleaseBodyBytes(gzipped, publishMeta);
68732
+ if (gzBytes <= MAX_PUBLISH_BODY_BYTES) {
68733
+ sendPayload = gzipped;
68734
+ templateBodyBytes = gzBytes;
68735
+ }
68736
+ }
68737
+ if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68738
+ io.error(JSON.stringify({
68739
+ ok: false,
68740
+ template: name,
68741
+ app: templateAppId,
68742
+ published: false,
68743
+ error: "publish_body_too_large",
68744
+ bodyBytes: templateBodyBytes,
68745
+ limit: MAX_PUBLISH_BODY_BYTES,
68746
+ // Both halves, because "the body is too big" does not say which one to cut, and the
68747
+ // two answer to different edits: `sourceChars` shrinks by removing FILES from the
68748
+ // tree, `appSourceBytes` by removing bytes (an image, a fixture) that may not be
68749
+ // text at all. They overlap by design — the same tree, twice — so they are reported
68750
+ // separately rather than summed into a figure that double-counts it.
68751
+ sourceChars: JSON.stringify(payload.source).length,
68752
+ appSourceBytes: templateSourceB64.length,
68753
+ // The catalog view is reported COMPRESSED as well, because "trim the tree" was the
68754
+ // wrong instruction and cost real time: the collector already drops binaries,
68755
+ // symlinks and dist/node_modules/.git/.himi, so there is rarely anything to trim.
68756
+ // What the two figures actually say is whether compression can still save you --
68757
+ // `sourceGzChars` is what this publish already tried and what remains after it.
68758
+ sourceGzChars,
68759
+ hint: "the catalog view is already sent gzipped when the plain body would not fit, and it still does not: the remainder is the release itself (screens, bundles, assets) plus the private archive, which no flag omits. Cut what the app ships, not the source tree \u2014 the collector already excludes binaries, symlinks, dist/, node_modules/, .git/ and .himi/."
68760
+ }));
68761
+ return 1;
68762
+ }
68763
+ if (sendPayload.sourceGz) {
68764
+ const { gzipSync: gz } = await import("node:zlib");
68765
+ const probeApp = templateAppId;
68766
+ let probeErr;
68767
+ try {
68768
+ const probe = await client2().publishRelease({
68769
+ app: probeApp,
68770
+ manifest: { releaseId: "preflight", gitSha: "0".repeat(40), app: probeApp, minBinaryVersion: null, screens: {}, bundles: {} },
68771
+ screens: {},
68772
+ sourceGz: gz(Buffer.from("null")).toString("base64")
68773
+ });
68774
+ probeErr = probe.json?.error ?? probe.status;
68775
+ } catch (e) {
68776
+ const target = targetName();
68777
+ io.error(JSON.stringify(serveUnreachableError(target, serveBase(), e.message)));
68778
+ return 1;
68779
+ }
68780
+ if (probeErr !== "source_gz_not_an_object") {
68781
+ io.error(JSON.stringify({
68782
+ ok: false,
68783
+ template: name,
68784
+ app: templateAppId,
68785
+ published: false,
68786
+ error: "serve_plane_lacks_source_gz",
68787
+ probe: probeErr,
68788
+ bodyBytes: templateBodyBytes,
68789
+ limit: MAX_PUBLISH_BODY_BYTES,
68790
+ hint: `this template only fits with a compressed catalog view, and this plane does not accept one \u2014 it answered ${JSON.stringify(probeErr)} to the capability probe instead of "source_gz_not_an_object". NOTHING was published. Upgrade the serve plane and run this again.`
68791
+ }));
68792
+ return 1;
68793
+ }
68794
+ }
68795
+ let r;
68661
68796
  try {
68662
- const probe = await client2().publishRelease({
68663
- app: probeApp,
68664
- manifest: { releaseId: "preflight", gitSha: "0".repeat(40), app: probeApp, minBinaryVersion: null, screens: {}, bundles: {} },
68665
- screens: {},
68666
- sourceGz: gz(Buffer.from("null")).toString("base64")
68667
- });
68668
- probeErr = probe.json?.error ?? probe.status;
68797
+ r = await client2().publishRelease(sendPayload, publishMeta);
68669
68798
  } catch (e) {
68670
68799
  const target = targetName();
68671
68800
  io.error(JSON.stringify(serveUnreachableError(target, serveBase(), e.message)));
68672
68801
  return 1;
68673
68802
  }
68674
- if (probeErr !== "source_gz_not_an_object") {
68803
+ if (!r.ok) {
68804
+ print({ ok: false, status: r.status, body: r.json ?? r.text });
68805
+ return 1;
68806
+ }
68807
+ if (sendPayload.sourceGz && r.json?.template !== true) {
68675
68808
  io.error(JSON.stringify({
68676
68809
  ok: false,
68677
68810
  template: name,
68678
68811
  app: templateAppId,
68679
68812
  published: false,
68680
- error: "serve_plane_lacks_source_gz",
68681
- probe: probeErr,
68682
- bodyBytes: templateBodyBytes,
68683
- limit: MAX_PUBLISH_BODY_BYTES,
68684
- hint: `this template only fits with a compressed catalog view, and this plane does not accept one \u2014 it answered ${JSON.stringify(probeErr)} to the capability probe instead of "source_gz_not_an_object". NOTHING was published. Upgrade the serve plane and run this again.`
68813
+ error: "serve_plane_ignored_source_gz",
68814
+ hint: `this plane accepted the publish but did not score it as a template, which means it predates compressed catalog views (\`sourceGz\`). A release WAS created for ${templateAppId} as an ordinary app \u2014 it carries no template source and will not appear in \`himi template list --remote\`. Upgrade the serve plane, then publish again; the republish repairs it.`
68685
68815
  }));
68686
68816
  return 1;
68687
68817
  }
68688
- }
68689
- let r;
68690
- try {
68691
- r = await client2().publishRelease(sendPayload, publishMeta);
68692
- } catch (e) {
68693
- const target = targetName();
68694
- io.error(JSON.stringify(serveUnreachableError(target, serveBase(), e.message)));
68695
- return 1;
68696
- }
68697
- if (!r.ok) {
68698
- print({ ok: false, status: r.status, body: r.json ?? r.text });
68699
- return 1;
68700
- }
68701
- if (sendPayload.sourceGz && r.json?.template !== true) {
68702
- io.error(JSON.stringify({
68703
- ok: false,
68818
+ const releaseId = r.json?.releaseId ?? payload.manifest.releaseId;
68819
+ const targetFlag = str6(flags.target) ? ` --target ${str6(flags.target)}` : "";
68820
+ print({
68821
+ ok: true,
68704
68822
  template: name,
68705
68823
  app: templateAppId,
68706
- published: false,
68707
- error: "serve_plane_ignored_source_gz",
68708
- hint: `this plane accepted the publish but did not score it as a template, which means it predates compressed catalog views (\`sourceGz\`). A release WAS created for ${templateAppId} as an ordinary app \u2014 it carries no template source and will not appear in \`himi template list --remote\`. Upgrade the serve plane, then publish again; the republish repairs it.`
68709
- }));
68710
- return 1;
68824
+ releaseId,
68825
+ title: meta.title,
68826
+ tags: meta.tags,
68827
+ icon: Boolean(iconHash),
68828
+ ...meta.accent ? { accent: meta.accent } : {},
68829
+ ...meta.palette ? { palette: meta.palette } : {},
68830
+ sourceFiles: Object.keys(files).length,
68831
+ ...hasErrors && force ? { forcedPastErrors: true } : {},
68832
+ initWith: `himi init <new-app-name> --template ${name} --remote${targetFlag}`
68833
+ });
68834
+ return 0;
68835
+ } finally {
68836
+ for (const dir of publishScratch) {
68837
+ try {
68838
+ rmSync14(dir, { recursive: true, force: true });
68839
+ } catch (e) {
68840
+ io.error(JSON.stringify({ warning: `could not remove temp dir ${dir}: ${e.message}` }));
68841
+ }
68842
+ }
68711
68843
  }
68712
- const releaseId = r.json?.releaseId ?? payload.manifest.releaseId;
68713
- const targetFlag = str6(flags.target) ? ` --target ${str6(flags.target)}` : "";
68714
- print({
68715
- ok: true,
68716
- template: name,
68717
- app: templateAppId,
68718
- releaseId,
68719
- title: meta.title,
68720
- tags: meta.tags,
68721
- icon: Boolean(iconHash),
68722
- ...meta.accent ? { accent: meta.accent } : {},
68723
- ...meta.palette ? { palette: meta.palette } : {},
68724
- sourceFiles: Object.keys(files).length,
68725
- ...hasErrors && force ? { forcedPastErrors: true } : {},
68726
- initWith: `himi init <new-app-name> --template ${name} --remote${targetFlag}`
68727
- });
68728
- return 0;
68729
68844
  }
68730
68845
  io.error(JSON.stringify({ error: "template needs a subcommand", usage: "himi template list [--remote] | himi template prompt <name> | himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--target <t>] | himi template trim --keep <id,id,\u2026> [--content <dir>] [--links follow|drop] [--dry-run] [--force]" }));
68731
68846
  return 2;
@@ -73513,7 +73628,9 @@ export {
73513
73628
  spawnUpdateRefresh,
73514
73629
  staleUpdateLockFile,
73515
73630
  switchFlagErrors,
73631
+ templateNameError,
73516
73632
  templatePreviewAuthError,
73633
+ templatePublishName,
73517
73634
  tokenCoversApp,
73518
73635
  trustedConfiguredRegistry,
73519
73636
  updateCachePath,