@wix/himalaya-cli 0.1454.0 → 0.1456.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
@@ -4346,8 +4346,8 @@ function committedProvisioningPath(name) {
4346
4346
  function committedShellFor(path2) {
4347
4347
  const normalized = path2.replace(/\\/g, "/");
4348
4348
  return SHELL_NAMES2.find((name) => {
4349
- const relative17 = committedProvisioningPath(name);
4350
- return normalized === relative17 || normalized.endsWith(`/${relative17}`);
4349
+ const relative18 = committedProvisioningPath(name);
4350
+ return normalized === relative18 || normalized.endsWith(`/${relative18}`);
4351
4351
  });
4352
4352
  }
4353
4353
  function isBrandable(repoRoot2, name) {
@@ -7070,9 +7070,9 @@ function compileV2(html, profile, options) {
7070
7070
  if (!row) error(el2, "HHP-BINDING-SCOPE", "Item bindings require a keyed list template.");
7071
7071
  return raw;
7072
7072
  }
7073
- const relative17 = statePath(raw);
7074
- if (!info4?.paths.has(relative17)) error(el2, "HHP-BINDING-PATH", `Unknown controller state path ${relative17}.`);
7075
- return `$service:${serviceName}.${relative17}`;
7073
+ const relative18 = statePath(raw);
7074
+ if (!info4?.paths.has(relative18)) error(el2, "HHP-BINDING-PATH", `Unknown controller state path ${relative18}.`);
7075
+ return `$service:${serviceName}.${relative18}`;
7076
7076
  }
7077
7077
  const read = (value, dotted) => dotted.split(".").reduce((current, key3) => current && typeof current === "object" && Object.hasOwn(current, key3) ? current[key3] : void 0, value);
7078
7078
  function rowValues(row, raw) {
@@ -14624,6 +14624,277 @@ var init_loader = __esm({
14624
14624
  }
14625
14625
  });
14626
14626
 
14627
+ // ../app-icons/pixels.ts
14628
+ var pixels_exports = {};
14629
+ __export(pixels_exports, {
14630
+ LETTERBOX_ERROR_FRACTION: () => LETTERBOX_ERROR_FRACTION,
14631
+ LETTERBOX_WARN_FRACTION: () => LETTERBOX_WARN_FRACTION,
14632
+ NORMALIZE_FIT_FRACTION: () => NORMALIZE_FIT_FRACTION,
14633
+ PIXEL_TOOLCHAIN_HINT: () => PIXEL_TOOLCHAIN_HINT,
14634
+ PixelToolchainUnavailable: () => PixelToolchainUnavailable,
14635
+ SVG_RASTER_MAX_EDGE: () => SVG_RASTER_MAX_EDGE,
14636
+ appStoreIconPixelFindings: () => appStoreIconPixelFindings,
14637
+ derivePlayListingIcon: () => derivePlayListingIcon,
14638
+ inspectIconPixels: () => inspectIconPixels,
14639
+ normalizeSourceIcon: () => normalizeSourceIcon,
14640
+ pixelToolchainAvailable: () => pixelToolchainAvailable,
14641
+ pixelToolchainProblem: () => pixelToolchainProblem,
14642
+ rasterizeSvg: () => rasterizeSvg,
14643
+ renderGlyphTile: () => renderGlyphTile
14644
+ });
14645
+ function isNotInstalled(e2) {
14646
+ const code = e2?.code;
14647
+ return code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND";
14648
+ }
14649
+ function messageOf(e2) {
14650
+ return e2 instanceof Error ? e2.message : String(e2);
14651
+ }
14652
+ async function loadSharp() {
14653
+ if (cachedSharp) return cachedSharp;
14654
+ const failures = [];
14655
+ let presentButBroken = false;
14656
+ try {
14657
+ const { createRequire: createRequire4 } = await import("node:module");
14658
+ cachedSharp = createRequire4(import.meta.url)("sharp");
14659
+ lastProblem = null;
14660
+ return cachedSharp;
14661
+ } catch (e2) {
14662
+ presentButBroken ||= !isNotInstalled(e2);
14663
+ failures.push(`require: ${messageOf(e2)}`);
14664
+ }
14665
+ try {
14666
+ cachedSharp = (await import("sharp")).default;
14667
+ lastProblem = null;
14668
+ return cachedSharp;
14669
+ } catch (e2) {
14670
+ presentButBroken ||= !isNotInstalled(e2);
14671
+ failures.push(`import: ${messageOf(e2)}`);
14672
+ }
14673
+ lastProblem = presentButBroken ? `the image toolchain (sharp) is installed but could not be loaded \u2014 ${failures.join("; ")}` : PIXEL_TOOLCHAIN_HINT;
14674
+ return null;
14675
+ }
14676
+ async function pixelToolchainAvailable() {
14677
+ return await loadSharp() !== null;
14678
+ }
14679
+ async function pixelToolchainProblem() {
14680
+ return await loadSharp() === null ? lastProblem ?? PIXEL_TOOLCHAIN_HINT : null;
14681
+ }
14682
+ async function sharpOrThrow() {
14683
+ const s2 = await loadSharp();
14684
+ if (!s2) throw new PixelToolchainUnavailable(lastProblem ?? PIXEL_TOOLCHAIN_HINT);
14685
+ return s2;
14686
+ }
14687
+ async function inspectIconPixels(png) {
14688
+ const sharp = await sharpOrThrow();
14689
+ const { data, info: info4 } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
14690
+ const { width, height, channels: channels3 } = info4;
14691
+ if (channels3 !== 4) throw new Error(`app-icons: expected RGBA after ensureAlpha, got ${channels3} channels`);
14692
+ let minAlpha = 255;
14693
+ let transparent = 0;
14694
+ for (let i2 = 3; i2 < data.length; i2 += 4) {
14695
+ const a2 = data[i2];
14696
+ if (a2 < minAlpha) minAlpha = a2;
14697
+ if (a2 <= ALPHA_OPAQUE) transparent++;
14698
+ }
14699
+ const probe = Math.max(1, Math.round(Math.min(width, height) * CORNER_PROBE));
14700
+ let minCornerAlpha = 255;
14701
+ for (const [x0, y0] of [
14702
+ [0, 0],
14703
+ [width - probe, 0],
14704
+ [0, height - probe],
14705
+ [width - probe, height - probe]
14706
+ ]) {
14707
+ for (let y2 = y0; y2 < y0 + probe; y2++) {
14708
+ for (let x2 = x0; x2 < x0 + probe; x2++) {
14709
+ const a2 = data[(y2 * width + x2) * 4 + 3];
14710
+ if (a2 < minCornerAlpha) minCornerAlpha = a2;
14711
+ }
14712
+ }
14713
+ }
14714
+ const inset = Math.round(Math.min(width, height) * INTERIOR_INSET);
14715
+ let interiorMinAlpha = 255;
14716
+ for (let y2 = inset; y2 < height - inset; y2++) {
14717
+ for (let x2 = inset; x2 < width - inset; x2++) {
14718
+ const a2 = data[(y2 * width + x2) * 4 + 3];
14719
+ if (a2 < interiorMinAlpha) interiorMinAlpha = a2;
14720
+ }
14721
+ }
14722
+ return {
14723
+ width,
14724
+ height,
14725
+ minAlpha,
14726
+ transparentFraction: transparent / (width * height),
14727
+ minCornerAlpha,
14728
+ interiorMinAlpha,
14729
+ // `minAlpha <= ALPHA_OPAQUE` IS "has transparency", and the caller has just computed
14730
+ // it — contentBox used to re-derive it with a full extra sweep of the raster.
14731
+ contentBox: contentBox(data, width, height, minAlpha <= ALPHA_OPAQUE)
14732
+ };
14733
+ }
14734
+ function contentBox(data, width, height, hasTransparency) {
14735
+ const at2 = (x2, y2) => (y2 * width + x2) * 4;
14736
+ const bg = [data[at2(0, 0)], data[at2(0, 0) + 1], data[at2(0, 0) + 2]];
14737
+ const isInk = (x2, y2) => {
14738
+ const i2 = at2(x2, y2);
14739
+ if (hasTransparency) return data[i2 + 3] > ALPHA_OPAQUE;
14740
+ return Math.abs(data[i2] - bg[0]) > INK_TOLERANCE || Math.abs(data[i2 + 1] - bg[1]) > INK_TOLERANCE || Math.abs(data[i2 + 2] - bg[2]) > INK_TOLERANCE;
14741
+ };
14742
+ let minX = width, minY = height, maxX = -1, maxY = -1;
14743
+ for (let y2 = 0; y2 < height; y2++) {
14744
+ for (let x2 = 0; x2 < width; x2++) {
14745
+ if (!isInk(x2, y2)) continue;
14746
+ if (x2 < minX) minX = x2;
14747
+ if (x2 > maxX) maxX = x2;
14748
+ if (y2 < minY) minY = y2;
14749
+ if (y2 > maxY) maxY = y2;
14750
+ }
14751
+ }
14752
+ if (maxX < 0) return null;
14753
+ return {
14754
+ widthFraction: (maxX - minX + 1) / width,
14755
+ heightFraction: (maxY - minY + 1) / height
14756
+ };
14757
+ }
14758
+ function appStoreIconPixelFindings(f2) {
14759
+ const out2 = [];
14760
+ const preMasked = f2.interiorMinAlpha > ALPHA_OPAQUE && f2.minCornerAlpha <= ALPHA_OPAQUE && f2.transparentFraction <= PRE_MASK_MAX_TRANSPARENT_FRACTION;
14761
+ if (preMasked) {
14762
+ out2.push({
14763
+ level: "error",
14764
+ message: "App icon looks pre-masked: its corners are transparent while the middle is opaque. iOS and Play both apply their own corner mask, so rounded artwork is rounded twice and looks visibly wrong on device \u2014 and the transparency itself is an App Store rejection (ITMS-90717 / ITMS-90647). Ship a full-bleed square with 90-degree corners and no inset."
14765
+ });
14766
+ } else if (f2.minAlpha <= ALPHA_OPAQUE) {
14767
+ out2.push({
14768
+ level: "error",
14769
+ message: `App icon has transparent pixels (${(f2.transparentFraction * 100).toFixed(1)}% of the canvas, lowest alpha ${f2.minAlpha}). An App Store icon may carry an alpha channel but no transparent regions \u2014 Apple rejects this as ITMS-90717 / ITMS-90647. Flatten it onto an opaque background (\`himi icon set <file>\` does this for you).`
14770
+ });
14771
+ }
14772
+ if (f2.contentBox === null) {
14773
+ out2.push({
14774
+ level: "error",
14775
+ message: "App icon is a single flat colour with no distinguishable artwork \u2014 the mark is not visible in it. Supply art that contrasts with the background, or use `himi icon generate` for a glyph tile."
14776
+ });
14777
+ return out2;
14778
+ }
14779
+ const shorter = Math.min(f2.contentBox.widthFraction, f2.contentBox.heightFraction);
14780
+ if (shorter < LETTERBOX_ERROR_FRACTION) {
14781
+ out2.push({
14782
+ level: "error",
14783
+ message: `App icon artwork fills only ${(shorter * 100).toFixed(0)}% of the canvas on its shorter side, so it reads as a speck on a home screen. This is what a wide wordmark centred in a square looks like \u2014 crop to the mark, or use a monogram/glyph instead of the wordmark.`
14784
+ });
14785
+ } else if (shorter < LETTERBOX_WARN_FRACTION) {
14786
+ out2.push({
14787
+ level: "warn",
14788
+ message: `App icon artwork fills ${(shorter * 100).toFixed(0)}% of the canvas on its shorter side \u2014 it will read as a thin strip at home-screen size. Consider cropping to the mark.`
14789
+ });
14790
+ }
14791
+ return out2;
14792
+ }
14793
+ function luminance([r2, g2, b2]) {
14794
+ const lin = [r2, g2, b2].map((v2) => {
14795
+ const c2 = v2 / 255;
14796
+ return c2 <= 0.04045 ? c2 / 12.92 : ((c2 + 0.055) / 1.055) ** 2.4;
14797
+ });
14798
+ return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
14799
+ }
14800
+ function contrastRatio(a2, b2) {
14801
+ const [hi2, lo2] = [luminance(a2), luminance(b2)].sort((x2, y2) => y2 - x2);
14802
+ return (hi2 + 0.05) / (lo2 + 0.05);
14803
+ }
14804
+ async function contrastingBackdrop(mark, requested) {
14805
+ const sharp = await sharpOrThrow();
14806
+ const { dominant } = await sharp(mark).stats();
14807
+ const ink = [dominant.r, dominant.g, dominant.b];
14808
+ const want = hexToRgb(requested);
14809
+ if (!want || contrastRatio(ink, want) >= MIN_BACKDROP_CONTRAST) return requested;
14810
+ const towardsWhite = luminance(ink) < 0.5;
14811
+ for (const amount of [0.35, 0.55, 0.75, 0.9]) {
14812
+ const candidate = mix(want, towardsWhite ? [255, 255, 255] : [0, 0, 0], amount);
14813
+ if (contrastRatio(ink, candidate) >= MIN_BACKDROP_CONTRAST) return rgbToHex(candidate);
14814
+ }
14815
+ return towardsWhite ? "#FFFFFF" : "#000000";
14816
+ }
14817
+ function hexToRgb(hex2) {
14818
+ const m2 = /^#([0-9a-fA-F]{6})$/.exec(hex2);
14819
+ if (!m2) return null;
14820
+ const n2 = parseInt(m2[1], 16);
14821
+ return [n2 >> 16 & 255, n2 >> 8 & 255, n2 & 255];
14822
+ }
14823
+ function mix(a2, b2, t2) {
14824
+ return [0, 1, 2].map((i2) => Math.round(a2[i2] + (b2[i2] - a2[i2]) * t2));
14825
+ }
14826
+ function rgbToHex([r2, g2, b2]) {
14827
+ return `#${[r2, g2, b2].map((v2) => v2.toString(16).padStart(2, "0")).join("")}`.toUpperCase();
14828
+ }
14829
+ async function normalizeSourceIcon(input, opts) {
14830
+ const sharp = await sharpOrThrow();
14831
+ const size = opts.size ?? 1024;
14832
+ const fit2 = opts.fit ?? NORMALIZE_FIT_FRACTION;
14833
+ const inner = Math.max(1, Math.round(size * fit2));
14834
+ const pad3 = Math.round((size - inner) / 2);
14835
+ let trimmed;
14836
+ try {
14837
+ trimmed = await sharp(input).ensureAlpha().trim().png().toBuffer();
14838
+ } catch {
14839
+ trimmed = await sharp(input).ensureAlpha().png().toBuffer();
14840
+ }
14841
+ const mark = await sharp(trimmed).resize(inner, inner, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toBuffer();
14842
+ const background = await contrastingBackdrop(mark, opts.background);
14843
+ return sharp({
14844
+ create: {
14845
+ width: size,
14846
+ height: size,
14847
+ channels: 4,
14848
+ background: { r: 0, g: 0, b: 0, alpha: 0 }
14849
+ }
14850
+ }).composite([{ input: mark, top: pad3, left: pad3 }]).flatten({ background }).removeAlpha().png({ palette: false }).toBuffer();
14851
+ }
14852
+ async function derivePlayListingIcon(master, size = 512) {
14853
+ const sharp = await sharpOrThrow();
14854
+ return sharp(master).resize(size, size).ensureAlpha().png({ palette: false }).toBuffer();
14855
+ }
14856
+ async function renderGlyphTile(svg, background, size = 1024) {
14857
+ const sharp = await sharpOrThrow();
14858
+ return sharp(Buffer.from(svg)).resize(size, size).flatten({ background }).removeAlpha().png({ palette: false }).toBuffer();
14859
+ }
14860
+ async function rasterizeSvg(svg, scale = 3) {
14861
+ const sharp = await loadSharp();
14862
+ if (!sharp) return null;
14863
+ const { width = 0, height = 0 } = await sharp(svg, { limitInputPixels: false }).metadata();
14864
+ const edge = Math.max(width, height);
14865
+ const density = edge > 0 ? Math.max(1, Math.min(72 * scale, 72 * SVG_RASTER_MAX_EDGE / edge)) : 72 * scale;
14866
+ const decoded = width * density / 72 * (height * density / 72);
14867
+ if (decoded > SHARP_INPUT_PIXEL_LIMIT) {
14868
+ throw new Error(`SVG is too large to rasterize: ${width}x${height} intrinsic size; give it a width/height (or viewBox) under ${Math.floor(72 * Math.sqrt(SHARP_INPUT_PIXEL_LIMIT))}px per side`);
14869
+ }
14870
+ return sharp(svg, { density }).resize({ width: SVG_RASTER_MAX_EDGE, height: SVG_RASTER_MAX_EDGE, fit: "inside", withoutEnlargement: true }).png().toBuffer();
14871
+ }
14872
+ var cachedSharp, lastProblem, PIXEL_TOOLCHAIN_HINT, PixelToolchainUnavailable, ALPHA_OPAQUE, CORNER_PROBE, INTERIOR_INSET, INK_TOLERANCE, PRE_MASK_MAX_TRANSPARENT_FRACTION, LETTERBOX_WARN_FRACTION, LETTERBOX_ERROR_FRACTION, NORMALIZE_FIT_FRACTION, MIN_BACKDROP_CONTRAST, SVG_RASTER_MAX_EDGE, SHARP_INPUT_PIXEL_LIMIT;
14873
+ var init_pixels = __esm({
14874
+ "../app-icons/pixels.ts"() {
14875
+ cachedSharp = null;
14876
+ lastProblem = null;
14877
+ PIXEL_TOOLCHAIN_HINT = "the image toolchain (sharp) is not installed \u2014 run `npm i -g sharp` to enable transparency, pre-masked-corner and letterbox checks";
14878
+ PixelToolchainUnavailable = class extends Error {
14879
+ constructor(reason = PIXEL_TOOLCHAIN_HINT) {
14880
+ super(`app-icons: ${reason}`);
14881
+ this.name = "PixelToolchainUnavailable";
14882
+ }
14883
+ };
14884
+ ALPHA_OPAQUE = 250;
14885
+ CORNER_PROBE = 0.06;
14886
+ INTERIOR_INSET = 0.2;
14887
+ INK_TOLERANCE = 24;
14888
+ PRE_MASK_MAX_TRANSPARENT_FRACTION = 0.1;
14889
+ LETTERBOX_WARN_FRACTION = 0.35;
14890
+ LETTERBOX_ERROR_FRACTION = 0.2;
14891
+ NORMALIZE_FIT_FRACTION = 0.82;
14892
+ MIN_BACKDROP_CONTRAST = 1.5;
14893
+ SVG_RASTER_MAX_EDGE = 2048;
14894
+ SHARP_INPUT_PIXEL_LIMIT = 16383 * 16383;
14895
+ }
14896
+ });
14897
+
14627
14898
  // ../../apps/himi-player/player-host-manifest.ts
14628
14899
  var PLAYER_HOST_MANIFEST;
14629
14900
  var init_player_host_manifest = __esm({
@@ -15011,8 +15282,8 @@ var init_hhp_command = __esm({
15011
15282
 
15012
15283
  // src/hhp.ts
15013
15284
  import { existsSync as existsSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, renameSync, rmSync as rmSync4, lstatSync as lstatSync5, readdirSync as readdirSync12 } from "node:fs";
15014
- import { dirname as dirname11, resolve as resolve17, relative as relative3, sep as sep6, join as join15 } from "node:path";
15015
- import { randomUUID } from "node:crypto";
15285
+ import { basename as basename7, dirname as dirname11, resolve as resolve17, relative as relative3, sep as sep6, join as join15 } from "node:path";
15286
+ import { createHash as createHash8, randomUUID } from "node:crypto";
15016
15287
  import ts3 from "typescript";
15017
15288
  function localPath(root, path2, from = join15(root, "hhp.json")) {
15018
15289
  if (/^[a-z]+:|^\/\//i.test(path2)) throw new Error("HHP source dependencies must be local files.");
@@ -15082,7 +15353,7 @@ export function withHhpScreens<T extends { screens: { exact: Record<string, any>
15082
15353
  for (const id of Object.keys(descriptors)) if (Object.hasOwn(app.screens.exact, id)) throw new Error("HHP screen conflicts with authored screen: " + id);
15083
15354
  const exact = Object.fromEntries(Object.entries(descriptors).map(([id, descriptor]) => [id, (ctx: { serverBaseUrl: string; bundleIntegrity(name: string): string | null; asset(name: string): {url:string} | null }) => {
15084
15355
  const result = structuredClone(descriptor);
15085
- const resolveAssets = (value: any, field = ""): any => { if (["url", "imageUrl", "src"].includes(field) && typeof value === "string" && value.startsWith("asset:")) { const asset = ctx.asset(value.slice(6)); if (!asset) throw new Error("Missing HHP asset " + value); return asset.url; } if (Array.isArray(value)) return value.map((child) => resolveAssets(child, field)); if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, resolveAssets(child, key)])); return value; }; result.payload = resolveAssets(result.payload);
15356
+ const resolveAssets = (value: any, field = ""): any => { if (["url", "imageUrl", "src"].includes(field) && typeof value === "string" && value.startsWith("asset:")) { const asset = ctx.asset(value.slice(6)); if (!asset) throw new Error("Missing HHP asset " + value); return asset.url.replace(/^[a-z][a-z0-9+.-]*:\\/\\/[^/?#]*/i, ""); } if (Array.isArray(value)) return value.map((child) => resolveAssets(child, field)); if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, resolveAssets(child, key)])); return value; }; result.payload = resolveAssets(result.payload);
15086
15357
  if (result.logicBundle) { const name = result.logicBundle.url.split("/").pop()!; const sri = ctx.bundleIntegrity(name); if (!sri) return null; result.logicBundle.url = ctx.serverBaseUrl + result.logicBundle.url; result.logicBundle.integrity = sri; }
15087
15358
  return result;
15088
15359
  }]));
@@ -15097,9 +15368,18 @@ async function compileContentHhp(root, opts = {}) {
15097
15368
  const diagnostics = [], results = [];
15098
15369
  const outputs = /* @__PURE__ */ new Map();
15099
15370
  const assets = collectAssets(root);
15371
+ const svgRefs = /* @__PURE__ */ new Map();
15372
+ const directRasters = /* @__PURE__ */ new Set();
15100
15373
  const assetName = (path2, from) => {
15101
15374
  const hit = path2.startsWith("asset:") ? assets.find((asset) => asset.name === path2.slice(6)) : assets.find((asset) => asset.path === localPath(root, path2, from));
15102
15375
  if (!hit) throw new Error(`Local resource ${path2} must exist in the app's declared assets directory.`);
15376
+ if (/\.svg$/i.test(hit.name) && !hit.name.startsWith(`${RASTER_DIR}/`)) {
15377
+ const ref = svgRefs.get(hit.path) ?? { name: hit.name, raster: rasterName(hit.name, readFileSync16(hit.path)), files: /* @__PURE__ */ new Set() };
15378
+ ref.files.add(from);
15379
+ svgRefs.set(hit.path, ref);
15380
+ return ref.raster;
15381
+ }
15382
+ if (hit.name.startsWith(`${RASTER_DIR}/`)) directRasters.add(hit.path);
15103
15383
  return hit.name;
15104
15384
  };
15105
15385
  const indexPath = localPath(root, "dev/index.ts"), originalIndex = readFileSync16(indexPath, "utf8"), wiredIndex = managedRegistry(originalIndex);
@@ -15146,6 +15426,28 @@ async function compileContentHhp(root, opts = {}) {
15146
15426
  const existing = (await loadContent(root, { fresh: true })).module.screens.exact;
15147
15427
  for (const id of Object.keys(manifest.screens)) if (Object.hasOwn(existing, id)) diagnostics.push({ code: "HHP-REGISTRY-CONFLICT", severity: "error", file: indexPath, line: 1, column: 1, message: `HHP screen ${id} conflicts with an authored screen.` });
15148
15428
  }
15429
+ const rasters = /* @__PURE__ */ new Map();
15430
+ for (const path2 of directRasters) rasters.set(path2, null);
15431
+ const assetsDir = findAssetsDir(root);
15432
+ for (const [path2, ref] of [...svgRefs].sort(([a2], [b2]) => a2.localeCompare(b2))) {
15433
+ let png;
15434
+ try {
15435
+ png = await (opts.rasterizeSvg ?? rasterizeSvg)(readFileSync16(path2));
15436
+ } catch (e2) {
15437
+ for (const file2 of ref.files) diagnostics.push({ code: "HHP-ASSET-SVG", severity: "error", file: file2, line: 1, column: 1, message: `Could not rasterize ${ref.name} (${e2.message.split("\n")[0]}). Fix the SVG, or export it as PNG or WebP.` });
15438
+ continue;
15439
+ }
15440
+ const target2 = assetsDir && join15(assetsDir, ref.raster);
15441
+ if (!png && target2 && existsExact(target2)) {
15442
+ rasters.set(target2, null);
15443
+ continue;
15444
+ }
15445
+ if (!png || !target2) {
15446
+ for (const file2 of ref.files) diagnostics.push({ code: "HHP-ASSET-SVG", severity: "error", file: file2, line: 1, column: 1, message: `${ref.name} is an SVG image, which iOS and Android cannot decode, and the image toolchain (sharp) that converts it to PNG is unavailable. Export it as PNG or WebP, or install sharp (\`npm i -g sharp\`).` });
15447
+ continue;
15448
+ }
15449
+ rasters.set(target2, png);
15450
+ }
15149
15451
  const ok2 = !diagnostics.some((d2) => d2.severity === "error");
15150
15452
  if (!ok2) return { ok: ok2, diagnostics, screens: Object.keys(manifest.screens), outputs: [] };
15151
15453
  outputs.set("dev/hhp/index.ts", registry(results));
@@ -15167,6 +15469,14 @@ async function compileContentHhp(root, opts = {}) {
15167
15469
  const path2 = localPath(root, rel);
15168
15470
  if (existsSync15(path2) && rel.endsWith(".ts") && !readFileSync16(path2, "utf8").startsWith(GENERATED.trim()) && !readFileSync16(path2, "utf8").startsWith("// GENERATED HHP v2 worker.")) throw new Error(`HHP output conflicts with authored file ${rel}.`);
15169
15471
  }
15472
+ for (const [path2, png] of rasters) {
15473
+ if (!png) continue;
15474
+ if (existsExact(path2) && readFileSync16(path2).equals(png)) continue;
15475
+ mkdirSync7(dirname11(path2), { recursive: true });
15476
+ writeFileSync7(`${path2}.tmp`, png);
15477
+ rmSync4(path2, { force: true });
15478
+ renameSync(`${path2}.tmp`, path2);
15479
+ }
15170
15480
  const target = localPath(root, "dev/hhp"), stage = localPath(root, `dev/.hhp-${randomUUID()}`), backup = `${stage}-old`;
15171
15481
  mkdirSync7(stage, { recursive: true });
15172
15482
  try {
@@ -15211,8 +15521,26 @@ async function compileContentHhp(root, opts = {}) {
15211
15521
  } finally {
15212
15522
  rmSync4(stage, { recursive: true, force: true });
15213
15523
  }
15524
+ pruneRasters(root, rasters);
15214
15525
  return result;
15215
15526
  }
15527
+ function pruneRasters(root, rasters) {
15528
+ const keep = new Set(rasters.keys());
15529
+ const assetsDir = findAssetsDir(root);
15530
+ const dir = assetsDir && join15(assetsDir, RASTER_DIR);
15531
+ if (!dir || !existsSync15(dir)) return;
15532
+ const walk2 = (abs) => {
15533
+ for (const entry of readdirSync12(abs, { withFileTypes: true })) {
15534
+ const child = join15(abs, entry.name);
15535
+ if (entry.isDirectory()) {
15536
+ walk2(child);
15537
+ if (!readdirSync12(child).length) rmSync4(child, { recursive: true });
15538
+ } else if (entry.isFile() && RASTER_FILE.test(entry.name) && !keep.has(child)) rmSync4(child);
15539
+ }
15540
+ };
15541
+ walk2(dir);
15542
+ if (!readdirSync12(dir).length) rmSync4(dir, { recursive: true });
15543
+ }
15216
15544
  async function initHhpScreen(root, id) {
15217
15545
  if (!/^[a-z][a-z0-9_]*$/.test(id)) throw new Error("Use a lowercase screen name with underscores.");
15218
15546
  const manifest = readHhpManifest(root) ?? { version: "hhp/2", screens: {} };
@@ -15270,7 +15598,7 @@ function hhpEditorSettings(source) {
15270
15598
  }
15271
15599
  return JSON.stringify(settings, null, 2) + "\n";
15272
15600
  }
15273
- var GENERATED, BEGIN, END;
15601
+ var GENERATED, BEGIN, END, RASTER_DIR, existsExact, rasterName, RASTER_FILE;
15274
15602
  var init_hhp = __esm({
15275
15603
  "src/hhp.ts"() {
15276
15604
  init_catalog();
@@ -15279,9 +15607,14 @@ var init_hhp = __esm({
15279
15607
  init_loader();
15280
15608
  init_preview_surfaces();
15281
15609
  init_app_assets();
15610
+ init_pixels();
15282
15611
  GENERATED = "// GENERATED HHP v2 \u2014 edit screens/, then run himi build.\n";
15283
15612
  BEGIN = "// himi:hhp:begin";
15284
15613
  END = "// himi:hhp:end";
15614
+ RASTER_DIR = "hhp-raster";
15615
+ existsExact = (path2) => existsSync15(path2) && readdirSync12(dirname11(path2)).includes(basename7(path2));
15616
+ rasterName = (svgName, svg) => `${RASTER_DIR}/${svgName.split("/").join("__")}.${createHash8("sha256").update(svg).digest("hex").slice(0, 12)}.png`;
15617
+ RASTER_FILE = /\.svg\.[0-9a-f]{12}\.png$/i;
15285
15618
  }
15286
15619
  });
15287
15620
 
@@ -17842,7 +18175,7 @@ function relativeLuminance(hex2) {
17842
18175
  const lin = [1, 3, 5].map((i2) => parseInt(hex2.slice(i2, i2 + 2), 16) / 255).map((v2) => v2 <= 0.03928 ? v2 / 12.92 : ((v2 + 0.055) / 1.055) ** 2.4);
17843
18176
  return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
17844
18177
  }
17845
- function contrastRatio(a2, b2) {
18178
+ function contrastRatio2(a2, b2) {
17846
18179
  const la2 = relativeLuminance(a2);
17847
18180
  const lb = relativeLuminance(b2);
17848
18181
  return (Math.max(la2, lb) + 0.05) / (Math.min(la2, lb) + 0.05);
@@ -17930,7 +18263,7 @@ function channel2(hex2, offset) {
17930
18263
  function toHex(r2, g2, b2) {
17931
18264
  return `#${[r2, g2, b2].map((value) => Math.round(value).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
17932
18265
  }
17933
- function mix(color, background, colorWeight) {
18266
+ function mix2(color, background, colorWeight) {
17934
18267
  const values = [1, 3, 5].map((offset) => channel2(color, offset) * colorWeight + channel2(background, offset) * (1 - colorWeight));
17935
18268
  return toHex(values[0], values[1], values[2]);
17936
18269
  }
@@ -17941,7 +18274,7 @@ function relativeLuminance2(color) {
17941
18274
  });
17942
18275
  return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
17943
18276
  }
17944
- function contrastRatio2(a2, b2) {
18277
+ function contrastRatio3(a2, b2) {
17945
18278
  const la2 = relativeLuminance2(a2);
17946
18279
  const lb = relativeLuminance2(b2);
17947
18280
  return (Math.max(la2, lb) + 0.05) / (Math.min(la2, lb) + 0.05);
@@ -17951,19 +18284,19 @@ function lstar(color) {
17951
18284
  return y2 <= 8856e-6 ? y2 * 903.3 : 116 * Math.cbrt(y2) - 16;
17952
18285
  }
17953
18286
  function deriveAccentSurface(accent, surface, weight) {
17954
- const tint = mix(accent, surface, weight);
17955
- if (contrastRatio2(tint, surface) >= ACCENT_SURFACE_MIN_DELTA) return tint;
18287
+ const tint = mix2(accent, surface, weight);
18288
+ if (contrastRatio3(tint, surface) >= ACCENT_SURFACE_MIN_DELTA) return tint;
17956
18289
  const away = relativeLuminance2(surface) < 0.5 ? "#FFFFFF" : "#000000";
17957
18290
  for (let w2 = 0.02; w2 <= 0.5; w2 += 0.02) {
17958
- const candidate = mix(away, surface, w2);
17959
- if (contrastRatio2(candidate, surface) >= ACCENT_SURFACE_MIN_DELTA) return candidate;
18291
+ const candidate = mix2(away, surface, w2);
18292
+ if (contrastRatio3(candidate, surface) >= ACCENT_SURFACE_MIN_DELTA) return candidate;
17960
18293
  }
17961
18294
  return tint;
17962
18295
  }
17963
18296
  function deriveSecondaryAccent(primary) {
17964
- const shade2 = mix(primary, "#000000", SECONDARY_SHADE_WEIGHT);
18297
+ const shade2 = mix2(primary, "#000000", SECONDARY_SHADE_WEIGHT);
17965
18298
  if (lstar(shade2) >= SECONDARY_MIN_LSTAR) return shade2;
17966
- return mix(primary, "#FFFFFF", SECONDARY_SHADE_WEIGHT);
18299
+ return mix2(primary, "#FFFFFF", SECONDARY_SHADE_WEIGHT);
17967
18300
  }
17968
18301
  function textOn(color) {
17969
18302
  const luminance2 = relativeLuminance2(color);
@@ -17973,42 +18306,42 @@ function textOn(color) {
17973
18306
  }
17974
18307
  function tuneDarkAccent(brand, darkSurface, lightSurface) {
17975
18308
  const kitInherentlyDark = lstar(lightSurface) < 50;
17976
- if (kitInherentlyDark && contrastRatio2(brand, darkSurface) >= DARK_ARM_TARGET_CONTRAST && lstar(brand) <= DARK_ARM_LSTAR_CAP) {
18309
+ if (kitInherentlyDark && contrastRatio3(brand, darkSurface) >= DARK_ARM_TARGET_CONTRAST && lstar(brand) <= DARK_ARM_LSTAR_CAP) {
17977
18310
  return brand;
17978
18311
  }
17979
18312
  let lift = DARK_ARM_MIN_LIFT;
17980
- let candidate = mix(brand, "#FFFFFF", 1 - lift);
17981
- while (contrastRatio2(candidate, darkSurface) < DARK_ARM_TARGET_CONTRAST && lift < 0.6) {
18313
+ let candidate = mix2(brand, "#FFFFFF", 1 - lift);
18314
+ while (contrastRatio3(candidate, darkSurface) < DARK_ARM_TARGET_CONTRAST && lift < 0.6) {
17982
18315
  lift += 0.04;
17983
- candidate = mix(brand, "#FFFFFF", 1 - lift);
18316
+ candidate = mix2(brand, "#FFFFFF", 1 - lift);
17984
18317
  }
17985
18318
  while (lstar(candidate) > DARK_ARM_LSTAR_CAP) {
17986
- const darker = mix(candidate, "#000000", 0.94);
17987
- if (contrastRatio2(darker, darkSurface) < DARK_ARM_FLOOR_CONTRAST) break;
18319
+ const darker = mix2(candidate, "#000000", 0.94);
18320
+ if (contrastRatio3(darker, darkSurface) < DARK_ARM_FLOOR_CONTRAST) break;
17988
18321
  candidate = darker;
17989
18322
  }
17990
18323
  return candidate;
17991
18324
  }
17992
18325
  function textOnArm(accent) {
17993
- return contrastRatio2("#FFFFFF", accent) >= 4.5 ? "#FFFFFF" : textOn(accent);
18326
+ return contrastRatio3("#FFFFFF", accent) >= 4.5 ? "#FFFFFF" : textOn(accent);
17994
18327
  }
17995
18328
  function tuneHighContrastAccent(brand, surface) {
17996
18329
  const towards = relativeLuminance2(surface) < 0.5 ? "#FFFFFF" : "#000000";
17997
18330
  let candidate = brand;
17998
- for (let lift = 0; lift <= 0.9 && contrastRatio2(candidate, surface) < HIGH_CONTRAST_TARGET; lift += 0.04) {
17999
- candidate = mix(brand, towards, 1 - lift);
18331
+ for (let lift = 0; lift <= 0.9 && contrastRatio3(candidate, surface) < HIGH_CONTRAST_TARGET; lift += 0.04) {
18332
+ candidate = mix2(brand, towards, 1 - lift);
18000
18333
  }
18001
18334
  return candidate;
18002
18335
  }
18003
18336
  function deriveAccentText(accent, surfaces) {
18004
18337
  const all = (typeof surfaces === "string" ? [surfaces] : [...surfaces]).map((s2) => s2.slice(0, 7));
18005
18338
  const page = all[0];
18006
- const worst = (color) => Math.min(...all.map((s2) => contrastRatio2(color, s2)));
18007
- const opaque = accent.length === 9 ? mix(accent.slice(0, 7), page, channel2(accent, 7) / 255) : accent;
18339
+ const worst = (color) => Math.min(...all.map((s2) => contrastRatio3(color, s2)));
18340
+ const opaque = accent.length === 9 ? mix2(accent.slice(0, 7), page, channel2(accent, 7) / 255) : accent;
18008
18341
  if (worst(opaque) >= ACCENT_TEXT_MIN_CONTRAST) return opaque;
18009
18342
  const pole = worst("#FFFFFF") >= worst("#000000") ? "#FFFFFF" : "#000000";
18010
18343
  for (let step = 1; step <= 100; step++) {
18011
- const candidate = mix(pole, opaque, step / 100);
18344
+ const candidate = mix2(pole, opaque, step / 100);
18012
18345
  if (worst(candidate) >= ACCENT_TEXT_MIN_CONTRAST) return candidate;
18013
18346
  }
18014
18347
  if (all.length > 1) {
@@ -18352,10 +18685,10 @@ function paintedOver(fg, bg) {
18352
18685
  function sameHueForContrast(fg, bg, target = AA_TEXT) {
18353
18686
  const base = fg.slice(0, 7);
18354
18687
  const opaqueBg = bg.slice(0, 7);
18355
- const pole = contrastRatio("#000000", opaqueBg) >= contrastRatio("#FFFFFF", opaqueBg) ? "#000000" : "#FFFFFF";
18688
+ const pole = contrastRatio2("#000000", opaqueBg) >= contrastRatio2("#FFFFFF", opaqueBg) ? "#000000" : "#FFFFFF";
18356
18689
  for (let step = 0; step <= 100; step++) {
18357
18690
  const candidate = mixHex(base, pole, 1 - step / 100);
18358
- if (contrastRatio(candidate, opaqueBg) >= target) return candidate;
18691
+ if (contrastRatio2(candidate, opaqueBg) >= target) return candidate;
18359
18692
  }
18360
18693
  return void 0;
18361
18694
  }
@@ -18377,7 +18710,7 @@ function contrastFindings(colors) {
18377
18710
  const painted2 = paintedOver(fg, bg);
18378
18711
  if (!painted2) continue;
18379
18712
  const opaqueBg = bg.slice(0, 7);
18380
- const ratio = contrastRatio(painted2, opaqueBg);
18713
+ const ratio = contrastRatio2(painted2, opaqueBg);
18381
18714
  if (ratio >= AA_TEXT) continue;
18382
18715
  const key3 = `${fgName}|${pair.bg}|${fg}|${bg}`;
18383
18716
  const same3 = findings.get(key3);
@@ -18385,10 +18718,10 @@ function contrastFindings(colors) {
18385
18718
  same3.schemes.push(scheme);
18386
18719
  continue;
18387
18720
  }
18388
- const darker = contrastRatio("#000000", opaqueBg) >= contrastRatio("#FFFFFF", opaqueBg);
18721
+ const darker = contrastRatio2("#000000", opaqueBg) >= contrastRatio2("#FFFFFF", opaqueBg);
18389
18722
  const ink = darker ? "#000000" : "#FFFFFF";
18390
- const suggestion = pair.suggest === "same-hue" ? sameHueForContrast(painted2, opaqueBg) : pair.suggest === "ink" && contrastRatio(ink, opaqueBg) >= AA_TEXT ? ink : void 0;
18391
- const fix = !suggestion ? ` Pick a ${fgName} that clears ${AA_TEXT}:1 against ${pair.bg}.` : pair.suggest === "same-hue" ? ` A ${darker ? "darker" : "lighter"} ${fgName} of the same hue that clears it is ${suggestion} (${contrastRatio(suggestion, opaqueBg).toFixed(2)}:1).` : ` ${suggestion} clears it (${contrastRatio(suggestion, opaqueBg).toFixed(2)}:1).`;
18723
+ const suggestion = pair.suggest === "same-hue" ? sameHueForContrast(painted2, opaqueBg) : pair.suggest === "ink" && contrastRatio2(ink, opaqueBg) >= AA_TEXT ? ink : void 0;
18724
+ const fix = !suggestion ? ` Pick a ${fgName} that clears ${AA_TEXT}:1 against ${pair.bg}.` : pair.suggest === "same-hue" ? ` A ${darker ? "darker" : "lighter"} ${fgName} of the same hue that clears it is ${suggestion} (${contrastRatio2(suggestion, opaqueBg).toFixed(2)}:1).` : ` ${suggestion} clears it (${contrastRatio2(suggestion, opaqueBg).toFixed(2)}:1).`;
18392
18725
  findings.set(key3, {
18393
18726
  key: key3,
18394
18727
  schemes: [scheme],
@@ -18405,7 +18738,7 @@ function sameHueClearingAll(fg, bgs, target = AA_TEXT) {
18405
18738
  for (let step = 0; step <= 100; step++) {
18406
18739
  for (const pole of poles) {
18407
18740
  const candidate = mixHex(base, pole, 1 - step / 100);
18408
- if (bgs.every((bg) => contrastRatio(candidate, bg) >= target)) return candidate;
18741
+ if (bgs.every((bg) => contrastRatio2(candidate, bg) >= target)) return candidate;
18409
18742
  }
18410
18743
  }
18411
18744
  return void 0;
@@ -18432,8 +18765,8 @@ function accentOnSurfacesFor(fgName, role, colors, declared, findings) {
18432
18765
  const page = pageRaw && paintedOver(fg, pageRaw) ? pageRaw.slice(0, 7) : void 0;
18433
18766
  const opaqueBg = bg.slice(0, 7);
18434
18767
  if (page === opaqueBg) continue;
18435
- if (page && contrastRatio(paintedOver(fg, pageRaw), page) < AA_TEXT) continue;
18436
- const ratio = contrastRatio(painted2, opaqueBg);
18768
+ if (page && contrastRatio2(paintedOver(fg, pageRaw), page) < AA_TEXT) continue;
18769
+ const ratio = contrastRatio2(painted2, opaqueBg);
18437
18770
  if (ratio >= AA_TEXT) continue;
18438
18771
  const key3 = `${fgName}|${surface}|${fg}|${bg}|${page ?? ""}`;
18439
18772
  const same3 = findings.get(key3);
@@ -18442,9 +18775,9 @@ function accentOnSurfacesFor(fgName, role, colors, declared, findings) {
18442
18775
  continue;
18443
18776
  }
18444
18777
  const opposite = page !== void 0 && onLightSide(page) !== onLightSide(opaqueBg);
18445
- const noSingle = page !== void 0 && opposite && contrastRatio(page, opaqueBg) < AA_TEXT * AA_TEXT;
18778
+ const noSingle = page !== void 0 && opposite && contrastRatio2(page, opaqueBg) < AA_TEXT * AA_TEXT;
18446
18779
  const suggestion = noSingle ? void 0 : sameHueClearingAll(painted2, page ? [page, opaqueBg] : [opaqueBg]);
18447
- const measured = (c2) => page ? `${contrastRatio(c2, opaqueBg).toFixed(2)}:1 on ${surface}, ${contrastRatio(c2, page).toFixed(2)}:1 on surfacePrimary` : `${contrastRatio(c2, opaqueBg).toFixed(2)}:1`;
18780
+ const measured = (c2) => page ? `${contrastRatio2(c2, opaqueBg).toFixed(2)}:1 on ${surface}, ${contrastRatio2(c2, page).toFixed(2)}:1 on surfacePrimary` : `${contrastRatio2(c2, opaqueBg).toFixed(2)}:1`;
18448
18781
  const restructure = ` Move ${surface} to the page's side of mid-grey (a ${page && onLightSide(page) ? "light" : "dark"} ${surface} on this page), or scope an explicit accent for that section with \`modifiers.theme\` (e.g. { "colors": { "accentText": "#RRGGBB" } } on the card).`;
18449
18782
  const fix = noSingle ? ` surfacePrimary (${page}) and ${surface} (${opaqueBg}) sit on opposite sides of mid-grey, so no single accent text colour can clear ${AA_TEXT}:1 on both.${restructure}` : suggestion ? ` A same-hue ${fgName} that clears it${page ? " and the page" : ""} is ${suggestion} (${measured(suggestion)}).` + (opposite ? ` The page and ${surface} sit on opposite sides of mid-grey, so only a narrow mid-tone clears both.${restructure}` : "") : ` No ${fgName} of this hue clears ${AA_TEXT}:1 on ${surface}${page ? " and the page" : ""}.` + (opposite ? restructure : "");
18450
18783
  findings.set(key3, {
@@ -18487,7 +18820,7 @@ var init_token_lint = __esm({
18487
18820
  const [ca2, cb] = [channels(a2), channels(b2)];
18488
18821
  return toHex2(ca2.map((c2, i2) => c2 * w2 + cb[i2] * (1 - w2)));
18489
18822
  };
18490
- onLightSide = (hex2) => contrastRatio("#000000", hex2) >= contrastRatio("#FFFFFF", hex2);
18823
+ onLightSide = (hex2) => contrastRatio2("#000000", hex2) >= contrastRatio2("#FFFFFF", hex2);
18491
18824
  }
18492
18825
  });
18493
18826
 
@@ -19085,261 +19418,6 @@ var init_app_icon = __esm({
19085
19418
  }
19086
19419
  });
19087
19420
 
19088
- // ../app-icons/pixels.ts
19089
- var pixels_exports = {};
19090
- __export(pixels_exports, {
19091
- LETTERBOX_ERROR_FRACTION: () => LETTERBOX_ERROR_FRACTION,
19092
- LETTERBOX_WARN_FRACTION: () => LETTERBOX_WARN_FRACTION,
19093
- NORMALIZE_FIT_FRACTION: () => NORMALIZE_FIT_FRACTION,
19094
- PIXEL_TOOLCHAIN_HINT: () => PIXEL_TOOLCHAIN_HINT,
19095
- PixelToolchainUnavailable: () => PixelToolchainUnavailable,
19096
- appStoreIconPixelFindings: () => appStoreIconPixelFindings,
19097
- derivePlayListingIcon: () => derivePlayListingIcon,
19098
- inspectIconPixels: () => inspectIconPixels,
19099
- normalizeSourceIcon: () => normalizeSourceIcon,
19100
- pixelToolchainAvailable: () => pixelToolchainAvailable,
19101
- pixelToolchainProblem: () => pixelToolchainProblem,
19102
- renderGlyphTile: () => renderGlyphTile
19103
- });
19104
- function isNotInstalled(e2) {
19105
- const code = e2?.code;
19106
- return code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND";
19107
- }
19108
- function messageOf(e2) {
19109
- return e2 instanceof Error ? e2.message : String(e2);
19110
- }
19111
- async function loadSharp() {
19112
- if (cachedSharp) return cachedSharp;
19113
- const failures = [];
19114
- let presentButBroken = false;
19115
- try {
19116
- const { createRequire: createRequire4 } = await import("node:module");
19117
- cachedSharp = createRequire4(import.meta.url)("sharp");
19118
- lastProblem = null;
19119
- return cachedSharp;
19120
- } catch (e2) {
19121
- presentButBroken ||= !isNotInstalled(e2);
19122
- failures.push(`require: ${messageOf(e2)}`);
19123
- }
19124
- try {
19125
- cachedSharp = (await import("sharp")).default;
19126
- lastProblem = null;
19127
- return cachedSharp;
19128
- } catch (e2) {
19129
- presentButBroken ||= !isNotInstalled(e2);
19130
- failures.push(`import: ${messageOf(e2)}`);
19131
- }
19132
- lastProblem = presentButBroken ? `the image toolchain (sharp) is installed but could not be loaded \u2014 ${failures.join("; ")}` : PIXEL_TOOLCHAIN_HINT;
19133
- return null;
19134
- }
19135
- async function pixelToolchainAvailable() {
19136
- return await loadSharp() !== null;
19137
- }
19138
- async function pixelToolchainProblem() {
19139
- return await loadSharp() === null ? lastProblem ?? PIXEL_TOOLCHAIN_HINT : null;
19140
- }
19141
- async function sharpOrThrow() {
19142
- const s2 = await loadSharp();
19143
- if (!s2) throw new PixelToolchainUnavailable(lastProblem ?? PIXEL_TOOLCHAIN_HINT);
19144
- return s2;
19145
- }
19146
- async function inspectIconPixels(png) {
19147
- const sharp = await sharpOrThrow();
19148
- const { data, info: info4 } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
19149
- const { width, height, channels: channels3 } = info4;
19150
- if (channels3 !== 4) throw new Error(`app-icons: expected RGBA after ensureAlpha, got ${channels3} channels`);
19151
- let minAlpha = 255;
19152
- let transparent = 0;
19153
- for (let i2 = 3; i2 < data.length; i2 += 4) {
19154
- const a2 = data[i2];
19155
- if (a2 < minAlpha) minAlpha = a2;
19156
- if (a2 <= ALPHA_OPAQUE) transparent++;
19157
- }
19158
- const probe = Math.max(1, Math.round(Math.min(width, height) * CORNER_PROBE));
19159
- let minCornerAlpha = 255;
19160
- for (const [x0, y0] of [
19161
- [0, 0],
19162
- [width - probe, 0],
19163
- [0, height - probe],
19164
- [width - probe, height - probe]
19165
- ]) {
19166
- for (let y2 = y0; y2 < y0 + probe; y2++) {
19167
- for (let x2 = x0; x2 < x0 + probe; x2++) {
19168
- const a2 = data[(y2 * width + x2) * 4 + 3];
19169
- if (a2 < minCornerAlpha) minCornerAlpha = a2;
19170
- }
19171
- }
19172
- }
19173
- const inset = Math.round(Math.min(width, height) * INTERIOR_INSET);
19174
- let interiorMinAlpha = 255;
19175
- for (let y2 = inset; y2 < height - inset; y2++) {
19176
- for (let x2 = inset; x2 < width - inset; x2++) {
19177
- const a2 = data[(y2 * width + x2) * 4 + 3];
19178
- if (a2 < interiorMinAlpha) interiorMinAlpha = a2;
19179
- }
19180
- }
19181
- return {
19182
- width,
19183
- height,
19184
- minAlpha,
19185
- transparentFraction: transparent / (width * height),
19186
- minCornerAlpha,
19187
- interiorMinAlpha,
19188
- // `minAlpha <= ALPHA_OPAQUE` IS "has transparency", and the caller has just computed
19189
- // it — contentBox used to re-derive it with a full extra sweep of the raster.
19190
- contentBox: contentBox(data, width, height, minAlpha <= ALPHA_OPAQUE)
19191
- };
19192
- }
19193
- function contentBox(data, width, height, hasTransparency) {
19194
- const at2 = (x2, y2) => (y2 * width + x2) * 4;
19195
- const bg = [data[at2(0, 0)], data[at2(0, 0) + 1], data[at2(0, 0) + 2]];
19196
- const isInk = (x2, y2) => {
19197
- const i2 = at2(x2, y2);
19198
- if (hasTransparency) return data[i2 + 3] > ALPHA_OPAQUE;
19199
- return Math.abs(data[i2] - bg[0]) > INK_TOLERANCE || Math.abs(data[i2 + 1] - bg[1]) > INK_TOLERANCE || Math.abs(data[i2 + 2] - bg[2]) > INK_TOLERANCE;
19200
- };
19201
- let minX = width, minY = height, maxX = -1, maxY = -1;
19202
- for (let y2 = 0; y2 < height; y2++) {
19203
- for (let x2 = 0; x2 < width; x2++) {
19204
- if (!isInk(x2, y2)) continue;
19205
- if (x2 < minX) minX = x2;
19206
- if (x2 > maxX) maxX = x2;
19207
- if (y2 < minY) minY = y2;
19208
- if (y2 > maxY) maxY = y2;
19209
- }
19210
- }
19211
- if (maxX < 0) return null;
19212
- return {
19213
- widthFraction: (maxX - minX + 1) / width,
19214
- heightFraction: (maxY - minY + 1) / height
19215
- };
19216
- }
19217
- function appStoreIconPixelFindings(f2) {
19218
- const out2 = [];
19219
- const preMasked = f2.interiorMinAlpha > ALPHA_OPAQUE && f2.minCornerAlpha <= ALPHA_OPAQUE && f2.transparentFraction <= PRE_MASK_MAX_TRANSPARENT_FRACTION;
19220
- if (preMasked) {
19221
- out2.push({
19222
- level: "error",
19223
- message: "App icon looks pre-masked: its corners are transparent while the middle is opaque. iOS and Play both apply their own corner mask, so rounded artwork is rounded twice and looks visibly wrong on device \u2014 and the transparency itself is an App Store rejection (ITMS-90717 / ITMS-90647). Ship a full-bleed square with 90-degree corners and no inset."
19224
- });
19225
- } else if (f2.minAlpha <= ALPHA_OPAQUE) {
19226
- out2.push({
19227
- level: "error",
19228
- message: `App icon has transparent pixels (${(f2.transparentFraction * 100).toFixed(1)}% of the canvas, lowest alpha ${f2.minAlpha}). An App Store icon may carry an alpha channel but no transparent regions \u2014 Apple rejects this as ITMS-90717 / ITMS-90647. Flatten it onto an opaque background (\`himi icon set <file>\` does this for you).`
19229
- });
19230
- }
19231
- if (f2.contentBox === null) {
19232
- out2.push({
19233
- level: "error",
19234
- message: "App icon is a single flat colour with no distinguishable artwork \u2014 the mark is not visible in it. Supply art that contrasts with the background, or use `himi icon generate` for a glyph tile."
19235
- });
19236
- return out2;
19237
- }
19238
- const shorter = Math.min(f2.contentBox.widthFraction, f2.contentBox.heightFraction);
19239
- if (shorter < LETTERBOX_ERROR_FRACTION) {
19240
- out2.push({
19241
- level: "error",
19242
- message: `App icon artwork fills only ${(shorter * 100).toFixed(0)}% of the canvas on its shorter side, so it reads as a speck on a home screen. This is what a wide wordmark centred in a square looks like \u2014 crop to the mark, or use a monogram/glyph instead of the wordmark.`
19243
- });
19244
- } else if (shorter < LETTERBOX_WARN_FRACTION) {
19245
- out2.push({
19246
- level: "warn",
19247
- message: `App icon artwork fills ${(shorter * 100).toFixed(0)}% of the canvas on its shorter side \u2014 it will read as a thin strip at home-screen size. Consider cropping to the mark.`
19248
- });
19249
- }
19250
- return out2;
19251
- }
19252
- function luminance([r2, g2, b2]) {
19253
- const lin = [r2, g2, b2].map((v2) => {
19254
- const c2 = v2 / 255;
19255
- return c2 <= 0.04045 ? c2 / 12.92 : ((c2 + 0.055) / 1.055) ** 2.4;
19256
- });
19257
- return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
19258
- }
19259
- function contrastRatio3(a2, b2) {
19260
- const [hi2, lo2] = [luminance(a2), luminance(b2)].sort((x2, y2) => y2 - x2);
19261
- return (hi2 + 0.05) / (lo2 + 0.05);
19262
- }
19263
- async function contrastingBackdrop(mark, requested) {
19264
- const sharp = await sharpOrThrow();
19265
- const { dominant } = await sharp(mark).stats();
19266
- const ink = [dominant.r, dominant.g, dominant.b];
19267
- const want = hexToRgb(requested);
19268
- if (!want || contrastRatio3(ink, want) >= MIN_BACKDROP_CONTRAST) return requested;
19269
- const towardsWhite = luminance(ink) < 0.5;
19270
- for (const amount of [0.35, 0.55, 0.75, 0.9]) {
19271
- const candidate = mix2(want, towardsWhite ? [255, 255, 255] : [0, 0, 0], amount);
19272
- if (contrastRatio3(ink, candidate) >= MIN_BACKDROP_CONTRAST) return rgbToHex(candidate);
19273
- }
19274
- return towardsWhite ? "#FFFFFF" : "#000000";
19275
- }
19276
- function hexToRgb(hex2) {
19277
- const m2 = /^#([0-9a-fA-F]{6})$/.exec(hex2);
19278
- if (!m2) return null;
19279
- const n2 = parseInt(m2[1], 16);
19280
- return [n2 >> 16 & 255, n2 >> 8 & 255, n2 & 255];
19281
- }
19282
- function mix2(a2, b2, t2) {
19283
- return [0, 1, 2].map((i2) => Math.round(a2[i2] + (b2[i2] - a2[i2]) * t2));
19284
- }
19285
- function rgbToHex([r2, g2, b2]) {
19286
- return `#${[r2, g2, b2].map((v2) => v2.toString(16).padStart(2, "0")).join("")}`.toUpperCase();
19287
- }
19288
- async function normalizeSourceIcon(input, opts) {
19289
- const sharp = await sharpOrThrow();
19290
- const size = opts.size ?? 1024;
19291
- const fit2 = opts.fit ?? NORMALIZE_FIT_FRACTION;
19292
- const inner = Math.max(1, Math.round(size * fit2));
19293
- const pad3 = Math.round((size - inner) / 2);
19294
- let trimmed;
19295
- try {
19296
- trimmed = await sharp(input).ensureAlpha().trim().png().toBuffer();
19297
- } catch {
19298
- trimmed = await sharp(input).ensureAlpha().png().toBuffer();
19299
- }
19300
- const mark = await sharp(trimmed).resize(inner, inner, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toBuffer();
19301
- const background = await contrastingBackdrop(mark, opts.background);
19302
- return sharp({
19303
- create: {
19304
- width: size,
19305
- height: size,
19306
- channels: 4,
19307
- background: { r: 0, g: 0, b: 0, alpha: 0 }
19308
- }
19309
- }).composite([{ input: mark, top: pad3, left: pad3 }]).flatten({ background }).removeAlpha().png({ palette: false }).toBuffer();
19310
- }
19311
- async function derivePlayListingIcon(master, size = 512) {
19312
- const sharp = await sharpOrThrow();
19313
- return sharp(master).resize(size, size).ensureAlpha().png({ palette: false }).toBuffer();
19314
- }
19315
- async function renderGlyphTile(svg, background, size = 1024) {
19316
- const sharp = await sharpOrThrow();
19317
- return sharp(Buffer.from(svg)).resize(size, size).flatten({ background }).removeAlpha().png({ palette: false }).toBuffer();
19318
- }
19319
- var cachedSharp, lastProblem, PIXEL_TOOLCHAIN_HINT, PixelToolchainUnavailable, ALPHA_OPAQUE, CORNER_PROBE, INTERIOR_INSET, INK_TOLERANCE, PRE_MASK_MAX_TRANSPARENT_FRACTION, LETTERBOX_WARN_FRACTION, LETTERBOX_ERROR_FRACTION, NORMALIZE_FIT_FRACTION, MIN_BACKDROP_CONTRAST;
19320
- var init_pixels = __esm({
19321
- "../app-icons/pixels.ts"() {
19322
- cachedSharp = null;
19323
- lastProblem = null;
19324
- PIXEL_TOOLCHAIN_HINT = "the image toolchain (sharp) is not installed \u2014 run `npm i -g sharp` to enable transparency, pre-masked-corner and letterbox checks";
19325
- PixelToolchainUnavailable = class extends Error {
19326
- constructor(reason = PIXEL_TOOLCHAIN_HINT) {
19327
- super(`app-icons: ${reason}`);
19328
- this.name = "PixelToolchainUnavailable";
19329
- }
19330
- };
19331
- ALPHA_OPAQUE = 250;
19332
- CORNER_PROBE = 0.06;
19333
- INTERIOR_INSET = 0.2;
19334
- INK_TOLERANCE = 24;
19335
- PRE_MASK_MAX_TRANSPARENT_FRACTION = 0.1;
19336
- LETTERBOX_WARN_FRACTION = 0.35;
19337
- LETTERBOX_ERROR_FRACTION = 0.2;
19338
- NORMALIZE_FIT_FRACTION = 0.82;
19339
- MIN_BACKDROP_CONTRAST = 1.5;
19340
- }
19341
- });
19342
-
19343
19421
  // src/icon.ts
19344
19422
  var icon_exports = {};
19345
19423
  __export(icon_exports, {
@@ -19360,9 +19438,9 @@ __export(icon_exports, {
19360
19438
  siteLogoAbsence: () => siteLogoAbsence,
19361
19439
  summarize: () => summarize
19362
19440
  });
19363
- import { createHash as createHash8 } from "node:crypto";
19441
+ import { createHash as createHash9 } from "node:crypto";
19364
19442
  import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync21, statSync as statSync5, writeFileSync as writeFileSync8 } from "node:fs";
19365
- import { basename as basename7, dirname as dirname15, join as join21, resolve as resolve21, sep as sep7 } from "node:path";
19443
+ import { basename as basename8, dirname as dirname15, join as join21, resolve as resolve21, sep as sep7 } from "node:path";
19366
19444
  function readManifest(dir) {
19367
19445
  try {
19368
19446
  return JSON.parse(readFileSync21(join21(dir, MANIFEST), "utf8"));
@@ -19449,7 +19527,7 @@ async function iconStatus(dir) {
19449
19527
  ...base,
19450
19528
  templateIcon: templateIconMatch(png),
19451
19529
  bytes: png.length,
19452
- sha256: createHash8("sha256").update(png).digest("hex"),
19530
+ sha256: createHash9("sha256").update(png).digest("hex"),
19453
19531
  header: pngHeader(png),
19454
19532
  byteIssues
19455
19533
  };
@@ -19537,7 +19615,7 @@ async function installIcon(dir, sourcePath, opts = {}) {
19537
19615
  const issues = appStoreIconIssues(input);
19538
19616
  if (issues.length) {
19539
19617
  throw new Error(
19540
- `himi icon: ${basename7(src)} ${issues.join("; ")}. Normalizing it needs the image toolchain \u2014 ${await pixelToolchainProblem() ?? PIXEL_TOOLCHAIN_HINT} \u2014 or supply an opaque 1024x1024 PNG. The existing icon was left alone.`
19618
+ `himi icon: ${basename8(src)} ${issues.join("; ")}. Normalizing it needs the image toolchain \u2014 ${await pixelToolchainProblem() ?? PIXEL_TOOLCHAIN_HINT} \u2014 or supply an opaque 1024x1024 PNG. The existing icon was left alone.`
19541
19619
  );
19542
19620
  }
19543
19621
  }
@@ -19843,17 +19921,17 @@ var init_plan_fonts = __esm({
19843
19921
  });
19844
19922
 
19845
19923
  // ../font-bake/face-source.ts
19846
- import { createHash as createHash9 } from "node:crypto";
19924
+ import { createHash as createHash10 } from "node:crypto";
19847
19925
  import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync23, renameSync as renameSync2, rmSync as rmSync5, writeFileSync as writeFileSync9 } from "node:fs";
19848
19926
  import { homedir as homedir3 } from "node:os";
19849
- import { basename as basename8, join as join24 } from "node:path";
19927
+ import { basename as basename9, join as join24 } from "node:path";
19850
19928
  import { fileURLToPath as fileURLToPath11 } from "node:url";
19851
19929
  function hasVendoredFloor() {
19852
19930
  return existsSync24(VENDORED_DIR);
19853
19931
  }
19854
19932
  function faceSource(file2, sha2567) {
19855
- const vendored = join24(VENDORED_DIR, basename8(file2));
19856
- if (existsSync24(vendored) && createHash9("sha256").update(readFileSync23(vendored)).digest("hex") === sha2567) {
19933
+ const vendored = join24(VENDORED_DIR, basename9(file2));
19934
+ if (existsSync24(vendored) && createHash10("sha256").update(readFileSync23(vendored)).digest("hex") === sha2567) {
19857
19935
  return vendored;
19858
19936
  }
19859
19937
  const cached3 = join24(CACHE_DIR, sha2567);
@@ -19987,7 +20065,7 @@ var init_face_source = __esm({
19987
20065
  REPO_ROOT5 = fileURLToPath11(new URL("../..", import.meta.url));
19988
20066
  VENDORED_DIR = process.env.HIMI_FONTS_VENDORED_DIR || join24(REPO_ROOT5, "stdlib", "fonts", "vendored", "wix-madefor");
19989
20067
  CACHE_DIR = join24(process.env.XDG_CACHE_HOME || join24(homedir3(), ".cache"), "himi", "fonts");
19990
- sha256Hex3 = (bytes2) => createHash9("sha256").update(bytes2).digest("hex");
20068
+ sha256Hex3 = (bytes2) => createHash10("sha256").update(bytes2).digest("hex");
19991
20069
  RAW = ["https://", "raw.", "githubuser", "content.com"].join("");
19992
20070
  UPSTREAM_DIR = /^(ofl|apache|ufl)\/[a-z0-9_-]+$/;
19993
20071
  FETCH_TIMEOUT_MS = 2e4;
@@ -21193,7 +21271,7 @@ __export(catalog_cache_exports, {
21193
21271
  catalogCompatible: () => catalogCompatible,
21194
21272
  catalogManifestDigest: () => catalogManifestDigest
21195
21273
  });
21196
- import { createHash as createHash10, randomUUID as randomUUID2 } from "node:crypto";
21274
+ import { createHash as createHash11, randomUUID as randomUUID2 } from "node:crypto";
21197
21275
  import {
21198
21276
  existsSync as existsSync29,
21199
21277
  mkdirSync as mkdirSync13,
@@ -21289,7 +21367,7 @@ var init_catalog_cache = __esm({
21289
21367
  this.name = "CatalogError";
21290
21368
  }
21291
21369
  };
21292
- hash = (b2) => `sha256-${createHash10("sha256").update(b2).digest("hex")}`;
21370
+ hash = (b2) => `sha256-${createHash11("sha256").update(b2).digest("hex")}`;
21293
21371
  CatalogCache = class {
21294
21372
  root;
21295
21373
  baseUrl;
@@ -21307,7 +21385,7 @@ var init_catalog_cache = __esm({
21307
21385
  homedir4(),
21308
21386
  ".himi",
21309
21387
  "authoring-cache",
21310
- createHash10("sha256").update(this.baseUrl).digest("hex").slice(0, 16)
21388
+ createHash11("sha256").update(this.baseUrl).digest("hex").slice(0, 16)
21311
21389
  )
21312
21390
  );
21313
21391
  this.cliVersion = opts.cliVersion;
@@ -23547,7 +23625,7 @@ var init_src2 = __esm({
23547
23625
 
23548
23626
  // ../app-icons/plan.ts
23549
23627
  import { existsSync as existsSync31, readFileSync as readFileSync31, readdirSync as readdirSync18 } from "node:fs";
23550
- import { createHash as createHash11 } from "node:crypto";
23628
+ import { createHash as createHash12 } from "node:crypto";
23551
23629
  import { dirname as dirname19, join as join32, resolve as resolve26 } from "node:path";
23552
23630
  function stripComments(src) {
23553
23631
  let out2 = "";
@@ -23634,7 +23712,7 @@ function parseIconConfig(configTs) {
23634
23712
  return out2;
23635
23713
  }
23636
23714
  function idHash(id) {
23637
- return createHash11("sha1").update(id).digest();
23715
+ return createHash12("sha1").update(id).digest();
23638
23716
  }
23639
23717
  function deriveGlyph(id) {
23640
23718
  const h2 = idHash(id);
@@ -23812,7 +23890,7 @@ function placeholderHashes() {
23812
23890
  }
23813
23891
  function fileSha256(path2) {
23814
23892
  try {
23815
- return createHash11("sha256").update(readFileSync31(path2)).digest("hex");
23893
+ return createHash12("sha256").update(readFileSync31(path2)).digest("hex");
23816
23894
  } catch {
23817
23895
  return null;
23818
23896
  }
@@ -25518,7 +25596,7 @@ __export(progress_exports, {
25518
25596
  writeFileAtomic: () => writeFileAtomic
25519
25597
  });
25520
25598
  import { spawn as spawn3 } from "node:child_process";
25521
- import { createHash as createHash12 } from "node:crypto";
25599
+ import { createHash as createHash13 } from "node:crypto";
25522
25600
  import { randomUUID as randomUUID3 } from "node:crypto";
25523
25601
  import { appendFileSync, closeSync, existsSync as existsSync35, lstatSync as lstatSync8, mkdirSync as mkdirSync16, openSync, readdirSync as readdirSync21, readFileSync as readFileSync35, readSync, renameSync as renameSync5, rmSync as rmSync8, statSync as statSync8, writeFileSync as writeFileSync14 } from "node:fs";
25524
25602
  import { isAbsolute as isAbsolute3, join as join36, relative as relative6, resolve as resolve28, sep as sep8 } from "node:path";
@@ -25604,7 +25682,7 @@ function isSymlink(file2) {
25604
25682
  function archiveBytes(dir, bytes2) {
25605
25683
  const ext = imageExtension(bytes2);
25606
25684
  if (!ext) return null;
25607
- const sha2567 = createHash12("sha256").update(bytes2).digest("hex");
25685
+ const sha2567 = createHash13("sha256").update(bytes2).digest("hex");
25608
25686
  const rel = join36(HISTORY_DIR, `${sha2567}.${ext}`);
25609
25687
  const dest = join36(dir, rel);
25610
25688
  if (!realDirIn(dir, HISTORY_DIR)) throw new UnsafeHimiDirError(HISTORY_DIR);
@@ -25689,7 +25767,7 @@ function journalMilestone(dir, input) {
25689
25767
  const now = input.now ?? /* @__PURE__ */ new Date();
25690
25768
  const step = MILESTONE_STEP[input.milestone];
25691
25769
  const abs = input.image ? locateImage(dir, input.image) : null;
25692
- const images = abs ? [{ sha256: createHash12("sha256").update(readFileSync35(abs)).digest("hex") }] : [];
25770
+ const images = abs ? [{ sha256: createHash13("sha256").update(readFileSync35(abs)).digest("hex") }] : [];
25693
25771
  let message = input.message;
25694
25772
  if (input.unlessUnchanged) {
25695
25773
  const latest = [...entries].reverse().find((e2) => isCliEntry(e2) && e2.milestone === input.milestone);
@@ -26608,7 +26686,7 @@ var init_claude_plugin = __esm({
26608
26686
  });
26609
26687
 
26610
26688
  // src/run-identity.ts
26611
- import { createHash as createHash13 } from "node:crypto";
26689
+ import { createHash as createHash14 } from "node:crypto";
26612
26690
  import { realpathSync as realpathSync5 } from "node:fs";
26613
26691
  import { resolve as resolve29 } from "node:path";
26614
26692
  function runRootId(dir) {
@@ -26618,7 +26696,7 @@ function runRootId(dir) {
26618
26696
  } catch {
26619
26697
  real = resolve29(dir);
26620
26698
  }
26621
- return createHash13("sha256").update(real).digest("hex");
26699
+ return createHash14("sha256").update(real).digest("hex");
26622
26700
  }
26623
26701
  function runIdentity(dir) {
26624
26702
  return { pid: process.pid, root: runRootId(dir) };
@@ -26634,7 +26712,7 @@ var init_run_identity = __esm({
26634
26712
  import { spawn as nodeSpawn } from "node:child_process";
26635
26713
  import { closeSync as closeSync2, constants as fsc, existsSync as existsSync36, lstatSync as lstatSync9, mkdirSync as mkdirSync17, openSync as openSync2, readFileSync as readFileSync36, rmSync as rmSync9 } from "node:fs";
26636
26714
  import { createServer as createServer2 } from "node:net";
26637
- import { basename as basename9, join as join37 } from "node:path";
26715
+ import { basename as basename10, join as join37 } from "node:path";
26638
26716
  function previewPortFor(appName2) {
26639
26717
  let h2 = 2166136261;
26640
26718
  for (let i2 = 0; i2 < appName2.length; i2++) {
@@ -26649,7 +26727,7 @@ function previewKey(manifestText, dir) {
26649
26727
  if (typeof name === "string" && name) return name;
26650
26728
  } catch {
26651
26729
  }
26652
- return basename9(dir);
26730
+ return basename10(dir);
26653
26731
  }
26654
26732
  function previewPortForDir(dir) {
26655
26733
  const p2 = join37(dir, "himalaya.content.json");
@@ -30799,7 +30877,7 @@ var init_wix_push_links = __esm({
30799
30877
  });
30800
30878
 
30801
30879
  // ../../core/dev-server/src/app-config.ts
30802
- import { createHash as createHash14 } from "node:crypto";
30880
+ import { createHash as createHash15 } from "node:crypto";
30803
30881
  function deriveTelemetryConfig(app) {
30804
30882
  const cfg = { enabled: true, samplingRate: 1 };
30805
30883
  const prodTelemetry = app.environments?.prod?.telemetry;
@@ -30996,7 +31074,7 @@ function buildAppConfigDocument(req, meta, override, bundleCtx, devNetMocks) {
30996
31074
  const locale = pickLocale(typeof acceptLanguage === "string" ? acceptLanguage : null);
30997
31075
  const config = locale ? translate(JSON.parse(JSON.stringify(baseline)), locale) : baseline;
30998
31076
  const body = JSON.stringify(config);
30999
- const etag = `"${createHash14("sha1").update(`${locale ?? "en"}:${body}`).digest("hex")}"`;
31077
+ const etag = `"${createHash15("sha1").update(`${locale ?? "en"}:${body}`).digest("hex")}"`;
31000
31078
  return { config, body, etag, locale };
31001
31079
  }
31002
31080
  function handleAppConfigRequest(req, res, meta, override, bundleCtx, devNetMocks) {
@@ -35540,7 +35618,7 @@ __export(wix_member_login_exports, {
35540
35618
  webWixCall: () => webWixCall,
35541
35619
  webWixStream: () => webWixStream
35542
35620
  });
35543
- import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
35621
+ import { createHash as createHash16, randomBytes as randomBytes3 } from "node:crypto";
35544
35622
  function b64url(buf) {
35545
35623
  return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
35546
35624
  }
@@ -35579,7 +35657,7 @@ function authCodeFrom(url) {
35579
35657
  }
35580
35658
  async function exchangeSessionToken(clientId, redirectUri, visitorToken, sessionToken) {
35581
35659
  const verifier = b64url(randomBytes3(32));
35582
- const challenge = b64url(createHash15("sha256").update(verifier).digest());
35660
+ const challenge = b64url(createHash16("sha256").update(verifier).digest());
35583
35661
  const state = b64url(randomBytes3(8));
35584
35662
  const rs2 = await postJson(
35585
35663
  "/_api/redirects-api/v1/redirect-session",
@@ -36430,7 +36508,7 @@ var init_person_session_auth = __esm({
36430
36508
  import { readFileSync as readFileSync45 } from "node:fs";
36431
36509
  import { homedir as homedir6 } from "node:os";
36432
36510
  import { join as pathJoin2 } from "node:path";
36433
- import { createHash as createHash16 } from "node:crypto";
36511
+ import { createHash as createHash17 } from "node:crypto";
36434
36512
  function captureAccountSession(req, opts = {}) {
36435
36513
  const env = opts.env ?? process.env;
36436
36514
  const now = opts.now ?? Date.now();
@@ -36469,7 +36547,7 @@ async function accountPrincipal(cookie, fetchImpl = fetch) {
36469
36547
  const identity = await resolveWixAccounts(cookie, fetchImpl);
36470
36548
  if (!identity.ok) return { ok: false, reason: identity.reason };
36471
36549
  const ids = identity.accounts.map((a2) => a2.accountId).sort().join("\n");
36472
- return { ok: true, principal: `acct_${createHash16("sha256").update(ids).digest("hex").slice(0, 32)}` };
36550
+ return { ok: true, principal: `acct_${createHash17("sha256").update(ids).digest("hex").slice(0, 32)}` };
36473
36551
  }
36474
36552
  function safeAccountReturn(raw, requestHost, requestProto = "http") {
36475
36553
  if (typeof raw !== "string" || !raw) return "/";
@@ -36579,9 +36657,9 @@ var init_flag_assignments = __esm({
36579
36657
  });
36580
36658
 
36581
36659
  // ../../core/dev-server/src/images.ts
36582
- import { createHash as createHash17 } from "node:crypto";
36660
+ import { createHash as createHash18 } from "node:crypto";
36583
36661
  function svgFor(productId, title, sku) {
36584
- const seedHex = createHash17("sha1").update(productId + ":" + title).digest("hex");
36662
+ const seedHex = createHash18("sha1").update(productId + ":" + title).digest("hex");
36585
36663
  const h2 = (n2) => parseInt(seedHex.slice(n2, n2 + 2), 16);
36586
36664
  const hue1 = h2(0) % 360;
36587
36665
  const hue2 = (hue1 + 30 + h2(2) % 60) % 360;
@@ -36613,7 +36691,7 @@ function escapeXml(s2) {
36613
36691
  return s2.replace(/[<>&"']/g, (c2) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;", '"': "&quot;", "'": "&apos;" })[c2] ?? c2);
36614
36692
  }
36615
36693
  function wixPhotoSvg(category, seed2) {
36616
- const seedHex = createHash17("sha1").update(category + ":" + seed2).digest("hex");
36694
+ const seedHex = createHash18("sha1").update(category + ":" + seed2).digest("hex");
36617
36695
  const h2 = (n2) => parseInt(seedHex.slice(n2, n2 + 2), 16);
36618
36696
  const palette2 = paletteForCategory(category, h2);
36619
36697
  const glyph = glyphForCategory(category);
@@ -36738,7 +36816,7 @@ var init_stable_ids = __esm({
36738
36816
 
36739
36817
  // ../../core/dev-server/src/preview.ts
36740
36818
  import { spawn as spawn4 } from "node:child_process";
36741
- import { createHash as createHash18 } from "node:crypto";
36819
+ import { createHash as createHash19 } from "node:crypto";
36742
36820
  import { existsSync as existsSync44, createReadStream, statSync as statSync11, readFileSync as readFileSync46, renameSync as renameSync9, unlinkSync as unlinkSync3 } from "node:fs";
36743
36821
  import { homedir as homedir7, tmpdir as tmpdir2 } from "node:os";
36744
36822
  import { resolve as resolvePath, dirname as dirname27 } from "node:path";
@@ -36754,7 +36832,7 @@ function macosWinlistBin() {
36754
36832
  }
36755
36833
  function macosWinlistSourceHash() {
36756
36834
  try {
36757
- return createHash18("sha1").update(readFileSync46(MACOS_WINLIST)).digest("hex").slice(0, 12);
36835
+ return createHash19("sha1").update(readFileSync46(MACOS_WINLIST)).digest("hex").slice(0, 12);
36758
36836
  } catch {
36759
36837
  return "missing";
36760
36838
  }
@@ -37610,9 +37688,9 @@ var init_preview2 = __esm({
37610
37688
  });
37611
37689
 
37612
37690
  // ../../core/dev-server/src/tier4-integrity.ts
37613
- import { createHash as createHash19 } from "node:crypto";
37691
+ import { createHash as createHash20 } from "node:crypto";
37614
37692
  function computeSri(bytes2) {
37615
- const digest2 = createHash19("sha384").update(bytes2).digest("base64");
37693
+ const digest2 = createHash20("sha384").update(bytes2).digest("base64");
37616
37694
  return `sha384-${digest2}`;
37617
37695
  }
37618
37696
  function parseManifestIntegrity(manifest) {
@@ -131370,7 +131448,8 @@ function renderDescriptorToHtml(payload, tokens2, scheme = "light", provider = n
131370
131448
  ...typeof width === "number" && Number.isFinite(width) && width > 0 ? { sizeClass: sizeClassFromWidth(width), containerWidth: width } : {},
131371
131449
  ...rtl ? { isRTL: true } : {},
131372
131450
  ...opts?.idiomContext ? { idiomContext: opts.idiomContext } : {},
131373
- ...opts?.debugUnknownComponents ? { debugUnknownComponents: true } : {}
131451
+ ...opts?.debugUnknownComponents ? { debugUnknownComponents: true } : {},
131452
+ ...opts?.assetBaseUrl ? { assetBaseUrl: opts.assetBaseUrl } : {}
131374
131453
  });
131375
131454
  const FOCUS_STYLE_MARKUP = `<style data-himalaya-focus-ring>${FOCUS_RING_CSS}</style>`;
131376
131455
  const withFocusRing = (markup) => markup ? FOCUS_STYLE_MARKUP + markup : markup;
@@ -131791,7 +131870,7 @@ function composeStickyTree(payload, header, footer) {
131791
131870
  if (footer) children.push(footer);
131792
131871
  return { id: "screen", type: "ScreenChrome", children };
131793
131872
  }
131794
- async function renderDescriptorWeb(descriptor, tokens2, scheme, wantPng, services = null, width, isRTL, idiomContext) {
131873
+ async function renderDescriptorWeb(descriptor, tokens2, scheme, wantPng, services = null, width, isRTL, idiomContext, assetBaseUrl) {
131795
131874
  const desc = descriptor;
131796
131875
  const tooDeep = rawJsonDepthFinding(descriptor);
131797
131876
  if (tooDeep) return { renderable: false, reason: `depth_budget_exceeded: ${tooDeep.message}` };
@@ -131832,7 +131911,7 @@ async function renderDescriptorWeb(descriptor, tokens2, scheme, wantPng, service
131832
131911
  stickyHeader || stickyFooter ? { stickyHeader, stickyFooter } : void 0,
131833
131912
  // #4497 — the agent fast loop is a dev tool: an unknown component type keeps its
131834
131913
  // developer label here (a shipped web app renders nothing for it).
131835
- { ...isRTL ? { isRTL: true } : {}, ...idiomContext ? { idiomContext } : {}, debugUnknownComponents: true }
131914
+ { ...isRTL ? { isRTL: true } : {}, ...idiomContext ? { idiomContext } : {}, ...assetBaseUrl ? { assetBaseUrl } : {}, debugUnknownComponents: true }
131836
131915
  );
131837
131916
  } catch (err) {
131838
131917
  return {
@@ -132594,7 +132673,7 @@ __export(widgets_exports, {
132594
132673
  viewWidgetId: () => viewWidgetId,
132595
132674
  widgetRealm: () => widgetRealm
132596
132675
  });
132597
- import { createHash as createHash20 } from "node:crypto";
132676
+ import { createHash as createHash21 } from "node:crypto";
132598
132677
  import { inspect } from "node:util";
132599
132678
  async function widgetRealm() {
132600
132679
  if (!realmPromise) {
@@ -132650,7 +132729,7 @@ function dropEntry(cache, key3) {
132650
132729
  cache.bytes -= entry.bytes;
132651
132730
  }
132652
132731
  function produceCacheKey(bundleJs, timeoutMs, clock) {
132653
- const digest2 = createHash20("sha256").update(bundleJs).digest("base64");
132732
+ const digest2 = createHash21("sha256").update(bundleJs).digest("base64");
132654
132733
  return `${timeoutMs}|${clock === void 0 ? "live" : Math.trunc(clock)}|${digest2}`;
132655
132734
  }
132656
132735
  function cachedProduce(evaluator, key3) {
@@ -133040,7 +133119,7 @@ function contentEtag(snapshot2) {
133040
133119
  // Lever A — a timeline change must bump the etag (frame values/times are content).
133041
133120
  frames: snapshot2.frames ?? null
133042
133121
  });
133043
- return "sha256-" + createHash20("sha256").update(canonical6).digest("base64");
133122
+ return "sha256-" + createHash21("sha256").update(canonical6).digest("base64");
133044
133123
  }
133045
133124
  async function buildWidgetSnapshot(bundleJs, widgetId, opts = {}) {
133046
133125
  const { widgets } = await extractWidgets(bundleJs, opts);
@@ -133543,8 +133622,8 @@ var init_config2 = __esm({
133543
133622
  import { createServer as createServer3 } from "node:http";
133544
133623
  import { readFile, stat } from "node:fs/promises";
133545
133624
  import { readFileSync as readFileSync53, existsSync as existsSync47, watch, mkdirSync as mkdirSync23, rmSync as rmSync11 } from "node:fs";
133546
- import { basename as basename10, dirname as dirname30, extname as extname2, join as join47, relative as relative8, resolve as resolve38, normalize as normalize3, sep as sep10 } from "node:path";
133547
- import { createHash as createHash21, randomUUID as randomUUID5 } from "node:crypto";
133625
+ import { basename as basename11, dirname as dirname30, extname as extname2, join as join47, relative as relative8, resolve as resolve38, normalize as normalize3, sep as sep10 } from "node:path";
133626
+ import { createHash as createHash22, randomUUID as randomUUID5 } from "node:crypto";
133548
133627
  import { fileURLToPath as fileURLToPath23, pathToFileURL as pathToFileURL6 } from "node:url";
133549
133628
  function readLogicBundle(read, tier5Dir, name) {
133550
133629
  return read(tier5Dir ?? TIER5_ROOT2, name) ?? (tier5Dir && tier5Dir !== TIER5_ROOT2 ? read(TIER5_ROOT2, name) : void 0);
@@ -133590,7 +133669,7 @@ async function readBody2(req) {
133590
133669
  return Buffer.concat(chunks).toString("utf-8");
133591
133670
  }
133592
133671
  function makeEtag(body) {
133593
- return `"${createHash21("sha256").update(body).digest("hex").slice(0, 32)}"`;
133672
+ return `"${createHash22("sha256").update(body).digest("hex").slice(0, 32)}"`;
133594
133673
  }
133595
133674
  function servedTokensPath(app) {
133596
133675
  return app.module.designTokensPath ?? resolve38(app.dir, uiConfig(app.module.config).designTokensPath ?? "tokens.json");
@@ -134121,7 +134200,7 @@ function bundleIntegritySync(name, dir) {
134121
134200
  for (const root of dir ? [dir, TIER5_ROOT2] : [TIER5_ROOT2]) {
134122
134201
  try {
134123
134202
  const bytes2 = readFileSync53(resolve38(root, `${name}.bundle.js`));
134124
- return "sha256-" + createHash21("sha256").update(bytes2).digest("base64");
134203
+ return "sha256-" + createHash22("sha256").update(bytes2).digest("base64");
134125
134204
  } catch {
134126
134205
  }
134127
134206
  }
@@ -134542,7 +134621,7 @@ function tokensAreDerived(state) {
134542
134621
  }
134543
134622
  function servedTokensHash(app) {
134544
134623
  try {
134545
- return createHash21("sha256").update(readFileSync53(servedTokensPath(app))).digest("hex");
134624
+ return createHash22("sha256").update(readFileSync53(servedTokensPath(app))).digest("hex");
134546
134625
  } catch {
134547
134626
  return "";
134548
134627
  }
@@ -134570,7 +134649,7 @@ function syncAppDependencyWatchers(state, inputs) {
134570
134649
  for (const file2 of appDependencyFiles(inputs, state.app.dir)) {
134571
134650
  const dir = dirname30(file2);
134572
134651
  if (!byDir.has(dir)) byDir.set(dir, /* @__PURE__ */ new Set());
134573
- byDir.get(dir).add(basename10(file2));
134652
+ byDir.get(dir).add(basename11(file2));
134574
134653
  }
134575
134654
  let timer;
134576
134655
  const changed = /* @__PURE__ */ new Set();
@@ -135292,7 +135371,7 @@ async function handleWebWix(req, res, state) {
135292
135371
  userId: userId ?? session.email ?? null,
135293
135372
  email: session.email ?? "",
135294
135373
  preferenceContext: preferenceIdentity ? {
135295
- accountId: `m_${createHash21("sha256").update(preferenceIdentity).digest("hex")}`,
135374
+ accountId: `m_${createHash22("sha256").update(preferenceIdentity).digest("hex")}`,
135296
135375
  // Headless client IDs are site-bound and come from the server's app
135297
135376
  // configuration, never the URL's user-supplied business parameter.
135298
135377
  businessId: auth.clientId
@@ -135392,7 +135471,10 @@ async function handleDev(req, res, state) {
135392
135471
  body.services ?? null,
135393
135472
  width,
135394
135473
  body.rtl === true,
135395
- idiomContext
135474
+ idiomContext,
135475
+ // #4595 — HHP package images arrive host-less (`/v1/asset/<sha>?app=`); a PNG page loads at
135476
+ // about:blank, so resolve them against this server, as a device resolves them against its origin.
135477
+ state.serverBaseUrl
135396
135478
  );
135397
135479
  const legacy = { screenId, scheme, ...render, diagnostics };
135398
135480
  const report3 = foldLegacyReport(legacy, {
@@ -136032,7 +136114,7 @@ async function readBundleFile(name, dir) {
136032
136114
  if (!roots.some((root) => path2.startsWith(root))) continue;
136033
136115
  try {
136034
136116
  const bytes2 = await readFile(path2);
136035
- const integrity2 = "sha256-" + createHash21("sha256").update(bytes2).digest("base64");
136117
+ const integrity2 = "sha256-" + createHash22("sha256").update(bytes2).digest("base64");
136036
136118
  return { bytes: bytes2, integrity: integrity2 };
136037
136119
  } catch {
136038
136120
  }
@@ -136202,7 +136284,7 @@ function readActionHandlerManifest(app) {
136202
136284
  } catch {
136203
136285
  return { handlers: [] };
136204
136286
  }
136205
- const integrity2 = "sha256-" + createHash21("sha256").update(bytes2).digest("base64");
136287
+ const integrity2 = "sha256-" + createHash22("sha256").update(bytes2).digest("base64");
136206
136288
  const bundleUrl = `/v1/action-handler-bundles/${app}`;
136207
136289
  return { handlers: decls.map((d2) => ({ id: d2.id, policy: d2.policy, description: d2.description, bundleUrl, integrity: integrity2 })) };
136208
136290
  }
@@ -136215,7 +136297,7 @@ async function handleActionHandlerBundleGet(res, app) {
136215
136297
  if (!path2.startsWith(ACTION_HANDLERS_ROOT)) return sendJson(res, 404, { error: "not_found" });
136216
136298
  try {
136217
136299
  const bytes2 = await readFile(path2);
136218
- const integrity2 = "sha256-" + createHash21("sha256").update(bytes2).digest("base64");
136300
+ const integrity2 = "sha256-" + createHash22("sha256").update(bytes2).digest("base64");
136219
136301
  res.setHeader("content-type", "application/javascript; charset=utf-8");
136220
136302
  res.setHeader("cache-control", "max-age=300");
136221
136303
  res.setHeader("x-himalaya-bundle-integrity", integrity2);
@@ -136228,8 +136310,8 @@ async function handleActionHandlerBundleGet(res, app) {
136228
136310
  }
136229
136311
  async function serveStatic(req, res, root, prefix) {
136230
136312
  const requested = decodeURIComponent((req.url ?? "").split("?")[0]);
136231
- const relative17 = requested.replace(prefix, "").replace(/^\//, "");
136232
- const safe = normalize3(relative17).replace(/^(\.\.[\/\\])+/, "");
136313
+ const relative18 = requested.replace(prefix, "").replace(/^\//, "");
136314
+ const safe = normalize3(relative18).replace(/^(\.\.[\/\\])+/, "");
136233
136315
  const fullPath = resolve38(root, safe);
136234
136316
  if (!fullPath.startsWith(root)) {
136235
136317
  return send(res, 403, "forbidden");
@@ -137010,9 +137092,9 @@ var init_src6 = __esm({
137010
137092
  });
137011
137093
 
137012
137094
  // ../../core/serve-storage/src/release-patch.ts
137013
- import { createHash as createHash22 } from "node:crypto";
137095
+ import { createHash as createHash23 } from "node:crypto";
137014
137096
  function sha256hex(b2) {
137015
- return createHash22("sha256").update(b2).digest("hex");
137097
+ return createHash23("sha256").update(b2).digest("hex");
137016
137098
  }
137017
137099
  function stableStringify(value) {
137018
137100
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -140045,11 +140127,11 @@ var init_dependencies = __esm({
140045
140127
  });
140046
140128
 
140047
140129
  // ../../core/functions-build/src/sbom.ts
140048
- import { createHash as createHash23 } from "node:crypto";
140130
+ import { createHash as createHash24 } from "node:crypto";
140049
140131
  function createFunctionSbom(packages) {
140050
140132
  const components2 = [...packages].sort((a2, b2) => a2.name.localeCompare(b2.name)).map(({ name, version: version4, integrity: integrity2, license }) => ({ name, version: version4, integrity: integrity2, license }));
140051
140133
  const body = JSON.stringify({ format: "himalaya-sbom-v1", components: components2 });
140052
- return { format: "himalaya-sbom-v1", components: components2, hash: createHash23("sha256").update(body).digest("hex") };
140134
+ return { format: "himalaya-sbom-v1", components: components2, hash: createHash24("sha256").update(body).digest("hex") };
140053
140135
  }
140054
140136
  var init_sbom = __esm({
140055
140137
  "../../core/functions-build/src/sbom.ts"() {
@@ -140057,12 +140139,12 @@ var init_sbom = __esm({
140057
140139
  });
140058
140140
 
140059
140141
  // ../../core/functions-build/src/build.ts
140060
- import { createHash as createHash24 } from "node:crypto";
140142
+ import { createHash as createHash25 } from "node:crypto";
140061
140143
  function sha2563(value) {
140062
- return createHash24("sha256").update(value).digest("hex");
140144
+ return createHash25("sha256").update(value).digest("hex");
140063
140145
  }
140064
140146
  function sri(value) {
140065
- return `sha256-${createHash24("sha256").update(value).digest("base64")}`;
140147
+ return `sha256-${createHash25("sha256").update(value).digest("base64")}`;
140066
140148
  }
140067
140149
  function schemaHash(schema) {
140068
140150
  if (!schema) return "";
@@ -140257,7 +140339,7 @@ __export(build_exports2, {
140257
140339
  withStoreNavBundle: () => withStoreNavBundle,
140258
140340
  withoutDevOnlyAppConfig: () => withoutDevOnlyAppConfig
140259
140341
  });
140260
- import { createHash as createHash25 } from "node:crypto";
140342
+ import { createHash as createHash26 } from "node:crypto";
140261
140343
  import { readFileSync as readFileSync54, readdirSync as readdirSync25, existsSync as existsSync48 } from "node:fs";
140262
140344
  import { dirname as dirname31, join as join48, resolve as resolve39 } from "node:path";
140263
140345
  import { fileURLToPath as fileURLToPath24 } from "node:url";
@@ -140340,10 +140422,10 @@ function servesMessages(text9) {
140340
140422
  }
140341
140423
  }
140342
140424
  function sha2564(buf) {
140343
- return createHash25("sha256").update(buf).digest("hex");
140425
+ return createHash26("sha256").update(buf).digest("hex");
140344
140426
  }
140345
140427
  function sri2(buf) {
140346
- return "sha256-" + createHash25("sha256").update(buf).digest("base64");
140428
+ return "sha256-" + createHash26("sha256").update(buf).digest("base64");
140347
140429
  }
140348
140430
  async function widgetFailureDetail(res) {
140349
140431
  try {
@@ -140771,7 +140853,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140771
140853
  const compareScheme = collect2?.render?.compareScheme;
140772
140854
  const renderCompare = async (descriptor, services) => {
140773
140855
  if (!compareScheme || compareScheme === renderScheme) return {};
140774
- const rr2 = await renderDescriptorWeb(descriptor, renderTokens, compareScheme, false, services, collect2?.render?.width, collect2?.render?.rtl === true);
140856
+ const rr2 = await renderDescriptorWeb(descriptor, renderTokens, compareScheme, false, services, collect2?.render?.width, collect2?.render?.rtl === true, void 0, base);
140775
140857
  return { compare: { scheme: compareScheme, renderable: rr2.renderable, ...rr2.html ? { html: rr2.html } : {}, ...rr2.reason ? { reason: rr2.reason } : {} } };
140776
140858
  };
140777
140859
  const LIVE_LOAD_BUDGET_MS = 3e4;
@@ -141213,7 +141295,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
141213
141295
  }
141214
141296
  if (collect2?.render && (!collect2.render.screenId || collect2.render.screenId === screenId)) {
141215
141297
  const { services, liveCalls, liveReturned, liveErrors, mockCalls, mockUnserved, mockBootFailed, liveFailed } = await renderServicesFor(bundleName, descriptor, screenId);
141216
- const rr2 = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true);
141298
+ const rr2 = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true, void 0, base);
141217
141299
  renders.push({ screenId, scheme: renderScheme, ...rr2, ...await renderCompare(descriptor, services), ...collect2.render.dumpState && services ? { serviceState: services } : {}, ...liveCalls ? { liveCalls } : {}, ...liveReturned !== void 0 ? { liveReturned } : {}, ...liveFailed ? { liveFailed } : {}, ...liveErrors ? { liveErrors } : {}, ...mockCalls ? { mockCalls, mockUnserved } : {}, ...mockBootFailed ? { mockBootFailed } : {} });
141218
141300
  }
141219
141301
  }
@@ -141241,7 +141323,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
141241
141323
  lint.push({ screenId: wantedRender, ok: report3.ok, issues: report3.issues });
141242
141324
  }
141243
141325
  const { services, liveCalls, liveReturned, liveErrors, mockCalls, mockUnserved, mockBootFailed, liveFailed } = await renderServicesFor(bundleName, descriptor, wantedRender);
141244
- const rr2 = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true);
141326
+ const rr2 = await renderDescriptorWeb(descriptor, renderTokens, renderScheme, collect2.render.png === true, services, collect2.render.width, collect2.render.rtl === true, void 0, base);
141245
141327
  renders.push({ screenId: wantedRender, scheme: renderScheme, ...rr2, ...await renderCompare(descriptor, services), ...collect2.render.dumpState && services ? { serviceState: services } : {}, ...liveCalls ? { liveCalls } : {}, ...liveReturned !== void 0 ? { liveReturned } : {}, ...liveFailed ? { liveFailed } : {}, ...liveErrors ? { liveErrors } : {}, ...mockCalls ? { mockCalls, mockUnserved } : {}, ...mockBootFailed ? { mockBootFailed } : {} });
141246
141328
  } else if (res.status === 422) {
141247
141329
  const finding = await depthRefusalOf(res);
@@ -142555,9 +142637,9 @@ __export(studio_exports, {
142555
142637
  renderStudioHtml: () => renderStudioHtml,
142556
142638
  writeStudioRecord: () => writeStudioRecord
142557
142639
  });
142558
- import { createHash as createHash26 } from "node:crypto";
142640
+ import { createHash as createHash27 } from "node:crypto";
142559
142641
  import { existsSync as existsSync51, readdirSync as readdirSync27, readFileSync as readFileSync57, realpathSync as realpathSync8, statSync as statSync12 } from "node:fs";
142560
- import { basename as basename11, extname as extname3, join as join51, relative as relative9, resolve as resolve40, sep as sep11 } from "node:path";
142642
+ import { basename as basename12, extname as extname3, join as join51, relative as relative9, resolve as resolve40, sep as sep11 } from "node:path";
142561
142643
  import { pathToFileURL as pathToFileURL7 } from "node:url";
142562
142644
  function realInside(dir, abs) {
142563
142645
  try {
@@ -142605,7 +142687,7 @@ function observeShots(dir, memory, now) {
142605
142687
  const seen = /* @__PURE__ */ new Set();
142606
142688
  const files = existsSync51(shotsDir) ? readdirSync27(shotsDir).filter((f2) => /\.(png|jpe?g)$/i.test(f2)).sort() : [];
142607
142689
  for (const f2 of files) {
142608
- const id = basename11(f2, extname3(f2));
142690
+ const id = basename12(f2, extname3(f2));
142609
142691
  const archived = archiveFile(dir, memory, join51(shotsDir, f2));
142610
142692
  if (!archived) continue;
142611
142693
  seen.add(id);
@@ -142639,7 +142721,7 @@ function palette(dir) {
142639
142721
  }
142640
142722
  function appIdentity(dir) {
142641
142723
  const manifest = obj(readJson4(join51(dir, "himalaya.content.json")));
142642
- const name = text(manifest?.name) ?? basename11(resolve40(dir));
142724
+ const name = text(manifest?.name) ?? basename12(resolve40(dir));
142643
142725
  const icon = text(manifest?.icon)?.trim();
142644
142726
  const iconPath = icon ? resolve40(dir, icon) : null;
142645
142727
  const real = iconPath ? realInside(dir, iconPath) : null;
@@ -142818,7 +142900,7 @@ function buildView(dir, memory, now) {
142818
142900
  message: e2.message,
142819
142901
  step: isCliEntry(e2) ? null : e2.step ?? null,
142820
142902
  stepNumber: isCliEntry(e2) ? null : e2.stepNumber ?? null,
142821
- images: (e2.images ?? []).map((i2) => historyUrl(basename11(i2.file))),
142903
+ images: (e2.images ?? []).map((i2) => historyUrl(basename12(i2.file))),
142822
142904
  by: isCliEntry(e2) ? CLI_AUTHOR : null
142823
142905
  })),
142824
142906
  screens,
@@ -142837,7 +142919,7 @@ function buildView(dir, memory, now) {
142837
142919
  reveal: memory.reveal ?? null
142838
142920
  };
142839
142921
  const { generatedAt: _ignored, timelineFrames: _count, ...stable } = view;
142840
- const v2 = createHash26("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
142922
+ const v2 = createHash27("sha256").update(JSON.stringify(stable)).digest("hex").slice(0, 16);
142841
142923
  return { v: v2, ...view };
142842
142924
  }
142843
142925
  function writeAtomic(file2, content) {
@@ -149687,7 +149769,7 @@ var init_fxp = __esm({
149687
149769
  });
149688
149770
 
149689
149771
  // ../../packages/serve/src/pkcs12.ts
149690
- import { X509Certificate, createHash as createHash27, createPrivateKey as createPrivateKey2, createPublicKey, pbkdf2Sync, createDecipheriv } from "node:crypto";
149772
+ import { X509Certificate, createHash as createHash28, createPrivateKey as createPrivateKey2, createPublicKey, pbkdf2Sync, createDecipheriv } from "node:crypto";
149691
149773
  function decodePkcs12(der, password) {
149692
149774
  try {
149693
149775
  const pfx = readSequence(der, 0);
@@ -149961,7 +150043,7 @@ function expand(src, v2) {
149961
150043
  return out2;
149962
150044
  }
149963
150045
  function sha12(data) {
149964
- return createHash27("sha1").update(data).digest();
150046
+ return createHash28("sha1").update(data).digest();
149965
150047
  }
149966
150048
  function readAt(buf, i2) {
149967
150049
  if (i2 >= buf.length) return void 0;
@@ -150050,7 +150132,7 @@ var init_pkcs12 = __esm({
150050
150132
  });
150051
150133
 
150052
150134
  // ../../packages/serve/src/customer-signing-assets.ts
150053
- import { createHash as createHash28 } from "node:crypto";
150135
+ import { createHash as createHash29 } from "node:crypto";
150054
150136
  function readValue(node) {
150055
150137
  const [tag, children] = Object.entries(node)[0] ?? [];
150056
150138
  if (!tag || !Array.isArray(children)) return refuse3("customer_profile_invalid");
@@ -150128,7 +150210,7 @@ function toBundle(assets, expected, decoded, profileType, apns, deviceCount) {
150128
150210
  provisioningProfile: Buffer.from(assets.provisioningProfile).toString("base64"),
150129
150211
  profileSha256: sha2565(assets.provisioningProfile),
150130
150212
  certificateSha256: sha2565(decoded.cert.raw),
150131
- certificateSha1: createHash28("sha1").update(decoded.cert.raw).digest("hex").toUpperCase(),
150213
+ certificateSha1: createHash29("sha1").update(decoded.cert.raw).digest("hex").toUpperCase(),
150132
150214
  expiresAt: new Date(Math.min(decoded.expiration, Date.parse(decoded.cert.validTo))).toISOString(),
150133
150215
  ...apns ? { apnsEnvironment: apns } : {},
150134
150216
  deviceCount
@@ -150179,7 +150261,7 @@ var init_customer_signing_assets = __esm({
150179
150261
  init_fxp();
150180
150262
  init_pkcs12();
150181
150263
  init_customer_dist_signing();
150182
- sha2565 = (bytes2) => createHash28("sha256").update(bytes2).digest("hex");
150264
+ sha2565 = (bytes2) => createHash29("sha256").update(bytes2).digest("hex");
150183
150265
  refuse3 = (code) => {
150184
150266
  throw new CustomerSigningError(code);
150185
150267
  };
@@ -150347,7 +150429,7 @@ var init_customer_signing_inspect = __esm({
150347
150429
 
150348
150430
  // src/push-onboarding.ts
150349
150431
  import { existsSync as existsSync53, mkdirSync as mkdirSync24, readFileSync as readFileSync60, statSync as statSync13, writeFileSync as writeFileSync20, chmodSync as chmodSync2 } from "node:fs";
150350
- import { basename as basename12, dirname as dirname32, join as join52, resolve as resolve43 } from "node:path";
150432
+ import { basename as basename13, dirname as dirname32, join as join52, resolve as resolve43 } from "node:path";
150351
150433
  function readJson5(path2) {
150352
150434
  try {
150353
150435
  const parsed = JSON.parse(readFileSync60(path2, "utf8"));
@@ -150368,7 +150450,7 @@ function pushInputsFromContent(dir) {
150368
150450
  // autoClaimSlug's docblock describes as the project-declared slug. Without the fallback a
150369
150451
  // package with no `name` sent a body missing `app` and earned `400 invalid_app_id` with both
150370
150452
  // private keys already on the wire.
150371
- app: text3(content.name) ?? basename12(root),
150453
+ app: text3(content.name) ?? basename13(root),
150372
150454
  ...text3(provisioning.siteId) ? { site: text3(provisioning.siteId) } : {},
150373
150455
  ...text3(native.iosBundleId) ? { bundleId: text3(native.iosBundleId) } : {},
150374
150456
  ...text3(native.androidApplicationId) ? { androidApplicationId: text3(native.androidApplicationId) } : {}
@@ -150978,7 +151060,7 @@ __export(config_push_edit_exports, {
150978
151060
  previewPushConfigEdit: () => previewPushConfigEdit
150979
151061
  });
150980
151062
  import { readFileSync as readFileSync62 } from "node:fs";
150981
- import { basename as basename13 } from "node:path";
151063
+ import { basename as basename14 } from "node:path";
150982
151064
  import ts6 from "typescript";
150983
151065
  function enablePushInConfig(source, options) {
150984
151066
  const pingAppName = DEFAULT_PING_APP_NAME;
@@ -151097,13 +151179,13 @@ function noLiteralInstruction(pingAppName) {
151097
151179
  function previewPushConfigEdit(dir, options) {
151098
151180
  const path2 = configEntry(dir);
151099
151181
  if (!path2) return void 0;
151100
- const { applied, instructions } = enablePushInConfig(readFileSync62(path2, "utf8"), { ...options, fileName: basename13(path2) });
151182
+ const { applied, instructions } = enablePushInConfig(readFileSync62(path2, "utf8"), { ...options, fileName: basename14(path2) });
151101
151183
  return { wouldApply: applied, instructions };
151102
151184
  }
151103
151185
  async function applyPushConfigEdit(dir, options, validate5) {
151104
151186
  const path2 = configEntry(dir);
151105
151187
  if (!path2) return void 0;
151106
- const fileName = basename13(path2);
151188
+ const fileName = basename14(path2);
151107
151189
  const refuse7 = (why) => ({ changed: false, pushOn: false, applied: [], instructions: [why] });
151108
151190
  if (isSymlink2(path2)) return refuse7(`${fileName} is a symlink, so it was not edited: turn push on in the file it points to.`);
151109
151191
  const original = readFileSync62(path2, "utf8");
@@ -151424,7 +151506,7 @@ __export(pack_exports, {
151424
151506
  unpackTo: () => unpackTo,
151425
151507
  writeZip: () => writeZip
151426
151508
  });
151427
- import { createHash as createHash29 } from "node:crypto";
151509
+ import { createHash as createHash30 } from "node:crypto";
151428
151510
  import { deflateRawSync, inflateRawSync } from "node:zlib";
151429
151511
  import { existsSync as existsSync55, mkdirSync as mkdirSync25, readFileSync as readFileSync63, readdirSync as readdirSync28, rmSync as rmSync12, statSync as statSync15, writeFileSync as writeFileSync21 } from "node:fs";
151430
151512
  import { dirname as dirname33, join as join54, resolve as resolve46 } from "node:path";
@@ -151574,11 +151656,11 @@ function gatherReleaseEntries(appDir3) {
151574
151656
  return { entries, manifest, skipped };
151575
151657
  }
151576
151658
  function sha256Hex4(bytes2) {
151577
- return createHash29("sha256").update(bytes2).digest("hex");
151659
+ return createHash30("sha256").update(bytes2).digest("hex");
151578
151660
  }
151579
151661
  function computeContentHash(entries) {
151580
151662
  const input = packageHashInput(entries.map((e2) => ({ path: e2.path, hash: sha256Hex4(e2.bytes) })));
151581
- return "sha256-" + createHash29("sha256").update(input).digest("base64");
151663
+ return "sha256-" + createHash30("sha256").update(input).digest("base64");
151582
151664
  }
151583
151665
  function buildEntries(appDir3, app, opts) {
151584
151666
  const { entries: released, manifest, skipped } = gatherReleaseEntries(appDir3);
@@ -151769,7 +151851,7 @@ __export(app_source_exports, {
151769
151851
  writeConflicts: () => writeConflicts,
151770
151852
  zipAppSource: () => zipAppSource
151771
151853
  });
151772
- import { createHash as createHash30 } from "node:crypto";
151854
+ import { createHash as createHash31 } from "node:crypto";
151773
151855
  import { readdirSync as readdirSync29, lstatSync as lstatSync15, readFileSync as readFileSync64, mkdirSync as mkdirSync26, writeFileSync as writeFileSync22, existsSync as existsSync56, rmSync as rmSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync4 } from "node:fs";
151774
151856
  import { join as join55, resolve as resolve47, sep as sep13 } from "node:path";
151775
151857
  import { createRequire as createRequire3 } from "node:module";
@@ -151885,13 +151967,13 @@ function sourceFingerprint(dir) {
151885
151967
  dir,
151886
151968
  (archivePath, hostRel) => {
151887
151969
  if (/\.md$/i.test(archivePath)) return;
151888
- files.push(`${archivePath}\0${createHash30("sha256").update(readFileSync64(join55(dir, hostRel))).digest("hex")}`);
151970
+ files.push(`${archivePath}\0${createHash31("sha256").update(readFileSync64(join55(dir, hostRel))).digest("hex")}`);
151889
151971
  },
151890
151972
  () => {
151891
151973
  }
151892
151974
  );
151893
151975
  files.sort();
151894
- return createHash30("sha256").update(files.join("\n")).digest("hex");
151976
+ return createHash31("sha256").update(files.join("\n")).digest("hex");
151895
151977
  }
151896
151978
  function largestEntries(entries, limit = 5) {
151897
151979
  return entries.map((e2) => ({
@@ -152085,6 +152167,7 @@ __export(run_watch_exports, {
152085
152167
  ReloadBus: () => ReloadBus,
152086
152168
  boardFile: () => boardFile,
152087
152169
  diffReleases: () => diffReleases,
152170
+ hhpRasterDir: () => hhpRasterDir,
152088
152171
  himiDir: () => himiDir,
152089
152172
  ignoredChange: () => ignoredChange,
152090
152173
  isEmptyDiff: () => isEmptyDiff,
@@ -152100,7 +152183,7 @@ __export(run_watch_exports, {
152100
152183
  });
152101
152184
  import { closeSync as closeSync4, constants as constants2, existsSync as existsSync57, fstatSync, lstatSync as lstatSync16, statSync as statSync16, readdirSync as readdirSync30, mkdirSync as mkdirSync27, openSync as openSync4, readFileSync as readFileSync65, realpathSync as realpathSync9, renameSync as renameSync10, rmSync as rmSync14, writeFileSync as writeFileSync23, watch as fsWatch } from "node:fs";
152102
152185
  import { randomUUID as randomUUID6 } from "node:crypto";
152103
- import { extname as extname4, join as join56, resolve as resolve48, sep as sep14 } from "node:path";
152186
+ import { extname as extname4, join as join56, relative as relative11, resolve as resolve48, sep as sep14 } from "node:path";
152104
152187
  function diffReleases(prev, next) {
152105
152188
  const bundles = Object.keys(next.bundles).filter((b2) => prev.bundles[b2]?.sha256 !== next.bundles[b2].sha256).sort();
152106
152189
  const changedBundles = new Set(bundles);
@@ -152125,10 +152208,20 @@ function reloadEvents(d2) {
152125
152208
  if (d2.config) out2.push(["config.invalidated", {}]);
152126
152209
  return out2;
152127
152210
  }
152128
- function ignoredChange(relPath, isDirectory = () => false) {
152211
+ function hhpRasterDir(dir) {
152212
+ const assets = findAssetsDir(dir);
152213
+ if (!assets) return null;
152214
+ const rel = relative11(dir, assets).split(sep14).join("/");
152215
+ return rel ? `${rel}/hhp-raster` : "hhp-raster";
152216
+ }
152217
+ function ignoredChange(relPath, isDirectory = () => false, rasterDir = "assets/hhp-raster") {
152129
152218
  const parts = relPath.split(/[\\/]/).filter(Boolean);
152130
152219
  if (parts[0] === "dev" && (parts[1] === "hhp" || parts[1]?.startsWith(".hhp-"))) return true;
152131
152220
  if (parts[0] === "tier5-src" && parts[1]?.startsWith("hhp-")) return true;
152221
+ if (rasterDir) {
152222
+ const rel = parts.join("/");
152223
+ if (rel === rasterDir || rel.startsWith(`${rasterDir}/`)) return true;
152224
+ }
152132
152225
  if (!parts.length) return true;
152133
152226
  const [top] = parts;
152134
152227
  if ([".himi", "dist", "node_modules", ".git", ".build", ...AGENT_TOOLING_DIRS].includes(top)) return true;
@@ -152139,11 +152232,12 @@ function ignoredChange(relPath, isDirectory = () => false) {
152139
152232
  }
152140
152233
  function watchContent(dir, onChange, opts = {}) {
152141
152234
  const debounceMs = opts.debounceMs ?? 200;
152235
+ const rasterDir = hhpRasterDir(dir);
152142
152236
  const watch2 = opts.watch ?? ((p2, o2, l2) => fsWatch(p2, o2, (event, file2) => l2(event, file2 === null ? null : String(file2))));
152143
152237
  let timer = null;
152144
152238
  const pending = /* @__PURE__ */ new Set();
152145
152239
  const changed = (_event, file2) => {
152146
- if (!file2 || ignoredChange(file2, (rel) => isDir(join56(dir, rel)))) return;
152240
+ if (!file2 || ignoredChange(file2, (rel) => isDir(join56(dir, rel)), rasterDir)) return;
152147
152241
  pending.add(file2);
152148
152242
  if (timer) clearTimeout(timer);
152149
152243
  timer = setTimeout(() => {
@@ -152167,7 +152261,7 @@ function watchContent(dir, onChange, opts = {}) {
152167
152261
  const path2 = join56(directory, String(name)), rel = prefix ? `${prefix}/${name}` : String(name);
152168
152262
  try {
152169
152263
  const stat2 = lstatSync16(path2);
152170
- if (stat2.isSymbolicLink() || ignoredChange(rel, () => stat2.isDirectory())) continue;
152264
+ if (stat2.isSymbolicLink() || ignoredChange(rel, () => stat2.isDirectory(), rasterDir)) continue;
152171
152265
  if (stat2.isDirectory()) visit(path2, rel);
152172
152266
  else if (stat2.isFile()) files.set(rel, `${stat2.mtimeMs}:${stat2.ctimeMs}:${stat2.size}`);
152173
152267
  } catch {
@@ -152369,6 +152463,7 @@ var init_run_watch = __esm({
152369
152463
  "src/run-watch.ts"() {
152370
152464
  init_source_deny();
152371
152465
  init_run_identity();
152466
+ init_app_assets();
152372
152467
  isEmptyDiff = (d2) => !d2.screens.length && !d2.bundles.length && !d2.tokens && !d2.config;
152373
152468
  ReloadBus = class {
152374
152469
  subscribers = /* @__PURE__ */ new Set();
@@ -152413,7 +152508,7 @@ __export(screen_state_exports, {
152413
152508
  shotStateFor: () => shotStateFor,
152414
152509
  withHomeCaveat: () => withHomeCaveat
152415
152510
  });
152416
- import { createHash as createHash31 } from "node:crypto";
152511
+ import { createHash as createHash32 } from "node:crypto";
152417
152512
  import { lstatSync as lstatSync17, readFileSync as readFileSync66 } from "node:fs";
152418
152513
  import { join as join57 } from "node:path";
152419
152514
  function quotableLine(text9) {
@@ -152533,7 +152628,7 @@ var init_screen_state = __esm({
152533
152628
  KEEP = 40;
152534
152629
  LINE_MAX = 120;
152535
152630
  str3 = (v2) => typeof v2 === "string" && v2.trim() ? v2 : void 0;
152536
- sha256Of = (file2) => createHash31("sha256").update(readFileSync66(file2)).digest("hex");
152631
+ sha256Of = (file2) => createHash32("sha256").update(readFileSync66(file2)).digest("hex");
152537
152632
  }
152538
152633
  });
152539
152634
 
@@ -152548,9 +152643,9 @@ __export(studio_reel_exports, {
152548
152643
  thin: () => thin,
152549
152644
  utcDay: () => utcDay
152550
152645
  });
152551
- import { createHash as createHash32 } from "node:crypto";
152646
+ import { createHash as createHash33 } from "node:crypto";
152552
152647
  import { lstatSync as lstatSync18, readFileSync as readFileSync67 } from "node:fs";
152553
- import { basename as basename14, join as join58 } from "node:path";
152648
+ import { basename as basename15, join as join58 } from "node:path";
152554
152649
  function loadMemory2(dir) {
152555
152650
  const m2 = readJson6(join58(dir, ".himi", "studio", "memory.json"));
152556
152651
  if (!isObj3(m2) || m2.version !== 1 || !isObj3(m2.shots) || !Array.isArray(m2.timeline)) return null;
@@ -152606,13 +152701,13 @@ function planReel(dir) {
152606
152701
  message: e2.message,
152607
152702
  step: isCliEntry(e2) ? null : str4(e2.step),
152608
152703
  by: isCliEntry(e2) ? CLI_AUTHOR : null,
152609
- files: (Array.isArray(e2.images) ? e2.images : []).map((i2) => isObj3(i2) && typeof i2.file === "string" ? basename14(i2.file) : "").filter((f2) => HISTORY_NAME.test(f2))
152704
+ files: (Array.isArray(e2.images) ? e2.images : []).map((i2) => isObj3(i2) && typeof i2.file === "string" ? basename15(i2.file) : "").filter((f2) => HISTORY_NAME.test(f2))
152610
152705
  }));
152611
152706
  const times = [summary2.startedAt, ...timeline.map((t2) => t2.ts), memory?.createdAt].filter((x2) => !!x2).sort();
152612
152707
  const startedAt = times[0] ?? null;
152613
152708
  const endedAt = [summary2.lastAt, ...timeline.map((t2) => t2.ts)].filter((x2) => !!x2).sort().pop() ?? null;
152614
152709
  return {
152615
- app: typeof manifest?.name === "string" && manifest.name ? manifest.name : basename14(dir),
152710
+ app: typeof manifest?.name === "string" && manifest.name ? manifest.name : basename15(dir),
152616
152711
  accent,
152617
152712
  startedAt,
152618
152713
  endedAt,
@@ -152637,7 +152732,7 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
152637
152732
  const st2 = lstatSync18(path2);
152638
152733
  if (!st2.isFile() || st2.nlink !== 1) return null;
152639
152734
  const bytes2 = readFileSync67(path2);
152640
- return createHash32("sha256").update(bytes2).digest("hex") === f2.slice(0, 64) && imageExtension(bytes2) === f2.slice(65) ? bytes2 : null;
152735
+ return createHash33("sha256").update(bytes2).digest("hex") === f2.slice(0, 64) && imageExtension(bytes2) === f2.slice(65) ? bytes2 : null;
152641
152736
  } catch {
152642
152737
  return null;
152643
152738
  }
@@ -152858,11 +152953,11 @@ __export(milestones_exports, {
152858
152953
  watchUrlFor: () => watchUrlFor,
152859
152954
  writeHandoverReel: () => writeHandoverReel
152860
152955
  });
152861
- import { createHash as createHash33, randomUUID as randomUUID7 } from "node:crypto";
152956
+ import { createHash as createHash34, randomUUID as randomUUID7 } from "node:crypto";
152862
152957
  import { spawn as spawn5 } from "node:child_process";
152863
152958
  import { closeSync as closeSync5, existsSync as existsSync58, lstatSync as lstatSync19, readFileSync as readFileSync69, statSync as statSync17 } from "node:fs";
152864
152959
  import { createServer as createServer4 } from "node:net";
152865
- import { basename as basename15, dirname as dirname34, isAbsolute as isAbsolute6, join as join60, relative as relative11, resolve as resolve49, sep as sep15 } from "node:path";
152960
+ import { basename as basename16, dirname as dirname34, isAbsolute as isAbsolute6, join as join60, relative as relative12, resolve as resolve49, sep as sep15 } from "node:path";
152866
152961
  function homeShotPath(dir, home) {
152867
152962
  const root = resolve49(dir);
152868
152963
  const base = resolve49(root, ".himi", "shots");
@@ -152877,7 +152972,7 @@ function homeShotPath(dir, home) {
152877
152972
  return shot;
152878
152973
  }
152879
152974
  function displayPath(p2, cwd = process.cwd()) {
152880
- const rel = relative11(cwd, p2);
152975
+ const rel = relative12(cwd, p2);
152881
152976
  return rel && !rel.startsWith("..") && !isAbsolute6(rel) ? rel : p2;
152882
152977
  }
152883
152978
  function progressCommand(opts) {
@@ -153018,7 +153113,7 @@ async function openBoardForSession(dir, cwd = process.cwd()) {
153018
153113
  try {
153019
153114
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
153020
153115
  const { page, url } = openStudio2(dir);
153021
- const rel = relative11(cwd, page);
153116
+ const rel = relative12(cwd, page);
153022
153117
  return { page: rel && !rel.startsWith("..") && !isAbsolute6(rel) ? rel : page, url, opened: false };
153023
153118
  } catch (e2) {
153024
153119
  return { opened: false, error: `the build board was not created: ${e2.message} \u2014 \`himi studio --no-open\` in the app retries it` };
@@ -153502,7 +153597,7 @@ function writeHandoverReel(dir) {
153502
153597
  const { html } = buildReel(dir);
153503
153598
  const file2 = join60(dir, ".himi", "making-of.html");
153504
153599
  writeFileAtomic(file2, html);
153505
- return { file: file2, hint: `hand ${basename15(file2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` };
153600
+ return { file: file2, hint: `hand ${basename16(file2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` };
153506
153601
  } catch (e2) {
153507
153602
  return { skipped: `the making-of was not written: ${e2.message.split("\n")[0]} \u2014 \`himi studio reel\` retries it` };
153508
153603
  }
@@ -153516,7 +153611,7 @@ function afterHandoverPostcard(dir, opts = {}) {
153516
153611
  } catch {
153517
153612
  fresh = false;
153518
153613
  }
153519
- const makingOf = fresh ? { file: file2, hint: `hand ${basename15(file2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` } : writeHandoverReel(dir);
153614
+ const makingOf = fresh ? { file: file2, hint: `hand ${basename16(file2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` } : writeHandoverReel(dir);
153520
153615
  const next = "file" in makingOf ? [`hand the owner the making-of, ${shellQuote(displayPath(makingOf.file))}, beside the preview link \u2014 in your final message and report, never instead of the link`] : [`\`himi studio reel${contentArg(opts.content)}\` writes the making-of to hand the owner beside the preview link (${makingOf.skipped})`];
153521
153616
  return { makingOf, next };
153522
153617
  }
@@ -153539,7 +153634,7 @@ var init_milestones = __esm({
153539
153634
  FIRST_LOOK_BUDGET_MS = 9e4;
153540
153635
  VALIDATE_PICTURE_BUDGET_MS = 3e4;
153541
153636
  plural2 = (n2, one, many = `${one}s`) => `${n2} ${n2 === 1 ? one : many}`;
153542
- appName = (dir) => appLabel(dir) ?? basename15(dir);
153637
+ appName = (dir) => appLabel(dir) ?? basename16(dir);
153543
153638
  STILL_BUILDING_AFTER_MS = 10 * 60 * 1e3;
153544
153639
  STILL_BUILDING_MESSAGE = "Still building: here's the home screen now.";
153545
153640
  yourFirstLookMessage = (dir) => `First look at your app: ${appName(dir)}'s home screen, with your changes.`;
@@ -153548,7 +153643,7 @@ var init_milestones = __esm({
153548
153643
  INIT_SOURCE_FILE = join60(".himi", "init-source.json");
153549
153644
  STARTING_POINT_REF = "starting-point";
153550
153645
  TEMPLATE_NAME = /^[A-Za-z0-9._-]{1,64}$/;
153551
- sha256Of2 = (file2) => createHash33("sha256").update(readFileSync69(file2)).digest("hex");
153646
+ sha256Of2 = (file2) => createHash34("sha256").update(readFileSync69(file2)).digest("hex");
153552
153647
  WATCH_REMINDER_EVERY_MS = 10 * 60 * 1e3;
153553
153648
  REMINDERS_FILE = join60(".himi", "reminders.json");
153554
153649
  pidAlive = (pid) => {
@@ -154598,7 +154693,7 @@ __export(eject_exports, {
154598
154693
  ejectModule: () => ejectModule
154599
154694
  });
154600
154695
  import { existsSync as existsSync61, mkdirSync as mkdirSync29, readFileSync as readFileSync73, writeFileSync as writeFileSync26 } from "node:fs";
154601
- import { dirname as dirname36, join as join64, relative as relative12, resolve as resolve50, sep as sep16 } from "node:path";
154696
+ import { dirname as dirname36, join as join64, relative as relative13, resolve as resolve50, sep as sep16 } from "node:path";
154602
154697
  function locate(repoRootOrCwd, mod) {
154603
154698
  const inRepo = resolve50(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
154604
154699
  if (existsSync61(inRepo)) return { path: inRepo, form: "source" };
@@ -154651,7 +154746,7 @@ function ejectModule(opts) {
154651
154746
  if (!existsSync61(f2)) continue;
154652
154747
  const before = readFileSync73(f2, "utf8");
154653
154748
  let after = before;
154654
- let rel = relative12(dirname36(f2), target).split(sep16).join("/").replace(/\.tsx?$/, ".js");
154749
+ let rel = relative13(dirname36(f2), target).split(sep16).join("/").replace(/\.tsx?$/, ".js");
154655
154750
  if (!rel.startsWith(".")) rel = `./${rel}`;
154656
154751
  for (const spec of specifiersFor(mod)) {
154657
154752
  const q2 = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -156613,10 +156708,10 @@ var init_calls = __esm({
156613
156708
  });
156614
156709
 
156615
156710
  // ../../core/functions-runtime/src/queue.ts
156616
- import { createHash as createHash34 } from "node:crypto";
156711
+ import { createHash as createHash35 } from "node:crypto";
156617
156712
  function inputSchemaHash(definition) {
156618
156713
  const schema = definition.input;
156619
- return schema ? createHash34("sha256").update(JSON.stringify(schema.descriptor ?? { kind: schema.kind, optional: schema.optional === true })).digest("hex") : "";
156714
+ return schema ? createHash35("sha256").update(JSON.stringify(schema.descriptor ?? { kind: schema.kind, optional: schema.optional === true })).digest("hex") : "";
156620
156715
  }
156621
156716
  function createFunctionEnqueueCapability(options) {
156622
156717
  return {
@@ -156682,14 +156777,14 @@ var init_queue = __esm({
156682
156777
  });
156683
156778
 
156684
156779
  // ../../core/data-resources/src/plan.ts
156685
- import { createHash as createHash35 } from "node:crypto";
156780
+ import { createHash as createHash36 } from "node:crypto";
156686
156781
  function canonical3(value) {
156687
156782
  if (Array.isArray(value)) return `[${value.map(canonical3).join(",")}]`;
156688
156783
  if (value && typeof value === "object") return `{${Object.entries(value).sort(([a2], [b2]) => a2.localeCompare(b2)).map(([key3, item]) => `${JSON.stringify(key3)}:${canonical3(item)}`).join(",")}}`;
156689
156784
  return JSON.stringify(value);
156690
156785
  }
156691
156786
  function hashResourcePlan(plan) {
156692
- return createHash35("sha256").update(canonical3(plan)).digest("hex");
156787
+ return createHash36("sha256").update(canonical3(plan)).digest("hex");
156693
156788
  }
156694
156789
  function finalize(input) {
156695
156790
  return { ...input, hash: hashResourcePlan(input) };
@@ -157656,7 +157751,7 @@ var init_cli_bridge = __esm({
157656
157751
  // src/mcp/file-tools.ts
157657
157752
  import { closeSync as closeSync6, constants as constants3, lstatSync as lstatSync20, mkdirSync as mkdirSync31, openSync as openSync5, readdirSync as readdirSync31, readFileSync as readFileSync78, realpathSync as realpathSync10, renameSync as renameSync11, rmSync as rmSync15, statSync as statSync18, writeSync, existsSync as existsSync66 } from "node:fs";
157658
157753
  import { randomBytes as randomBytes4 } from "node:crypto";
157659
- import { dirname as dirname37, isAbsolute as isAbsolute7, join as join69, relative as relative13, resolve as resolve53, sep as sep17 } from "node:path";
157754
+ import { dirname as dirname37, isAbsolute as isAbsolute7, join as join69, relative as relative14, resolve as resolve53, sep as sep17 } from "node:path";
157660
157755
  import * as z2 from "zod";
157661
157756
  function within(root, p2) {
157662
157757
  return p2 === root || p2.startsWith(root.endsWith(sep17) ? root : root + sep17);
@@ -157674,10 +157769,10 @@ function realish(p2) {
157674
157769
  }
157675
157770
  function symlinkComponent(base, abs) {
157676
157771
  let cur = base;
157677
- for (const part3 of relative13(base, abs).split(sep17).filter(Boolean)) {
157772
+ for (const part3 of relative14(base, abs).split(sep17).filter(Boolean)) {
157678
157773
  cur = join69(cur, part3);
157679
157774
  try {
157680
- if (lstatSync20(cur).isSymbolicLink()) return relative13(base, cur);
157775
+ if (lstatSync20(cur).isSymbolicLink()) return relative14(base, cur);
157681
157776
  } catch {
157682
157777
  return void 0;
157683
157778
  }
@@ -157700,7 +157795,7 @@ async function confinedAppDir(args, deps) {
157700
157795
  if (!within(root, dir) || !within(realpathSync10(root), realish(dir))) return refuse5("outside_workspace", `"${String(args.dir)}" is outside the workspace folder`);
157701
157796
  const link = symlinkComponent(root, dir);
157702
157797
  if (link) return refuse5("outside_workspace", `"${link}" is a symlink \u2014 the app folder must be a real folder in the workspace`);
157703
- const rel = relative13(root, dir);
157798
+ const rel = relative14(root, dir);
157704
157799
  if (rel && (isDeniedSourcePath(`${rel}/x`) || isDeniedSourcePath(`${rel.toLowerCase()}/x`))) return refuse5("denied", `"${rel}" is a folder this tool does not touch`);
157705
157800
  if (!existsSync66(join69(dir, CONTENT_MARKER))) {
157706
157801
  return refuse5("not_an_app", `"${rel || "."}" is not an app folder (no ${CONTENT_MARKER}) \u2014 see himi_list_apps, or create one with himi_init`);
@@ -157719,10 +157814,10 @@ function onDiskSpelling(abs) {
157719
157814
  return join69(realpathSync10.native(cur), ...tail);
157720
157815
  }
157721
157816
  function denied(appDir3, abs) {
157722
- const rel = relative13(appDir3, abs);
157817
+ const rel = relative14(appDir3, abs);
157723
157818
  if (isDeniedSourcePath(rel) || isDeniedSourcePath(rel.toLowerCase())) return true;
157724
157819
  try {
157725
- return isDeniedSourcePath(relative13(realpathSync10.native(appDir3), onDiskSpelling(abs)));
157820
+ return isDeniedSourcePath(relative14(realpathSync10.native(appDir3), onDiskSpelling(abs)));
157726
157821
  } catch {
157727
157822
  return true;
157728
157823
  }
@@ -157755,14 +157850,14 @@ function writeNoFollow2(appDir3, abs, content) {
157755
157850
  const link = symlinkComponent(appDir3, abs);
157756
157851
  if (link || !within(realpathSync10(appDir3), realpathSync10(dirname37(abs)))) {
157757
157852
  rmSync15(tmp, { force: true });
157758
- throw new Error(`"${link ?? relative13(appDir3, dirname37(abs))}" changed into a link while writing`);
157853
+ throw new Error(`"${link ?? relative14(appDir3, dirname37(abs))}" changed into a link while writing`);
157759
157854
  }
157760
157855
  renameSync11(tmp, abs);
157761
157856
  }
157762
157857
  function listFiles(appDir3) {
157763
157858
  const files = [];
157764
157859
  const walk2 = (d2) => {
157765
- const parentRel = relative13(appDir3, d2);
157860
+ const parentRel = relative14(appDir3, d2);
157766
157861
  for (const e2 of readdirSync31(d2, { withFileTypes: true }).sort((a2, b2) => a2.name.localeCompare(b2.name))) {
157767
157862
  if (e2.isSymbolicLink()) continue;
157768
157863
  const p2 = join69(d2, e2.name);
@@ -157770,7 +157865,7 @@ function listFiles(appDir3) {
157770
157865
  if (isDeniedSourceDir(e2.name, parentRel) || isDeniedSourceDir(e2.name.toLowerCase(), parentRel)) continue;
157771
157866
  if (!walk2(p2)) return false;
157772
157867
  } else if (e2.isFile()) {
157773
- const rel = relative13(appDir3, p2);
157868
+ const rel = relative14(appDir3, p2);
157774
157869
  if (isDeniedSourcePath(rel) || isDeniedSourcePath(rel.toLowerCase())) continue;
157775
157870
  if (files.length >= MAX_LISTED) return false;
157776
157871
  files.push({ path: rel.split(sep17).join("/"), bytes: statSync18(p2).size });
@@ -158866,9 +158961,9 @@ __export(browser_authoring_exports, {
158866
158961
  sdkSubpathReader: () => sdkSubpathReader
158867
158962
  });
158868
158963
  import { createServer as createServer5 } from "node:http";
158869
- import { createHash as createHash36, randomBytes as randomBytes5 } from "node:crypto";
158964
+ import { createHash as createHash37, randomBytes as randomBytes5 } from "node:crypto";
158870
158965
  import { existsSync as existsSync68, readdirSync as readdirSync32, readFileSync as readFileSync81, realpathSync as realpathSync11, statSync as statSync19 } from "node:fs";
158871
- import { join as join71, posix as posix3, relative as relative14, sep as sep18 } from "node:path";
158966
+ import { join as join71, posix as posix3, relative as relative15, sep as sep18 } from "node:path";
158872
158967
  function humanMs(ms2) {
158873
158968
  return ms2 < 1e3 ? `${ms2}ms` : `${Math.round(ms2 / 1e3)}s`;
158874
158969
  }
@@ -158918,7 +159013,7 @@ function collectRelativeSiblings(files, tier5SrcDir) {
158918
159013
  ];
158919
159014
  for (const abs of candidates) {
158920
159015
  if (!existsSync68(abs) || statSync19(abs).isDirectory()) continue;
158921
- const key3 = `/tier5-src/${relative14(tier5SrcDir, abs).split(sep18).join(posix3.sep)}`;
159016
+ const key3 = `/tier5-src/${relative15(tier5SrcDir, abs).split(sep18).join(posix3.sep)}`;
158922
159017
  if (key3 in files) break;
158923
159018
  files[key3] = readFileSync81(abs, "utf8");
158924
159019
  queue.push(key3);
@@ -158965,7 +159060,7 @@ function encodeJob(job) {
158965
159060
  return Buffer.from(JSON.stringify(job), "utf8").toString("base64");
158966
159061
  }
158967
159062
  function mintToken2(payload, nonce = randomBytes5(16).toString("hex")) {
158968
- const digest2 = createHash36("sha256").update(payload, "utf8").digest("base64url");
159063
+ const digest2 = createHash37("sha256").update(payload, "utf8").digest("base64url");
158969
159064
  return `${nonce}.${digest2}`;
158970
159065
  }
158971
159066
  function defaultOpenBrowser2(url) {
@@ -159417,7 +159512,7 @@ __export(design_kits_exports, {
159417
159512
  listKits: () => listKits2
159418
159513
  });
159419
159514
  import { existsSync as existsSync70, readFileSync as readFileSync83, readdirSync as readdirSync33 } from "node:fs";
159420
- import { join as join73, basename as basename16 } from "node:path";
159515
+ import { join as join73, basename as basename17 } from "node:path";
159421
159516
  import { fileURLToPath as fileURLToPath27 } from "node:url";
159422
159517
  function kitsDir() {
159423
159518
  return KIT_DIR_CANDIDATES.find((p2) => existsSync70(p2));
@@ -159425,7 +159520,7 @@ function kitsDir() {
159425
159520
  function listKits2() {
159426
159521
  const dir = kitsDir();
159427
159522
  if (!dir) return [];
159428
- return readdirSync33(dir).filter((f2) => f2.endsWith(".json")).map((f2) => basename16(f2, ".json")).sort();
159523
+ return readdirSync33(dir).filter((f2) => f2.endsWith(".json")).map((f2) => basename17(f2, ".json")).sort();
159429
159524
  }
159430
159525
  function kitReport(name) {
159431
159526
  const dir = kitsDir();
@@ -159485,7 +159580,7 @@ __export(assets_exports, {
159485
159580
  verifyReport: () => verifyReport
159486
159581
  });
159487
159582
  import { readFileSync as readFileSync84 } from "node:fs";
159488
- import { basename as basename17 } from "node:path";
159583
+ import { basename as basename18 } from "node:path";
159489
159584
  function declaredAssets(dir) {
159490
159585
  return collectAssets(dir).map((a2) => {
159491
159586
  const bytes2 = readFileSync84(a2.path);
@@ -159566,7 +159661,7 @@ function verifyReport(dir, app, manifest) {
159566
159661
  }
159567
159662
  function addAssetPayload(manifest, filePath, opts) {
159568
159663
  const bytes2 = readFileSync84(filePath);
159569
- const name = opts.name ?? basename17(filePath);
159664
+ const name = opts.name ?? basename18(filePath);
159570
159665
  const sha2567 = assetSha256(bytes2);
159571
159666
  const record2 = {
159572
159667
  sha256: sha2567,
@@ -162691,7 +162786,7 @@ var init_iap_api = __esm({
162691
162786
  });
162692
162787
 
162693
162788
  // ../store-release/iap-manifest.ts
162694
- import { createHash as createHash37 } from "node:crypto";
162789
+ import { createHash as createHash38 } from "node:crypto";
162695
162790
  function readTarget(reader, value) {
162696
162791
  const obj4 = reader.object(value, "target", TARGET_KEYS);
162697
162792
  return {
@@ -162837,7 +162932,7 @@ function canonicalJson(value) {
162837
162932
  return JSON.stringify(sortKeys(value));
162838
162933
  }
162839
162934
  function sha256Digest(value) {
162840
- return `sha256:${createHash37("sha256").update(canonicalJson(value)).digest("hex")}`;
162935
+ return `sha256:${createHash38("sha256").update(canonicalJson(value)).digest("hex")}`;
162841
162936
  }
162842
162937
  function manifestDigest(manifest) {
162843
162938
  return sha256Digest(manifest);
@@ -163028,7 +163123,7 @@ var init_iap_pricing = __esm({
163028
163123
  });
163029
163124
 
163030
163125
  // ../store-release/iap-result.ts
163031
- import { basename as basename18 } from "node:path";
163126
+ import { basename as basename19 } from "node:path";
163032
163127
  function missingMetadataRequirement(screenshotState, itemNamesFile) {
163033
163128
  if (screenshotState === void 0) return MISSING_SCREENSHOT;
163034
163129
  const leftover = LEFTOVER_STATES[screenshotState];
@@ -163060,7 +163155,7 @@ function metadataVersion(observed) {
163060
163155
  }
163061
163156
  function screenshotReport(path2, observed) {
163062
163157
  const held = observed?.reviewScreenshot;
163063
- return { requested: basename18(path2), ...held ? { observed: { fileName: held.fileName, state: held.state } } : {} };
163158
+ return { requested: basename19(path2), ...held ? { observed: { fileName: held.fileName, state: held.state } } : {} };
163064
163159
  }
163065
163160
  function itemResult(input) {
163066
163161
  const { target, item, observed } = input;
@@ -163192,8 +163287,8 @@ var init_screenshot_display_types = __esm({
163192
163287
  });
163193
163288
 
163194
163289
  // ../store-release/iap-review-screenshot.ts
163195
- import { createHash as createHash38 } from "node:crypto";
163196
- import { basename as basename19, resolve as resolve56 } from "node:path";
163290
+ import { createHash as createHash39 } from "node:crypto";
163291
+ import { basename as basename20, resolve as resolve56 } from "node:path";
163197
163292
  function pngSize(bytes2) {
163198
163293
  const signed = PNG_SIGNATURE.every((b2, i2) => bytes2[i2] === b2);
163199
163294
  if (!signed || bytes2.length < 24 || bytes2.toString("ascii", 12, 16) !== "IHDR") return void 0;
@@ -163211,7 +163306,7 @@ function readImageSize(bytes2) {
163211
163306
  return pngSize(buffer) ?? jpegSize(buffer);
163212
163307
  }
163213
163308
  function md5Hex(bytes2) {
163214
- return createHash38("md5").update(bytes2).digest("hex");
163309
+ return createHash39("md5").update(bytes2).digest("hex");
163215
163310
  }
163216
163311
  function isAppStoreScreenshotSize(file2) {
163217
163312
  return displayTypeForSize(file2.width, file2.height) !== null;
@@ -163233,7 +163328,7 @@ function readReviewScreenshot(path2, source) {
163233
163328
  }
163234
163329
  const size = readImageSize(bytes2);
163235
163330
  if (!size) throw new Error(`${absolute} is not a PNG or JPEG image`);
163236
- return { ...size, path: absolute, fileName: basename19(absolute), bytes: bytes2, checksum: md5Hex(bytes2) };
163331
+ return { ...size, path: absolute, fileName: basename20(absolute), bytes: bytes2, checksum: md5Hex(bytes2) };
163237
163332
  }
163238
163333
  function loadReviewScreenshots(manifest, source, indices = manifest.items.map((_2, i2) => i2)) {
163239
163334
  const files = /* @__PURE__ */ new Map();
@@ -164144,11 +164239,11 @@ var init_credential_profile = __esm({
164144
164239
  });
164145
164240
 
164146
164241
  // ../store-release/iap-credentials.ts
164147
- import { createHash as createHash39 } from "node:crypto";
164242
+ import { createHash as createHash40 } from "node:crypto";
164148
164243
  import { existsSync as existsSync75, readFileSync as readFileSync90, realpathSync as realpathSync12 } from "node:fs";
164149
164244
  import { homedir as homedir11 } from "node:os";
164150
164245
  function accountFingerprint(keyId, issuerId) {
164151
- return `sha256:${createHash39("sha256").update(`${issuerId}:${keyId}`).digest("hex").slice(0, 16)}`;
164246
+ return `sha256:${createHash40("sha256").update(`${issuerId}:${keyId}`).digest("hex").slice(0, 16)}`;
164152
164247
  }
164153
164248
  function requireExplicitProfile(profileFlag, target) {
164154
164249
  const profile = profileFlag?.trim();
@@ -164235,7 +164330,7 @@ var init_wix_payments_contract = __esm({
164235
164330
  });
164236
164331
 
164237
164332
  // ../store-release/iap-draft.ts
164238
- import { createHash as createHash40 } from "node:crypto";
164333
+ import { createHash as createHash41 } from "node:crypto";
164239
164334
  function fit(text9, max) {
164240
164335
  const chars = [...text9];
164241
164336
  return chars.length <= max ? text9 : `${chars.slice(0, max - 1).join("").trimEnd()}${ELLIPSIS}`;
@@ -164385,7 +164480,7 @@ var init_iap_draft = __esm({
164385
164480
  TIER_DESCRIPTION = "Unlocks one item in the app";
164386
164481
  ELLIPSIS = "\u2026";
164387
164482
  entityKey = (entityType, entityId) => `${entityType}:${entityId}`;
164388
- sha = (text9) => createHash40("sha256").update(text9).digest("hex");
164483
+ sha = (text9) => createHash41("sha256").update(text9).digest("hex");
164389
164484
  ENTITIES = { "&amp;": "&", "&nbsp;": " ", "&lt;": "<", "&gt;": ">", "&quot;": '"', "&#39;": "'" };
164390
164485
  MAX_SUFFIX = 24;
164391
164486
  ReferenceNames = class {
@@ -164711,7 +164806,7 @@ var init_store_spec = __esm({
164711
164806
 
164712
164807
  // src/iap-config-edit.ts
164713
164808
  import { existsSync as existsSync76, readFileSync as readFileSync92 } from "node:fs";
164714
- import { basename as basename20, join as join77 } from "node:path";
164809
+ import { basename as basename21, join as join77 } from "node:path";
164715
164810
  import ts8 from "typescript";
164716
164811
  function bindPurchasesAppInConfig(source, options) {
164717
164812
  const fileName = options.fileName ?? "config.ts";
@@ -164798,14 +164893,14 @@ function withStoreBundle(dir, options) {
164798
164893
  function previewIapConfigEdit(dir, options) {
164799
164894
  const path2 = configEntry(dir);
164800
164895
  if (!path2) return void 0;
164801
- if (isSymlink2(path2)) return { path: path2, changed: false, applied: [], conflicts: [], instructions: [`${basename20(path2)} is a symlink, so it would not be edited: add the purchases app in the file it points to.`] };
164802
- const { changed, applied, conflicts, instructions } = bindPurchasesAppInConfig(readFileSync92(path2, "utf8"), { ...withStoreBundle(dir, options), fileName: basename20(path2) });
164896
+ if (isSymlink2(path2)) return { path: path2, changed: false, applied: [], conflicts: [], instructions: [`${basename21(path2)} is a symlink, so it would not be edited: add the purchases app in the file it points to.`] };
164897
+ const { changed, applied, conflicts, instructions } = bindPurchasesAppInConfig(readFileSync92(path2, "utf8"), { ...withStoreBundle(dir, options), fileName: basename21(path2) });
164803
164898
  return { path: path2, changed, applied, conflicts, instructions };
164804
164899
  }
164805
164900
  async function applyIapConfigEdit(dir, options, validate5) {
164806
164901
  const path2 = configEntry(dir);
164807
164902
  if (!path2) return void 0;
164808
- const fileName = basename20(path2);
164903
+ const fileName = basename21(path2);
164809
164904
  const refuse7 = (why) => ({ path: path2, changed: false, applied: [], conflicts: [], instructions: [why] });
164810
164905
  if (isSymlink2(path2)) return refuse7(`${fileName} is a symlink, so it was not edited: add the purchases app in the file it points to.`);
164811
164906
  const original = readFileSync92(path2, "utf8");
@@ -165265,7 +165360,7 @@ var init_store_iap_connect = __esm({
165265
165360
  });
165266
165361
 
165267
165362
  // src/store-iap-screenshots.ts
165268
- import { dirname as dirname41, join as join78, relative as relative15 } from "node:path";
165363
+ import { dirname as dirname41, join as join78, relative as relative16 } from "node:path";
165269
165364
  function purchaseScreensFor(item) {
165270
165365
  const screen = item.wix && PURCHASE_SCREENS[item.wix.entityType];
165271
165366
  if (!screen || !item.wix) return [];
@@ -165393,7 +165488,7 @@ async function shoot(selected, run2, outDir) {
165393
165488
  } catch (e2) {
165394
165489
  return { ...report3, skipped: `could not save the screenshot: ${e2.message}` };
165395
165490
  }
165396
- return { ...report3, screenshot: relative15(dirname41(run2.manifestPath), path2) };
165491
+ return { ...report3, screenshot: relative16(dirname41(run2.manifestPath), path2) };
165397
165492
  }
165398
165493
  function visibleText2(html) {
165399
165494
  return html.replace(/&nbsp;|&#160;|&#xa0;/gi, "\xA0").replace(/&amp;/g, "&");
@@ -166669,7 +166764,7 @@ __export(preflight_app_exports, {
166669
166764
  readXcconfigVars: () => readXcconfigVars
166670
166765
  });
166671
166766
  import { readFileSync as readFileSync95, existsSync as existsSync78, readdirSync as readdirSync34, statSync as statSync21 } from "node:fs";
166672
- import { createHash as createHash41 } from "node:crypto";
166767
+ import { createHash as createHash42 } from "node:crypto";
166673
166768
  import { dirname as dirname43, join as join81 } from "node:path";
166674
166769
  function analyze(projectYml, storeConfig, icon, account) {
166675
166770
  const f2 = [];
@@ -167050,7 +167145,7 @@ function placeholderHashes2() {
167050
167145
  const p2 = join81(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
167051
167146
  try {
167052
167147
  if (!statSync21(p2).isFile()) continue;
167053
- return [LEGACY_PLACEHOLDER_SHA2562, createHash41("sha256").update(readFileSync95(p2)).digest("hex")];
167148
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash42("sha256").update(readFileSync95(p2)).digest("hex")];
167054
167149
  } catch {
167055
167150
  }
167056
167151
  }
@@ -167087,7 +167182,7 @@ async function appIconState(iosDir) {
167087
167182
  try {
167088
167183
  if (!statSync21(p2).isFile()) continue;
167089
167184
  const bytes2 = readFileSync95(p2);
167090
- const hash2 = createHash41("sha256").update(bytes2).digest("hex");
167185
+ const hash2 = createHash42("sha256").update(bytes2).digest("hex");
167091
167186
  const pixels = await pixelIssues(bytes2);
167092
167187
  return {
167093
167188
  exists: true,
@@ -167690,7 +167785,7 @@ var init_site_member = __esm({
167690
167785
  });
167691
167786
 
167692
167787
  // src/notify-ping-all.ts
167693
- import { createHash as createHash42 } from "node:crypto";
167788
+ import { createHash as createHash43 } from "node:crypto";
167694
167789
  async function listApprovedMembers(input) {
167695
167790
  const first = await scanMembers(input);
167696
167791
  const second = await scanMembers(input);
@@ -167744,7 +167839,7 @@ async function scanMembers(input) {
167744
167839
  }
167745
167840
  }
167746
167841
  function campaignRequestId(campaignId, memberId) {
167747
- const bytes2 = createHash42("sha256").update(JSON.stringify(["himi-push-campaign", campaignId, memberId])).digest().subarray(0, 16);
167842
+ const bytes2 = createHash43("sha256").update(JSON.stringify(["himi-push-campaign", campaignId, memberId])).digest().subarray(0, 16);
167748
167843
  bytes2[6] = bytes2[6] & 15 | 80;
167749
167844
  bytes2[8] = bytes2[8] & 63 | 128;
167750
167845
  const hex2 = bytes2.toString("hex");
@@ -168728,7 +168823,7 @@ __export(generate_exports, {
168728
168823
  withHeader: () => withHeader,
168729
168824
  writeGenerated: () => writeGenerated
168730
168825
  });
168731
- import { createHash as createHash43 } from "node:crypto";
168826
+ import { createHash as createHash44 } from "node:crypto";
168732
168827
  import { existsSync as existsSync80, mkdirSync as mkdirSync38, readdirSync as readdirSync35, readFileSync as readFileSync100, writeFileSync as writeFileSync34 } from "node:fs";
168733
168828
  import { dirname as dirname46, join as join84 } from "node:path";
168734
168829
  function withHeader(body) {
@@ -168894,7 +168989,7 @@ var init_generate = __esm({
168894
168989
  GENERATED_HEADER = "// @generated by himi base44 screens \u2014 sha256:";
168895
168990
  HEADER_RE = /^\/\/ @generated by himi base44 screens — sha256:([0-9a-f]{64})\n/;
168896
168991
  ENGINE_FILES = ["tier5-src/_shared/entity-list.ts", "tier5-src/_shared/entity-detail.ts", "tier5-src/_shared/entity-plan.ts", "dev/entity-screens.ts"];
168897
- sha2566 = (s2) => createHash43("sha256").update(s2).digest("hex");
168992
+ sha2566 = (s2) => createHash44("sha256").update(s2).digest("hex");
168898
168993
  planFile = (p2) => `${p2.ids.bundleDetail}-plan`;
168899
168994
  ident = (p2) => `plan_${p2.ids.detail.replace(/^b44_/, "")}`;
168900
168995
  json7 = (v2) => JSON.stringify(v2, null, 2);
@@ -172272,9 +172367,9 @@ function tokensFromWst(wst) {
172272
172367
  if (surface2) colors.surfaceSecondary = { light: surface2 };
172273
172368
  const opaque = (c2) => c2 && c2.length === 7 ? c2 : void 0;
172274
172369
  const surface = opaque(usableCssColor(wst["--wst-color-fill-base-1"]));
172275
- const ramp = !surface ? [] : ["--wst-color-fill-base-shade-1", "--wst-color-fill-base-shade-2", "--wst-color-fill-base-shade-3"].map((v2) => opaque(usableCssColor(wst[v2]))).filter((c2) => !!c2).map((c2) => ({ c: c2, ratio: contrastRatio(c2, surface) }));
172370
+ const ramp = !surface ? [] : ["--wst-color-fill-base-shade-1", "--wst-color-fill-base-shade-2", "--wst-color-fill-base-shade-3"].map((v2) => opaque(usableCssColor(wst[v2]))).filter((c2) => !!c2).map((c2) => ({ c: c2, ratio: contrastRatio2(c2, surface) }));
172276
172371
  const primary = surface ? opaque(usableCssColor(wst["--wst-color-text-primary"])) : void 0;
172277
- const primaryRatio = primary && surface ? contrastRatio(primary, surface) : void 0;
172372
+ const primaryRatio = primary && surface ? contrastRatio2(primary, surface) : void 0;
172278
172373
  const tertiary = primaryRatio === void 0 ? void 0 : ramp.filter((x2) => x2.ratio >= 4.5 && x2.ratio < primaryRatio).sort((a2, b2) => a2.ratio - b2.ratio)[0];
172279
172374
  if (tertiary) colors.textTertiary = { light: tertiary.c };
172280
172375
  const border = ramp.filter((x2) => x2.ratio >= 1.3 && x2.ratio <= 3).sort((a2, b2) => a2.ratio - b2.ratio)[0];
@@ -177074,7 +177169,7 @@ var init_errors2 = __esm({
177074
177169
  });
177075
177170
 
177076
177171
  // src/one-app/collect.ts
177077
- import { createHash as createHash44 } from "node:crypto";
177172
+ import { createHash as createHash45 } from "node:crypto";
177078
177173
  function isObject2(value) {
177079
177174
  return value !== null && typeof value === "object" && !Array.isArray(value);
177080
177175
  }
@@ -177146,7 +177241,7 @@ function semanticFingerprint(structure) {
177146
177241
  }
177147
177242
  return JSON.stringify(value);
177148
177243
  };
177149
- return createHash44("sha256").update(canonical6(structure, true)).digest("hex");
177244
+ return createHash45("sha256").update(canonical6(structure, true)).digest("hex");
177150
177245
  }
177151
177246
  async function collectOneApp(input, deps = {}) {
177152
177247
  if (typeof input.metaSiteId !== "string" || !UUID.test(input.metaSiteId)) {
@@ -177358,7 +177453,7 @@ var init_collect = __esm({
177358
177453
  });
177359
177454
 
177360
177455
  // src/one-app/validate-topbar.ts
177361
- import { createHash as createHash45 } from "node:crypto";
177456
+ import { createHash as createHash46 } from "node:crypto";
177362
177457
  import { readFileSync as readFileSync105, statSync as statSync22 } from "node:fs";
177363
177458
  import { resolve as resolve62, join as join92 } from "node:path";
177364
177459
  import ts9 from "typescript";
@@ -177516,7 +177611,7 @@ function validate4(evidenceDir, contentDir2) {
177516
177611
  if (typeof expectedHash !== "string" || !/^[a-f0-9]{64}$/i.test(expectedHash)) {
177517
177612
  throw new Error("manifest.json is missing a valid raw-structure.json hash.");
177518
177613
  }
177519
- if (createHash45("sha256").update(rawBytes).digest("hex") !== expectedHash) {
177614
+ if (createHash46("sha256").update(rawBytes).digest("hex") !== expectedHash) {
177520
177615
  throw new Error("raw-structure.json does not match the hash recorded in manifest.json.");
177521
177616
  }
177522
177617
  source = authoredControls(JSON.parse(rawBytes.toString("utf8")));
@@ -177993,7 +178088,7 @@ __export(cli_exports2, {
177993
178088
  runOneAppCommand: () => runOneAppCommand
177994
178089
  });
177995
178090
  import { closeSync as closeSync7, constants as constants4, lstatSync as lstatSync22, mkdirSync as mkdirSync43, openSync as openSync6, readFileSync as readFileSync106, realpathSync as realpathSync14, writeFileSync as writeFileSync41 } from "node:fs";
177996
- import { createHash as createHash46 } from "node:crypto";
178091
+ import { createHash as createHash47 } from "node:crypto";
177997
178092
  import { dirname as dirname49, join as join93, parse as parse3, resolve as resolve63 } from "node:path";
177998
178093
  function readOptions(argv, cwd, commandIncluded) {
177999
178094
  let cursor = 0;
@@ -178134,7 +178229,7 @@ validate-topbar checks authored source top-bar controls against the migrated con
178134
178229
  throw new OneAppError("artifact_write_failed", "Could not save the migration evidence. Existing files were kept.", { stage });
178135
178230
  }
178136
178231
  artifacts[name] = path2;
178137
- hashes[name] = createHash46("sha256").update(content).digest("hex");
178232
+ hashes[name] = createHash47("sha256").update(content).digest("hex");
178138
178233
  return path2;
178139
178234
  };
178140
178235
  const progress = (nextStage) => {
@@ -182933,9 +183028,9 @@ var init_context = __esm({
182933
183028
  import { mkdtempSync as mkdtempSync6, readFileSync as readFileSync110, mkdirSync as mkdirSync45, writeFileSync as writeFileSync44, existsSync as existsSync89, rmSync as rmSync24, readdirSync as readdirSync36, statSync as statSync24, lstatSync as lstatSync23, accessSync, constants as constants5, renameSync as renameSync13, cpSync as cpSync3 } from "node:fs";
182934
183029
  import { execFile as execFile2, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn7 } from "node:child_process";
182935
183030
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
182936
- import { createHash as createHash47, randomUUID as randomUUID11 } from "node:crypto";
183031
+ import { createHash as createHash48, randomUUID as randomUUID11 } from "node:crypto";
182937
183032
  import { tmpdir as tmpdir7, homedir as homedir15, hostname as hostname2 } from "node:os";
182938
- import { join as join95, resolve as resolve66, relative as relative16, basename as basename21, dirname as dirname51 } from "node:path";
183033
+ import { join as join95, resolve as resolve66, relative as relative17, basename as basename22, dirname as dirname51 } from "node:path";
182939
183034
  import { fileURLToPath as fileURLToPath30 } from "node:url";
182940
183035
 
182941
183036
  // ../serve-cli/src/client.ts
@@ -187164,7 +187259,7 @@ function shellShaDivergenceWarnings(jobs) {
187164
187259
  }
187165
187260
  async function templatePublishName(sourceDir) {
187166
187261
  const id = await appId(sourceDir);
187167
- return IDENTITY_SENTINELS.some((s2) => id.includes(s2)) ? basename21(sourceDir) : id;
187262
+ return IDENTITY_SENTINELS.some((s2) => id.includes(s2)) ? basename22(sourceDir) : id;
187168
187263
  }
187169
187264
  function templateNameError(name, from) {
187170
187265
  if (name.startsWith(TEMPLATE_APP_PREFIX)) {
@@ -187224,7 +187319,7 @@ async function appId(dir) {
187224
187319
  if (m2.name) return m2.name;
187225
187320
  } catch {
187226
187321
  }
187227
- return basename21(dir);
187322
+ return basename22(dir);
187228
187323
  }
187229
187324
  async function contentVisibility(dir) {
187230
187325
  try {
@@ -188547,7 +188642,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
188547
188642
  try {
188548
188643
  const bytes2 = readFileSync110(iconPath);
188549
188644
  payload.icon = bytes2.toString("base64");
188550
- iconHash = createHash47("sha256").update(bytes2).digest("hex");
188645
+ iconHash = createHash48("sha256").update(bytes2).digest("hex");
188551
188646
  } catch {
188552
188647
  }
188553
188648
  }
@@ -189031,7 +189126,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
189031
189126
  const { openUrl: openUrl2 } = await Promise.resolve().then(() => (init_open_url(), open_url_exports));
189032
189127
  openUrl2(url2);
189033
189128
  }
189034
- const handover = { file: out2, hint: `hand ${basename21(out2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` };
189129
+ const handover = { file: out2, hint: `hand ${basename22(out2)} to the owner at the handover \u2014 one self-contained page of their app taking shape; send it beside the preview link, never instead of it` };
189035
189130
  const report3 = {
189036
189131
  ok: true,
189037
189132
  file: out2,
@@ -190364,7 +190459,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
190364
190459
  const manifest = outcome.manifest;
190365
190460
  const payload = await loadReleaseFromDir(declared, out2);
190366
190461
  const { shellQuote: shellQuote3 } = await Promise.resolve().then(() => (init_shell_quote(), shell_quote_exports));
190367
- const contentRel = relative16(process.cwd(), dir) || ".";
190462
+ const contentRel = relative17(process.cwd(), dir) || ".";
190368
190463
  const contentArg2 = shellQuote3(contentRel);
190369
190464
  if (onlyRaw !== void 0) {
190370
190465
  const onlyDraft = flags.draft === true;
@@ -190426,7 +190521,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
190426
190521
  let localSourceHash;
190427
190522
  try {
190428
190523
  const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
190429
- localSourceHash = createHash47("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
190524
+ localSourceHash = createHash48("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
190430
190525
  } catch {
190431
190526
  }
190432
190527
  return runReleasePatch({
@@ -192115,7 +192210,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
192115
192210
  });
192116
192211
  }
192117
192212
  const bytes2 = readFileSync110(file2);
192118
- const sha2 = createHash47("sha256").update(bytes2).digest("hex");
192213
+ const sha2 = createHash48("sha256").update(bytes2).digest("hex");
192119
192214
  const forScreens = (str10(flags["for-screen"]) ?? "").split(",").map((s2) => s2.trim()).filter(Boolean);
192120
192215
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
192121
192216
  io2.error(JSON.stringify({ ok: false, error: "no_rebind_takes_no_value", message: "--no-rebind is a switch and takes no value; pass it bare.", usage: PATCH_USAGE }));