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