@wix/himalaya-cli 0.1042.0 → 0.1044.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;
@@ -18383,151 +18569,6 @@ var init_wix_gateway = __esm({
18383
18569
  }
18384
18570
  });
18385
18571
 
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
18572
  // ../../core/serve-runtime/src/wix-bridge.ts
18532
18573
  function absoluteUrlRejection(path2) {
18533
18574
  let host = "";
@@ -18670,7 +18711,7 @@ var init_wix_bridge = __esm({
18670
18711
  * header with no session cookie falls back to itself — callers refuse that case before here. */
18671
18712
  key(msid, cookieHeader) {
18672
18713
  const session = /(?:^|;\s*)wixSession2=([^;]*)/.exec(cookieHeader)?.[1] ?? cookieHeader;
18673
- return sha256Hex3(`${msid}
18714
+ return sha256Hex(`${msid}
18674
18715
  ${session}`);
18675
18716
  }
18676
18717
  async instance(msid, cookieHeader, now = Date.now()) {
@@ -18745,7 +18786,7 @@ ${session}`);
18745
18786
  }
18746
18787
  cache = /* @__PURE__ */ new Map();
18747
18788
  key(cookieHeader, limit) {
18748
- return sha256Hex3(`${limit}
18789
+ return sha256Hex(`${limit}
18749
18790
  ${cookieHeader}`);
18750
18791
  }
18751
18792
  async sites(cookieHeader, limit, now = Date.now()) {
@@ -21250,7 +21291,6 @@ function shouldRevealError(args) {
21250
21291
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
21251
21292
  var init_validation = __esm({
21252
21293
  "../../core/ts/src/validation.ts"() {
21253
- "use strict";
21254
21294
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
21255
21295
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
21256
21296
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -24851,7 +24891,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
24851
24891
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
24852
24892
  var init_worker_subtree = __esm({
24853
24893
  "../../core/ts/src/worker-subtree.ts"() {
24854
- "use strict";
24855
24894
  init_worker_subtree_safe_types_generated();
24856
24895
  WORKER_SUBTREE_CEILINGS = Object.freeze({
24857
24896
  maxEncodedBytes: 65536,
@@ -33288,36 +33327,6 @@ var init_errors = __esm({
33288
33327
  }
33289
33328
  });
33290
33329
 
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
33330
  // ../../core/serve-control/src/config.ts
33322
33331
  var config_exports2 = {};
33323
33332
  __export(config_exports2, {
@@ -33630,11 +33639,13 @@ async function readBody2(req) {
33630
33639
  function makeEtag(body) {
33631
33640
  return `"${createHash16("sha256").update(body).digest("hex").slice(0, 32)}"`;
33632
33641
  }
33642
+ function servedTokensPath(app) {
33643
+ return app.module.designTokensPath ?? resolve31(app.dir, app.module.config.designTokensPath ?? "tokens.json");
33644
+ }
33633
33645
  function readAppTokens(state) {
33634
33646
  if (!state.app) return null;
33635
33647
  try {
33636
- const tokenPath = state.app.module.designTokensPath ?? resolve31(state.app.dir, state.app.module.config.designTokensPath ?? "tokens.json");
33637
- const parsed = JSON.parse(readFileSync41(tokenPath, "utf8"));
33648
+ const parsed = JSON.parse(readFileSync41(servedTokensPath(state.app), "utf8"));
33638
33649
  delete parsed._meta;
33639
33650
  return parsed;
33640
33651
  } catch {
@@ -34503,13 +34514,17 @@ async function reloadAppModuleNow(state, opts) {
34503
34514
  state.moduleStore = next.createStore(current.name);
34504
34515
  }
34505
34516
  state.serverCache.clear();
34506
- const screenIds = [.../* @__PURE__ */ new Set([...oldMeta.screenIds, ...state.app.meta.screenIds])];
34507
- publishSSE(state, "screen.invalidated", { screenIds, reason: opts.reason });
34517
+ if (opts.invalidateScreens !== false) {
34518
+ const screenIds = [.../* @__PURE__ */ new Set([...oldMeta.screenIds, ...state.app.meta.screenIds])];
34519
+ publishSSE(state, "screen.invalidated", { screenIds, reason: opts.reason });
34520
+ }
34508
34521
  const newNav = JSON.stringify(deriveNavigationConfig(state.app.meta) ?? null);
34509
34522
  if (oldNav !== newNav) {
34510
34523
  publishSSE(state, "config.invalidated", { reason: opts.reason, at: Date.now() });
34511
34524
  }
34512
- if (idiomOf(next) !== oldIdiom) {
34525
+ const tokensHash = servedTokensHash(state.app);
34526
+ if (idiomOf(next) !== oldIdiom || tokensHash !== state.announcedTokensHash) {
34527
+ state.announcedTokensHash = tokensHash;
34513
34528
  publishSSE(state, "tokens.changed", { reason: opts.reason, at: Date.now() });
34514
34529
  }
34515
34530
  console.log(`[himalaya-dev] reloaded app module in-process: ${current.name} (${opts.reason})`);
@@ -34525,6 +34540,17 @@ async function reloadAppModuleNow(state, opts) {
34525
34540
  }
34526
34541
  }
34527
34542
  }
34543
+ function tokensAreDerived(state) {
34544
+ if (!state.app) return false;
34545
+ return resolve31(servedTokensPath(state.app)) !== resolve31(state.app.dir, "tokens.json");
34546
+ }
34547
+ function servedTokensHash(app) {
34548
+ try {
34549
+ return createHash16("sha256").update(readFileSync41(servedTokensPath(app))).digest("hex");
34550
+ } catch {
34551
+ return "";
34552
+ }
34553
+ }
34528
34554
  function classifyAppPath(rel) {
34529
34555
  if (!rel || rel.startsWith("..")) return null;
34530
34556
  if (rel === "tokens.json") return "tokens";
@@ -34550,21 +34576,30 @@ function startAppSourceWatcher(state) {
34550
34576
  pending.clear();
34551
34577
  if (paths.length === 0) return;
34552
34578
  const kinds = new Set(paths.map(classifyAppPath));
34553
- if (kinds.has("tokens")) {
34554
- publishSSE(state, "tokens.changed", { reason: "file_changed", at: Date.now() });
34555
- }
34579
+ const tokensNeedReload = kinds.has("tokens") && tokensAreDerived(state);
34556
34580
  if (kinds.has("appconfig")) {
34557
34581
  publishSSE(state, "config.invalidated", { reason: "file_changed", at: Date.now() });
34558
34582
  }
34559
- if (kinds.has("module")) {
34583
+ if (kinds.has("module") || tokensNeedReload) {
34560
34584
  reloading = true;
34561
34585
  const recreateStore = paths.some((p) => p.startsWith("data/") || p.startsWith("seed/"));
34562
34586
  try {
34563
- await reloadAppModule(state, { reason: "source_changed", recreateStore });
34587
+ await reloadAppModule(state, {
34588
+ reason: kinds.has("module") ? "source_changed" : "tokens_changed",
34589
+ recreateStore,
34590
+ invalidateScreens: kinds.has("module")
34591
+ });
34564
34592
  } finally {
34565
34593
  reloading = false;
34566
34594
  }
34567
34595
  }
34596
+ if (kinds.has("tokens") && state.app) {
34597
+ const tokensHash = servedTokensHash(state.app);
34598
+ if (tokensHash !== state.announcedTokensHash) {
34599
+ state.announcedTokensHash = tokensHash;
34600
+ publishSSE(state, "tokens.changed", { reason: "file_changed", at: Date.now() });
34601
+ }
34602
+ }
34568
34603
  };
34569
34604
  try {
34570
34605
  const watcher2 = watch(appDir2, { persistent: false, recursive: true }, (_event, filename) => {
@@ -36003,6 +36038,7 @@ function createDevServer(opts = {}) {
36003
36038
  if (state.hotReloadEnabled) {
36004
36039
  state.bundleWatcher = startBundleArtifactWatcher(state);
36005
36040
  state.bundleSourceWatcher = startBundleSourceWatcher(state);
36041
+ if (state.app) state.announcedTokensHash = servedTokensHash(state.app);
36006
36042
  state.appWatcher = startAppSourceWatcher(state);
36007
36043
  }
36008
36044
  const handleRequest = (req, res) => {
@@ -49475,7 +49511,7 @@ function makeServeHandler(opts) {
49475
49511
  }
49476
49512
  const PINNED_MANIFEST_CACHE_CAP = 100;
49477
49513
  const pinnedManifestCache = /* @__PURE__ */ new Map();
49478
- const sha256hex = (b) => sha256Hex3(b);
49514
+ const sha256hex = (b) => sha256Hex(b);
49479
49515
  function resolvePin(request, url) {
49480
49516
  return request.headers.get("x-himalaya-release") || url.searchParams.get("release") || null;
49481
49517
  }
@@ -63187,6 +63223,7 @@ async function loadReleaseFromDir(app, dir) {
63187
63223
  // src/cli.ts
63188
63224
  init_scaffold();
63189
63225
  init_store();
63226
+ init_contract();
63190
63227
  init_app_link();
63191
63228
  init_himi_report();
63192
63229
 
@@ -63592,7 +63629,7 @@ function screenshotArtifactPath(contentDir2, hash) {
63592
63629
  return join6(resolve6(contentDir2), ".himi", "attestations", "artifacts", `${hash}.png`);
63593
63630
  }
63594
63631
  var GIT_HASH_MAX_BUFFER = 128 * 1024 * 1024;
63595
- var sha256 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
63632
+ var sha2562 = (bytes2) => createHash3("sha256").update(bytes2).digest("hex");
63596
63633
  function canonical(value) {
63597
63634
  if (value === null || typeof value !== "object") return JSON.stringify(value);
63598
63635
  if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
@@ -63600,21 +63637,21 @@ function canonical(value) {
63600
63637
  return `{${Object.keys(record2).filter((key2) => record2[key2] !== void 0).sort().map((key2) => `${JSON.stringify(key2)}:${canonical(record2[key2])}`).join(",")}}`;
63601
63638
  }
63602
63639
  function releasePayloadHash(payload) {
63603
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha256(canonical(payload));
63640
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return sha2562(canonical(payload));
63604
63641
  const record2 = payload;
63605
63642
  const manifest = record2.manifest;
63606
- if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha256(canonical(payload));
63643
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return sha2562(canonical(payload));
63607
63644
  const manifestRecord = manifest;
63608
63645
  const widgets = manifestRecord.widgets;
63609
- if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha256(canonical(payload));
63610
- return sha256(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
63646
+ if (!widgets || typeof widgets !== "object" || Array.isArray(widgets)) return sha2562(canonical(payload));
63647
+ return sha2562(canonical({ ...record2, manifest: { ...manifestRecord, widgets: stableWidgets(widgets) } }));
63611
63648
  }
63612
63649
  function withoutSelfHash(attestation) {
63613
63650
  const { selfHash: _selfHash, ...body } = attestation;
63614
63651
  return body;
63615
63652
  }
63616
63653
  function selfHashFor(attestation) {
63617
- return sha256(canonical(withoutSelfHash(attestation)));
63654
+ return sha2562(canonical(withoutSelfHash(attestation)));
63618
63655
  }
63619
63656
  function repoRoot(contentDir2) {
63620
63657
  try {
@@ -63728,7 +63765,7 @@ function readRetainedAttestations(contentDir2) {
63728
63765
  for (const expected of Object.values(run2.screenshots)) {
63729
63766
  const path2 = screenshotArtifactPath(contentDir2, expected);
63730
63767
  if (!existsSync3(path2)) errors.push(`missing screenshot for retained run ${run2.runId}: ${basename2(path2)}`);
63731
- else if (sha256(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
63768
+ else if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match retained run ${run2.runId}: ${basename2(path2)}`);
63732
63769
  }
63733
63770
  }
63734
63771
  const runs = [];
@@ -63787,11 +63824,11 @@ function writeAttestation(input, opts = {}) {
63787
63824
  const previous = readLatestAttestation(contentDir2);
63788
63825
  const runId = `${(opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()}-${(opts.randomSuffix ?? (() => randomBytes(3).toString("hex")))()}`;
63789
63826
  const descriptorHashes = Object.fromEntries(
63790
- Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha256(bytes2)])
63827
+ Object.entries(input.descriptors).sort(([a], [b]) => a.localeCompare(b)).map(([screen, bytes2]) => [screen, sha2562(bytes2)])
63791
63828
  );
63792
63829
  const screenshotEntries = Object.entries(input.screenshots ?? {}).sort(([a], [b]) => a.localeCompare(b)).filter(([, path2]) => existsSync3(path2)).map(([screen, path2]) => {
63793
63830
  const bytes2 = readFileSync5(path2);
63794
- return { screen, bytes: bytes2, hash: sha256(bytes2) };
63831
+ return { screen, bytes: bytes2, hash: sha2562(bytes2) };
63795
63832
  });
63796
63833
  const screenshots = Object.fromEntries(screenshotEntries.map(({ screen, hash }) => [screen, hash]));
63797
63834
  for (const { bytes: bytes2, hash } of screenshotEntries) {
@@ -63914,7 +63951,7 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
63914
63951
  errors.push(`missing baked descriptor for ${screen}`);
63915
63952
  continue;
63916
63953
  }
63917
- if (sha256(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
63954
+ if (sha2562(bytes2) !== expected) errors.push(`descriptor hash does not match for ${screen}`);
63918
63955
  }
63919
63956
  for (const [screen, expected] of Object.entries(attestation.screenshots)) {
63920
63957
  const path2 = screenshotArtifactPath(contentDir2, expected);
@@ -63922,7 +63959,7 @@ function verifyAttestation(attestation, contentDir2, descriptors, expectedApp, r
63922
63959
  errors.push(`missing screenshot for ${screen}: ${basename2(path2)}`);
63923
63960
  continue;
63924
63961
  }
63925
- if (sha256(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
63962
+ if (sha2562(readFileSync5(path2)) !== expected) errors.push(`screenshot hash does not match for ${screen}: ${basename2(path2)}`);
63926
63963
  }
63927
63964
  return { ok: errors.length === 0, errors };
63928
63965
  }
@@ -67557,8 +67594,26 @@ function shellShaDivergenceWarnings(jobs) {
67557
67594
  ([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.`
67558
67595
  );
67559
67596
  }
67597
+ async function templatePublishName(sourceDir) {
67598
+ const id = await appId(sourceDir);
67599
+ return IDENTITY_SENTINELS.some((s) => id.includes(s)) ? basename14(sourceDir) : id;
67600
+ }
67601
+ function templateNameError(name, from) {
67602
+ if (name.startsWith(TEMPLATE_APP_PREFIX)) {
67603
+ const stripped = name.slice(TEMPLATE_APP_PREFIX.length);
67604
+ const hint = stripped && !stripped.startsWith(TEMPLATE_APP_PREFIX) && !templateNameError(stripped, from) ? `pass --name ${stripped}` : "pass --name <kebab-id>";
67605
+ return `template name "${name}" (${from}) already starts with "${TEMPLATE_APP_PREFIX}", which would publish to "${TEMPLATE_APP_PREFIX}${name}"; ${hint}`;
67606
+ }
67607
+ if (!TEMPLATE_NAME_PATTERN.test(name)) {
67608
+ 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>)`;
67609
+ }
67610
+ const composed = TEMPLATE_APP_PREFIX + name;
67611
+ 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`;
67612
+ }
67560
67613
  function materializeForPreview(appDir2, slug, displayName) {
67561
67614
  if (!isTemplateSourceTree(appDir2)) return appDir2;
67615
+ const slugError = templateNameError(slug, "preview slug");
67616
+ if (slugError) throw Object.assign(new Error(slugError), { exitCode: 2 });
67562
67617
  const files = readTemplateFiles(appDir2);
67563
67618
  const forBuild = Object.fromEntries(
67564
67619
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
@@ -67586,7 +67641,12 @@ function materializeForPreview(appDir2, slug, displayName) {
67586
67641
  const dest = join60(root, `${slug}-${process.pid}`);
67587
67642
  rmSync14(dest, { recursive: true, force: true });
67588
67643
  mkdirSync31(dest, { recursive: true });
67589
- materializeSource(forBuild, displayName, dest, slug);
67644
+ try {
67645
+ materializeSource(forBuild, displayName, dest, slug);
67646
+ } catch (e) {
67647
+ rmSync14(dest, { recursive: true, force: true });
67648
+ throw e;
67649
+ }
67590
67650
  return dest;
67591
67651
  }
67592
67652
  async function appId(dir) {
@@ -68452,253 +68512,281 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
68452
68512
  return outcome.code;
68453
68513
  }
68454
68514
  if (sub === "publish") {
68455
- const appDir2 = resolve47(str6(flags.dir) ?? process.cwd());
68456
- const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
68457
- let meta;
68458
- try {
68459
- meta = JSON.parse(readFileSync75(join60(sourceDir, TEMPLATE_META_FILE), "utf8"));
68460
- } catch {
68461
- 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>)` }));
68462
- return 2;
68463
- }
68464
- if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
68465
- io.error(JSON.stringify({ error: `${TEMPLATE_META_FILE} must be { title: string, description: string, tags: string[], category?: string }` }));
68466
- return 2;
68467
- }
68515
+ const publishScratch = [];
68468
68516
  try {
68469
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync75(join60(sourceDir, "tokens.json"), "utf8")));
68470
- meta = { ...appearance, ...meta };
68471
- } catch {
68472
- }
68473
- const name = str6(flags.name) ?? await appId(sourceDir);
68474
- const templateAppId = TEMPLATE_APP_PREFIX + name;
68475
- const buildDir = materializeForPreview(appDir2, name, meta.title);
68476
- const app = await appId(buildDir);
68477
- const strict = flags.strict === true;
68478
- const force = flags.force === true;
68479
- const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68480
- const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
68481
- const out = mkdtempSync4(join60(tmpdir5(), "himi-template-"));
68482
- const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68483
- const outcome = await buildReleaseFromContent2(buildDir, out, {
68484
- ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
68485
- collect: { lint: true, strict }
68486
- });
68487
- const scaffoldSourceFindings = resolve47(sourceDir) === resolve47(appDir2) ? [] : [
68488
- ...tokenColorIssues(sourceDir),
68489
- ...surfaceIssues(sourceDir, void 0, strict),
68490
- ...await fontIssues(sourceDir, strict),
68491
- ...storesV1Issues(sourceDir, strict)
68492
- ].map((issue2) => ({
68493
- ...issue2,
68494
- // Say WHICH tree. The same message against two trees means two different fixes,
68495
- // and the scaffold source is the one the author is least likely to be looking at.
68496
- message: `scaffold source (--source-dir ${sourceDir}): ${issue2.message}`
68497
- }));
68498
- const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
68499
- const { report: report2, hasErrors } = summarizeLint(app, bres, outcome, strict, [
68500
- ...await cmsIssues(buildDir, app, strict, { local: true }),
68501
- ...surfaceIssues(buildDir, outcome.surfaceNav, strict),
68502
- ...tokenColorIssues(buildDir),
68503
- ...await fontIssues(buildDir, strict),
68504
- ...storesV1Issues(buildDir, strict),
68505
- ...await navIssues2(buildDir, strict),
68506
- ...scaffoldSourceFindings
68507
- ]);
68508
- if (hasErrors && !force) {
68509
- print({ ...report2, published: false, hint: "lint found errors \u2014 fix them, or re-run with --force to publish anyway" });
68510
- return 1;
68511
- }
68512
- const payload = await loadReleaseFromDir(app, out);
68513
- const previewAuthError = templatePreviewAuthError(payload.appConfig);
68514
- if (previewAuthError) {
68515
- io.error(JSON.stringify({ error: previewAuthError, published: false }));
68516
- return 1;
68517
- }
68518
- const leaked = sentineledFiles({
68519
- ...payload.screens,
68520
- ...payload.appConfig ? { "app-config.json": payload.appConfig } : {},
68521
- ...payload.tokens ? { "tokens.json": payload.tokens } : {}
68522
- });
68523
- if (leaked.length > 0) {
68524
- io.error(JSON.stringify({
68525
- error: `built release still contains __HIMI_APP_*__ scaffold markers in: ${leaked.join(", ")}`,
68526
- 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"
68527
- }));
68528
- return 1;
68529
- }
68530
- const iconPath = str6(flags.icon) ? resolve47(str6(flags.icon)) : templateIconPath(name);
68531
- let iconHash;
68532
- if (iconPath) {
68517
+ for (const f of ["dir", "source-dir", "name", "icon"]) {
68518
+ if (flags[f] === true) {
68519
+ io.error(JSON.stringify({ error: `--${f} needs a value`, published: false }));
68520
+ return 2;
68521
+ }
68522
+ }
68523
+ const appDir2 = resolve47(str6(flags.dir) ?? process.cwd());
68524
+ const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
68525
+ let meta;
68533
68526
  try {
68534
- const bytes2 = readFileSync75(iconPath);
68535
- payload.icon = bytes2.toString("base64");
68536
- iconHash = createHash27("sha256").update(bytes2).digest("hex");
68527
+ meta = JSON.parse(readFileSync75(join60(sourceDir, TEMPLATE_META_FILE), "utf8"));
68537
68528
  } catch {
68529
+ 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>)` }));
68530
+ return 2;
68538
68531
  }
68539
- }
68540
- payload.app = templateAppId;
68541
- payload.manifest = { ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} };
68542
- const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
68543
- if (Object.keys(files).length === 0) {
68544
- io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
68545
- return 1;
68546
- }
68547
- payload.source = { files, meta };
68548
- 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));
68549
- const templateArchive = collectAppSource2(sourceDir);
68550
- const templateSecrets = secretFindings2(templateArchive.entries);
68551
- if (templateSecrets.length > 0) {
68552
- io.error(JSON.stringify({
68553
- error: `refusing to publish: ${templateSecrets[0].path} looks like it contains ${templateSecrets[0].reason}`,
68554
- file: templateSecrets[0].path,
68555
- hint: "a template's source is served PUBLICLY \u2014 remove it from the tree before publishing"
68556
- }));
68557
- return 1;
68558
- }
68559
- const templateZip = zipAppSource2(templateArchive.entries);
68560
- const templateSourceB64 = templateZip.toString("base64");
68561
- if (templateSourceB64.length > MAX_APP_SOURCE_BYTES2) {
68562
- const templateLargest = largestEntries2(templateArchive.entries);
68563
- io.error(JSON.stringify({
68564
- ok: false,
68565
- template: name,
68566
- app: templateAppId,
68567
- published: false,
68568
- error: "app_source_too_large",
68569
- archivedBytes: templateZip.length,
68570
- encodedBytes: templateSourceB64.length,
68571
- limit: MAX_APP_SOURCE_BYTES2,
68572
- files: templateArchive.entries.length,
68573
- largest: templateLargest,
68574
- // No `--no-source` here, deliberately: a template's archive is not optional the way
68575
- // a push's is. `himi pull` restores templates through it, so a flag that dropped it
68576
- // would publish a template nobody can restore — the failure #2423 exists to prevent.
68577
- 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`
68578
- }));
68579
- return 1;
68580
- }
68581
- payload.appSource = templateSourceB64;
68582
- const tplVis = visibilityFlag(flags, io);
68583
- if (!tplVis) return 2;
68584
- const publishMeta = {
68585
- ...str6(flags.label) ? { label: str6(flags.label) } : {},
68586
- ...str6(flags.notes) ? { notes: str6(flags.notes) } : {},
68587
- ...tplVis.value ? { visibility: tplVis.value } : {}
68588
- };
68589
- let sendPayload = payload;
68590
- let templateBodyBytes = publishReleaseBodyBytes(payload, publishMeta);
68591
- let sourceGzChars;
68592
- if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68593
- const { gzipSync: gzipSync2 } = await import("node:zlib");
68594
- const { source: plainSource, ...rest } = payload;
68595
- const sourceGz = gzipSync2(Buffer.from(JSON.stringify(plainSource), "utf8"), { level: 9 }).toString("base64");
68596
- sourceGzChars = sourceGz.length;
68597
- const gzipped = { ...rest, sourceGz };
68598
- const gzBytes = publishReleaseBodyBytes(gzipped, publishMeta);
68599
- if (gzBytes <= MAX_PUBLISH_BODY_BYTES) {
68600
- sendPayload = gzipped;
68601
- templateBodyBytes = gzBytes;
68532
+ if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
68533
+ io.error(JSON.stringify({ error: `${TEMPLATE_META_FILE} must be { title: string, description: string, tags: string[], category?: string }` }));
68534
+ return 2;
68602
68535
  }
68603
- }
68604
- if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68605
- io.error(JSON.stringify({
68606
- ok: false,
68607
- template: name,
68608
- app: templateAppId,
68609
- published: false,
68610
- error: "publish_body_too_large",
68611
- bodyBytes: templateBodyBytes,
68612
- limit: MAX_PUBLISH_BODY_BYTES,
68613
- // Both halves, because "the body is too big" does not say which one to cut, and the
68614
- // two answer to different edits: `sourceChars` shrinks by removing FILES from the
68615
- // tree, `appSourceBytes` by removing bytes (an image, a fixture) that may not be
68616
- // text at all. They overlap by design — the same tree, twice — so they are reported
68617
- // separately rather than summed into a figure that double-counts it.
68618
- sourceChars: JSON.stringify(payload.source).length,
68619
- appSourceBytes: templateSourceB64.length,
68620
- // The catalog view is reported COMPRESSED as well, because "trim the tree" was the
68621
- // wrong instruction and cost real time: the collector already drops binaries,
68622
- // symlinks and dist/node_modules/.git/.himi, so there is rarely anything to trim.
68623
- // What the two figures actually say is whether compression can still save you --
68624
- // `sourceGzChars` is what this publish already tried and what remains after it.
68625
- sourceGzChars,
68626
- 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/."
68536
+ try {
68537
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync75(join60(sourceDir, "tokens.json"), "utf8")));
68538
+ meta = { ...appearance, ...meta };
68539
+ } catch {
68540
+ }
68541
+ const nameFlag = str6(flags.name);
68542
+ const name = nameFlag ?? await templatePublishName(sourceDir);
68543
+ const nameError = templateNameError(
68544
+ name,
68545
+ nameFlag !== void 0 ? "from --name" : `from ${sourceDir}'s himalaya.content.json, or its directory name when that is a scaffold marker`
68546
+ );
68547
+ if (nameError) {
68548
+ io.error(JSON.stringify({ error: nameError, published: false }));
68549
+ return 2;
68550
+ }
68551
+ const templateAppId = TEMPLATE_APP_PREFIX + name;
68552
+ const buildDir = materializeForPreview(appDir2, name, meta.title);
68553
+ if (buildDir !== appDir2) publishScratch.push(buildDir);
68554
+ const app = await appId(buildDir);
68555
+ const strict = flags.strict === true;
68556
+ const force = flags.force === true;
68557
+ const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
68558
+ const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
68559
+ const out = mkdtempSync4(join60(tmpdir5(), "himi-template-"));
68560
+ publishScratch.push(out);
68561
+ const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
68562
+ const outcome = await buildReleaseFromContent2(buildDir, out, {
68563
+ ...hasWorkers ? { tier5Dir: bundleOutDir2(buildDir) } : {},
68564
+ collect: { lint: true, strict }
68565
+ });
68566
+ const scaffoldSourceFindings = resolve47(sourceDir) === resolve47(appDir2) ? [] : [
68567
+ ...tokenColorIssues(sourceDir),
68568
+ ...surfaceIssues(sourceDir, void 0, strict),
68569
+ ...await fontIssues(sourceDir, strict),
68570
+ ...storesV1Issues(sourceDir, strict)
68571
+ ].map((issue2) => ({
68572
+ ...issue2,
68573
+ // Say WHICH tree. The same message against two trees means two different fixes,
68574
+ // and the scaffold source is the one the author is least likely to be looking at.
68575
+ message: `scaffold source (--source-dir ${sourceDir}): ${issue2.message}`
68627
68576
  }));
68628
- return 1;
68629
- }
68630
- if (sendPayload.sourceGz) {
68631
- const { gzipSync: gz } = await import("node:zlib");
68632
- const probeApp = templateAppId;
68633
- let probeErr;
68577
+ const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
68578
+ const { report: report2, hasErrors } = summarizeLint(app, bres, outcome, strict, [
68579
+ ...await cmsIssues(buildDir, app, strict, { local: true }),
68580
+ ...surfaceIssues(buildDir, outcome.surfaceNav, strict),
68581
+ ...tokenColorIssues(buildDir),
68582
+ ...await fontIssues(buildDir, strict),
68583
+ ...storesV1Issues(buildDir, strict),
68584
+ ...await navIssues2(buildDir, strict),
68585
+ ...scaffoldSourceFindings
68586
+ ]);
68587
+ if (hasErrors && !force) {
68588
+ print({ ...report2, published: false, hint: "lint found errors \u2014 fix them, or re-run with --force to publish anyway" });
68589
+ return 1;
68590
+ }
68591
+ const payload = await loadReleaseFromDir(app, out);
68592
+ const previewAuthError = templatePreviewAuthError(payload.appConfig);
68593
+ if (previewAuthError) {
68594
+ io.error(JSON.stringify({ error: previewAuthError, published: false }));
68595
+ return 1;
68596
+ }
68597
+ const leaked = sentineledFiles({
68598
+ ...payload.screens,
68599
+ ...payload.appConfig ? { "app-config.json": payload.appConfig } : {},
68600
+ ...payload.tokens ? { "tokens.json": payload.tokens } : {}
68601
+ });
68602
+ if (leaked.length > 0) {
68603
+ io.error(JSON.stringify({
68604
+ error: `built release still contains __HIMI_APP_*__ scaffold markers in: ${leaked.join(", ")}`,
68605
+ 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"
68606
+ }));
68607
+ return 1;
68608
+ }
68609
+ const iconPath = str6(flags.icon) ? resolve47(str6(flags.icon)) : templateIconPath(name);
68610
+ let iconHash;
68611
+ if (iconPath) {
68612
+ try {
68613
+ const bytes2 = readFileSync75(iconPath);
68614
+ payload.icon = bytes2.toString("base64");
68615
+ iconHash = createHash27("sha256").update(bytes2).digest("hex");
68616
+ } catch {
68617
+ }
68618
+ }
68619
+ payload.app = templateAppId;
68620
+ payload.manifest = { ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} };
68621
+ const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
68622
+ if (Object.keys(files).length === 0) {
68623
+ io.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
68624
+ return 1;
68625
+ }
68626
+ payload.source = { files, meta };
68627
+ 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));
68628
+ const templateArchive = collectAppSource2(sourceDir);
68629
+ const templateSecrets = secretFindings2(templateArchive.entries);
68630
+ if (templateSecrets.length > 0) {
68631
+ io.error(JSON.stringify({
68632
+ error: `refusing to publish: ${templateSecrets[0].path} looks like it contains ${templateSecrets[0].reason}`,
68633
+ file: templateSecrets[0].path,
68634
+ hint: "a template's source is served PUBLICLY \u2014 remove it from the tree before publishing"
68635
+ }));
68636
+ return 1;
68637
+ }
68638
+ const templateZip = zipAppSource2(templateArchive.entries);
68639
+ const templateSourceB64 = templateZip.toString("base64");
68640
+ if (templateSourceB64.length > MAX_APP_SOURCE_BYTES2) {
68641
+ const templateLargest = largestEntries2(templateArchive.entries);
68642
+ io.error(JSON.stringify({
68643
+ ok: false,
68644
+ template: name,
68645
+ app: templateAppId,
68646
+ published: false,
68647
+ error: "app_source_too_large",
68648
+ archivedBytes: templateZip.length,
68649
+ encodedBytes: templateSourceB64.length,
68650
+ limit: MAX_APP_SOURCE_BYTES2,
68651
+ files: templateArchive.entries.length,
68652
+ largest: templateLargest,
68653
+ // No `--no-source` here, deliberately: a template's archive is not optional the way
68654
+ // a push's is. `himi pull` restores templates through it, so a flag that dropped it
68655
+ // would publish a template nobody can restore — the failure #2423 exists to prevent.
68656
+ 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`
68657
+ }));
68658
+ return 1;
68659
+ }
68660
+ payload.appSource = templateSourceB64;
68661
+ const tplVis = visibilityFlag(flags, io);
68662
+ if (!tplVis) return 2;
68663
+ const publishMeta = {
68664
+ ...str6(flags.label) ? { label: str6(flags.label) } : {},
68665
+ ...str6(flags.notes) ? { notes: str6(flags.notes) } : {},
68666
+ ...tplVis.value ? { visibility: tplVis.value } : {}
68667
+ };
68668
+ let sendPayload = payload;
68669
+ let templateBodyBytes = publishReleaseBodyBytes(payload, publishMeta);
68670
+ let sourceGzChars;
68671
+ if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68672
+ const { gzipSync: gzipSync2 } = await import("node:zlib");
68673
+ const { source: plainSource, ...rest } = payload;
68674
+ const sourceGz = gzipSync2(Buffer.from(JSON.stringify(plainSource), "utf8"), { level: 9 }).toString("base64");
68675
+ sourceGzChars = sourceGz.length;
68676
+ const gzipped = { ...rest, sourceGz };
68677
+ const gzBytes = publishReleaseBodyBytes(gzipped, publishMeta);
68678
+ if (gzBytes <= MAX_PUBLISH_BODY_BYTES) {
68679
+ sendPayload = gzipped;
68680
+ templateBodyBytes = gzBytes;
68681
+ }
68682
+ }
68683
+ if (templateBodyBytes > MAX_PUBLISH_BODY_BYTES) {
68684
+ io.error(JSON.stringify({
68685
+ ok: false,
68686
+ template: name,
68687
+ app: templateAppId,
68688
+ published: false,
68689
+ error: "publish_body_too_large",
68690
+ bodyBytes: templateBodyBytes,
68691
+ limit: MAX_PUBLISH_BODY_BYTES,
68692
+ // Both halves, because "the body is too big" does not say which one to cut, and the
68693
+ // two answer to different edits: `sourceChars` shrinks by removing FILES from the
68694
+ // tree, `appSourceBytes` by removing bytes (an image, a fixture) that may not be
68695
+ // text at all. They overlap by design — the same tree, twice — so they are reported
68696
+ // separately rather than summed into a figure that double-counts it.
68697
+ sourceChars: JSON.stringify(payload.source).length,
68698
+ appSourceBytes: templateSourceB64.length,
68699
+ // The catalog view is reported COMPRESSED as well, because "trim the tree" was the
68700
+ // wrong instruction and cost real time: the collector already drops binaries,
68701
+ // symlinks and dist/node_modules/.git/.himi, so there is rarely anything to trim.
68702
+ // What the two figures actually say is whether compression can still save you --
68703
+ // `sourceGzChars` is what this publish already tried and what remains after it.
68704
+ sourceGzChars,
68705
+ 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/."
68706
+ }));
68707
+ return 1;
68708
+ }
68709
+ if (sendPayload.sourceGz) {
68710
+ const { gzipSync: gz } = await import("node:zlib");
68711
+ const probeApp = templateAppId;
68712
+ let probeErr;
68713
+ try {
68714
+ const probe = await client2().publishRelease({
68715
+ app: probeApp,
68716
+ manifest: { releaseId: "preflight", gitSha: "0".repeat(40), app: probeApp, minBinaryVersion: null, screens: {}, bundles: {} },
68717
+ screens: {},
68718
+ sourceGz: gz(Buffer.from("null")).toString("base64")
68719
+ });
68720
+ probeErr = probe.json?.error ?? probe.status;
68721
+ } catch (e) {
68722
+ const target = targetName();
68723
+ io.error(JSON.stringify(serveUnreachableError(target, serveBase(), e.message)));
68724
+ return 1;
68725
+ }
68726
+ if (probeErr !== "source_gz_not_an_object") {
68727
+ io.error(JSON.stringify({
68728
+ ok: false,
68729
+ template: name,
68730
+ app: templateAppId,
68731
+ published: false,
68732
+ error: "serve_plane_lacks_source_gz",
68733
+ probe: probeErr,
68734
+ bodyBytes: templateBodyBytes,
68735
+ limit: MAX_PUBLISH_BODY_BYTES,
68736
+ 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.`
68737
+ }));
68738
+ return 1;
68739
+ }
68740
+ }
68741
+ let r;
68634
68742
  try {
68635
- const probe = await client2().publishRelease({
68636
- app: probeApp,
68637
- manifest: { releaseId: "preflight", gitSha: "0".repeat(40), app: probeApp, minBinaryVersion: null, screens: {}, bundles: {} },
68638
- screens: {},
68639
- sourceGz: gz(Buffer.from("null")).toString("base64")
68640
- });
68641
- probeErr = probe.json?.error ?? probe.status;
68743
+ r = await client2().publishRelease(sendPayload, publishMeta);
68642
68744
  } catch (e) {
68643
68745
  const target = targetName();
68644
68746
  io.error(JSON.stringify(serveUnreachableError(target, serveBase(), e.message)));
68645
68747
  return 1;
68646
68748
  }
68647
- if (probeErr !== "source_gz_not_an_object") {
68749
+ if (!r.ok) {
68750
+ print({ ok: false, status: r.status, body: r.json ?? r.text });
68751
+ return 1;
68752
+ }
68753
+ if (sendPayload.sourceGz && r.json?.template !== true) {
68648
68754
  io.error(JSON.stringify({
68649
68755
  ok: false,
68650
68756
  template: name,
68651
68757
  app: templateAppId,
68652
68758
  published: false,
68653
- error: "serve_plane_lacks_source_gz",
68654
- probe: probeErr,
68655
- bodyBytes: templateBodyBytes,
68656
- limit: MAX_PUBLISH_BODY_BYTES,
68657
- 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.`
68759
+ error: "serve_plane_ignored_source_gz",
68760
+ 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.`
68658
68761
  }));
68659
68762
  return 1;
68660
68763
  }
68661
- }
68662
- let r;
68663
- try {
68664
- r = await client2().publishRelease(sendPayload, publishMeta);
68665
- } catch (e) {
68666
- const target = targetName();
68667
- io.error(JSON.stringify(serveUnreachableError(target, serveBase(), e.message)));
68668
- return 1;
68669
- }
68670
- if (!r.ok) {
68671
- print({ ok: false, status: r.status, body: r.json ?? r.text });
68672
- return 1;
68673
- }
68674
- if (sendPayload.sourceGz && r.json?.template !== true) {
68675
- io.error(JSON.stringify({
68676
- ok: false,
68764
+ const releaseId = r.json?.releaseId ?? payload.manifest.releaseId;
68765
+ const targetFlag = str6(flags.target) ? ` --target ${str6(flags.target)}` : "";
68766
+ print({
68767
+ ok: true,
68677
68768
  template: name,
68678
68769
  app: templateAppId,
68679
- published: false,
68680
- error: "serve_plane_ignored_source_gz",
68681
- 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.`
68682
- }));
68683
- return 1;
68770
+ releaseId,
68771
+ title: meta.title,
68772
+ tags: meta.tags,
68773
+ icon: Boolean(iconHash),
68774
+ ...meta.accent ? { accent: meta.accent } : {},
68775
+ ...meta.palette ? { palette: meta.palette } : {},
68776
+ sourceFiles: Object.keys(files).length,
68777
+ ...hasErrors && force ? { forcedPastErrors: true } : {},
68778
+ initWith: `himi init <new-app-name> --template ${name} --remote${targetFlag}`
68779
+ });
68780
+ return 0;
68781
+ } finally {
68782
+ for (const dir of publishScratch) {
68783
+ try {
68784
+ rmSync14(dir, { recursive: true, force: true });
68785
+ } catch (e) {
68786
+ io.error(JSON.stringify({ warning: `could not remove temp dir ${dir}: ${e.message}` }));
68787
+ }
68788
+ }
68684
68789
  }
68685
- const releaseId = r.json?.releaseId ?? payload.manifest.releaseId;
68686
- const targetFlag = str6(flags.target) ? ` --target ${str6(flags.target)}` : "";
68687
- print({
68688
- ok: true,
68689
- template: name,
68690
- app: templateAppId,
68691
- releaseId,
68692
- title: meta.title,
68693
- tags: meta.tags,
68694
- icon: Boolean(iconHash),
68695
- ...meta.accent ? { accent: meta.accent } : {},
68696
- ...meta.palette ? { palette: meta.palette } : {},
68697
- sourceFiles: Object.keys(files).length,
68698
- ...hasErrors && force ? { forcedPastErrors: true } : {},
68699
- initWith: `himi init <new-app-name> --template ${name} --remote${targetFlag}`
68700
- });
68701
- return 0;
68702
68790
  }
68703
68791
  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]" }));
68704
68792
  return 2;
@@ -73486,7 +73574,9 @@ export {
73486
73574
  spawnUpdateRefresh,
73487
73575
  staleUpdateLockFile,
73488
73576
  switchFlagErrors,
73577
+ templateNameError,
73489
73578
  templatePreviewAuthError,
73579
+ templatePublishName,
73490
73580
  tokenCoversApp,
73491
73581
  trustedConfiguredRegistry,
73492
73582
  updateCachePath,
@@ -116,6 +116,22 @@ himi template publish --target bo # from the app dir
116
116
  himi template publish --dir <app> --source-dir <dir> --name <name> --target bo
117
117
  ```
118
118
 
119
+ A **template tree that still carries the `__HIMI_APP_NAME__` placeholder** (a generated
120
+ template, as `himi init` receives it) publishes with `--dir` alone:
121
+
122
+ ```sh
123
+ himi template publish --dir <template-dir> --target bo
124
+ ```
125
+
126
+ Its `himalaya.content.json` name is the placeholder, so the template name comes from the
127
+ **directory name**, and the preview is built from a substituted copy titled with the template's
128
+ own `title`. Pass `--name` only to publish under a different name. Every name, given or
129
+ derived, must be a lowercase kebab id short enough to fit once `himi-template-` is prefixed; a
130
+ name that fails is refused before anything is built.
131
+
132
+ A published template does not follow its source: after changing a template, publish it again so
133
+ `himi template list --remote` and `himi init … --remote` serve the new version.
134
+
119
135
  `publish` builds a previewable release from the app and attaches the editable scaffold source,
120
136
  sentinel-izing identity so it re-parameterizes on the next `himi init`. It then appears in
121
137
  `himi template list --remote` and is scaffoldable with `himi init … --remote`. (Only the app
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1042.0",
3
+ "version": "0.1044.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -65,5 +65,5 @@
65
65
  "artifactId": "himalaya-cli"
66
66
  }
67
67
  },
68
- "falconPackageHash": "d80f9cca0922b2f864a60197972f9e23f2e17ddc1e40669cdf7a63a6"
68
+ "falconPackageHash": "7c2534e837382316a3416a0e6225572fbc86b02c8b5b35cea3d7530b"
69
69
  }