@wix/himalaya-cli 0.1051.0 → 0.1053.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.
@@ -1,4 +1,4 @@
1
- import type { AppConfigLike } from "@wix/himalaya/core";
1
+ import type { AppConfig, AppConfigLike, FunctionsAppConfig } from "@wix/himalaya/core";
2
2
  import type { IncomingMessage, ServerResponse } from "node:http";
3
3
  import type { ActionHandler } from "./actions/types.js";
4
4
  import type { ScreenDescriptor } from "./builder.js";
@@ -121,6 +121,15 @@ export type DataRouteHandler = (req: IncomingMessage, res: ServerResponse, ctx:
121
121
  store: AppStore;
122
122
  serverBaseUrl: string;
123
123
  }) => void | Promise<void>;
124
+ /** A functions-only app (`defineApp({ kind: "functions", … })`): no screens, and none of a UI
125
+ * app's config. The same test `defineApp` itself applies. */
126
+ export declare function isFunctionsOnlyConfig(config: AppConfigLike): config is FunctionsAppConfig;
127
+ /** An app's config seen as a UI app's, every field optional. `FunctionsAppConfig` declares none
128
+ * of `AppConfig`'s UI fields, so on a functions-only app each one reads as absent, which is what
129
+ * the `?.` / `??` at every call site already assumes. For code that handles both kinds; code
130
+ * past an `isFunctionsOnlyConfig` return should narrow instead. No app at all reads the same way,
131
+ * as `state.app?.module.config.x` did. */
132
+ export declare function uiConfig(config: AppConfigLike | undefined): Partial<AppConfig>;
124
133
  /** The complete contract an app package exports for the dev-server to serve. */
125
134
  export interface AppDevModule {
126
135
  /** The `defineApp()` result from the app's config.ts — single source of identity. */
@@ -15,3 +15,8 @@ export declare function webWixCall(method: string, path: string, body: string |
15
15
  status: number;
16
16
  body: string;
17
17
  }>;
18
+ /** The signed-in member's Wix id, or null when the profile read fails (the identity route then
19
+ * falls back to the login email). The token belongs in `webWixCall`'s TOKEN slot: the identity
20
+ * route used to pass it third, where it became the GET's ignored body, so the read went out as
21
+ * `Authorization: undefined`, always failed, and every member's id was reported as their email. */
22
+ export declare function readMemberId(memberToken: string): Promise<string | null>;
@@ -8549,6 +8549,12 @@ function agendaList(props, opts) {
8549
8549
  }
8550
8550
 
8551
8551
  // ../../core/dev-server/src/app-module.ts
8552
+ function isFunctionsOnlyConfig(config) {
8553
+ return config.kind === "functions";
8554
+ }
8555
+ function uiConfig(config) {
8556
+ return config ?? {};
8557
+ }
8552
8558
  function deriveAppMeta(name, m, platforms = {}) {
8553
8559
  const config = m.config;
8554
8560
  return {
@@ -8658,6 +8664,7 @@ export {
8658
8664
  icon,
8659
8665
  iconButton,
8660
8666
  image,
8667
+ isFunctionsOnlyConfig,
8661
8668
  label,
8662
8669
  labeledContent,
8663
8670
  lazyList,
@@ -8703,6 +8710,7 @@ export {
8703
8710
  timeline,
8704
8711
  toggle,
8705
8712
  tooltip,
8713
+ uiConfig,
8706
8714
  video,
8707
8715
  voiceNote,
8708
8716
  vstack,
package/dist/cli.mjs CHANGED
@@ -4274,8 +4274,9 @@ function confinedHost(ts2, contentDir2, options) {
4274
4274
  host.writeFile = (fileName) => {
4275
4275
  throw new Error(`Tier-5 typecheck attempted to write ${fileName} under noEmit \u2014 refusing`);
4276
4276
  };
4277
- if (host.createDirectory) {
4278
- host.createDirectory = (dir) => {
4277
+ const writable = host;
4278
+ if (writable.createDirectory) {
4279
+ writable.createDirectory = (dir) => {
4279
4280
  throw new Error(`Tier-5 typecheck attempted to create ${dir} under noEmit \u2014 refusing`);
4280
4281
  };
4281
4282
  }
@@ -11371,6 +11372,177 @@ var init_idiom = __esm({
11371
11372
  }
11372
11373
  });
11373
11374
 
11375
+ // ../../core/ts/src/brand-color-math.ts
11376
+ function channel2(hex2, offset) {
11377
+ return Number.parseInt(hex2.slice(offset, offset + 2), 16);
11378
+ }
11379
+ function toHex(r, g, b) {
11380
+ return `#${[r, g, b].map((value) => Math.round(value).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
11381
+ }
11382
+ function mix(color, background, colorWeight) {
11383
+ const values = [1, 3, 5].map((offset) => channel2(color, offset) * colorWeight + channel2(background, offset) * (1 - colorWeight));
11384
+ return toHex(values[0], values[1], values[2]);
11385
+ }
11386
+ function relativeLuminance2(color) {
11387
+ const linear = [1, 3, 5].map((offset) => {
11388
+ const value = channel2(color, offset) / 255;
11389
+ return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
11390
+ });
11391
+ return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
11392
+ }
11393
+ function contrastRatio2(a, b) {
11394
+ const la = relativeLuminance2(a);
11395
+ const lb = relativeLuminance2(b);
11396
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
11397
+ }
11398
+ function lstar(color) {
11399
+ const y = relativeLuminance2(color);
11400
+ return y <= 8856e-6 ? y * 903.3 : 116 * Math.cbrt(y) - 16;
11401
+ }
11402
+ function deriveAccentSurface(accent, surface, weight) {
11403
+ const tint = mix(accent, surface, weight);
11404
+ if (contrastRatio2(tint, surface) >= ACCENT_SURFACE_MIN_DELTA) return tint;
11405
+ const away = relativeLuminance2(surface) < 0.5 ? "#FFFFFF" : "#000000";
11406
+ for (let w = 0.02; w <= 0.5; w += 0.02) {
11407
+ const candidate = mix(away, surface, w);
11408
+ if (contrastRatio2(candidate, surface) >= ACCENT_SURFACE_MIN_DELTA) return candidate;
11409
+ }
11410
+ return tint;
11411
+ }
11412
+ function deriveSecondaryAccent(primary) {
11413
+ const shade2 = mix(primary, "#000000", SECONDARY_SHADE_WEIGHT);
11414
+ if (lstar(shade2) >= SECONDARY_MIN_LSTAR) return shade2;
11415
+ return mix(primary, "#FFFFFF", SECONDARY_SHADE_WEIGHT);
11416
+ }
11417
+ function textOn(color) {
11418
+ const luminance2 = relativeLuminance2(color);
11419
+ const whiteContrast = 1.05 / (luminance2 + 0.05);
11420
+ const blackContrast = (luminance2 + 0.05) / 0.05;
11421
+ return whiteContrast >= blackContrast ? "#FFFFFF" : "#000000";
11422
+ }
11423
+ function tuneDarkAccent(brand, darkSurface, lightSurface) {
11424
+ const kitInherentlyDark = lstar(lightSurface) < 50;
11425
+ if (kitInherentlyDark && contrastRatio2(brand, darkSurface) >= DARK_ARM_TARGET_CONTRAST && lstar(brand) <= DARK_ARM_LSTAR_CAP) {
11426
+ return brand;
11427
+ }
11428
+ let lift = DARK_ARM_MIN_LIFT;
11429
+ let candidate = mix(brand, "#FFFFFF", 1 - lift);
11430
+ while (contrastRatio2(candidate, darkSurface) < DARK_ARM_TARGET_CONTRAST && lift < 0.6) {
11431
+ lift += 0.04;
11432
+ candidate = mix(brand, "#FFFFFF", 1 - lift);
11433
+ }
11434
+ while (lstar(candidate) > DARK_ARM_LSTAR_CAP) {
11435
+ const darker = mix(candidate, "#000000", 0.94);
11436
+ if (contrastRatio2(darker, darkSurface) < DARK_ARM_FLOOR_CONTRAST) break;
11437
+ candidate = darker;
11438
+ }
11439
+ return candidate;
11440
+ }
11441
+ function textOnArm(accent) {
11442
+ return contrastRatio2("#FFFFFF", accent) >= 4.5 ? "#FFFFFF" : textOn(accent);
11443
+ }
11444
+ function tuneHighContrastAccent(brand, surface) {
11445
+ const towards = relativeLuminance2(surface) < 0.5 ? "#FFFFFF" : "#000000";
11446
+ let candidate = brand;
11447
+ for (let lift = 0; lift <= 0.9 && contrastRatio2(candidate, surface) < HIGH_CONTRAST_TARGET; lift += 0.04) {
11448
+ candidate = mix(brand, towards, 1 - lift);
11449
+ }
11450
+ return candidate;
11451
+ }
11452
+ function deriveAccentText(accent, surfaces) {
11453
+ const all = (typeof surfaces === "string" ? [surfaces] : [...surfaces]).map((s) => s.slice(0, 7));
11454
+ const page = all[0];
11455
+ const worst = (color) => Math.min(...all.map((s) => contrastRatio2(color, s)));
11456
+ const opaque = accent.length === 9 ? mix(accent.slice(0, 7), page, channel2(accent, 7) / 255) : accent;
11457
+ if (worst(opaque) >= ACCENT_TEXT_MIN_CONTRAST) return opaque;
11458
+ const pole = worst("#FFFFFF") >= worst("#000000") ? "#FFFFFF" : "#000000";
11459
+ for (let step = 1; step <= 100; step++) {
11460
+ const candidate = mix(pole, opaque, step / 100);
11461
+ if (worst(candidate) >= ACCENT_TEXT_MIN_CONTRAST) return candidate;
11462
+ }
11463
+ if (all.length > 1) {
11464
+ const side2 = (hex2) => relativeLuminance2(hex2) > MID_GREY_LUMINANCE;
11465
+ const sameSide = all.filter((s) => side2(s) === side2(page));
11466
+ return deriveAccentText(accent, sameSide.length < all.length ? sameSide : [page]);
11467
+ }
11468
+ return pole;
11469
+ }
11470
+ function textSurfacesOf(colors) {
11471
+ const out = { light: [], dark: [], highContrast: [] };
11472
+ for (const name of TEXT_BACKDROP_TOKENS) {
11473
+ const value = colors[name];
11474
+ for (const arm of ["light", "dark", "highContrast"]) {
11475
+ const hex2 = typeof value === "string" ? value : value && typeof value === "object" && !Array.isArray(value) ? value[arm] ?? value.light : void 0;
11476
+ if (typeof hex2 === "string" && hex2.startsWith("#") && hex2.length >= 7) out[arm].push(hex2);
11477
+ }
11478
+ }
11479
+ return out;
11480
+ }
11481
+ function stripBrandFromIdiom(tokens) {
11482
+ const idiom = tokens.idiom;
11483
+ if (!idiom || typeof idiom !== "object" || Array.isArray(idiom)) return;
11484
+ for (const overlay of Object.values(idiom)) {
11485
+ if (!overlay || typeof overlay !== "object" || Array.isArray(overlay)) continue;
11486
+ const o = overlay;
11487
+ const colors = o.colors;
11488
+ if (colors && typeof colors === "object" && !Array.isArray(colors)) {
11489
+ for (const key2 of BRAND_COLOR_TOKENS) delete colors[key2];
11490
+ if (Object.keys(colors).length === 0) delete o.colors;
11491
+ }
11492
+ const shadows = o.shadows;
11493
+ const glow = shadows && typeof shadows === "object" && !Array.isArray(shadows) ? shadows[BRAND_GLOW_SHADOW] : void 0;
11494
+ if (glow && typeof glow === "object" && !Array.isArray(glow) && "color" in glow) {
11495
+ delete glow.color;
11496
+ if (Object.keys(glow).length === 0) delete shadows[BRAND_GLOW_SHADOW];
11497
+ if (Object.keys(shadows).length === 0) delete o.shadows;
11498
+ }
11499
+ }
11500
+ }
11501
+ function deriveAccentFamily(primary, secondary, surfaces, textSurfaces = {}) {
11502
+ const primaryDark = tuneDarkAccent(primary, surfaces.dark, surfaces.light);
11503
+ const secondaryDark = tuneDarkAccent(secondary, surfaces.dark, surfaces.light);
11504
+ const primaryHigh = tuneHighContrastAccent(primary, surfaces.highContrast);
11505
+ const secondaryHigh = tuneHighContrastAccent(secondary, surfaces.highContrast);
11506
+ return {
11507
+ accentPrimary: { light: primary, dark: primaryDark, highContrast: primaryHigh },
11508
+ accentSecondary: { light: secondary, dark: secondaryDark, highContrast: secondaryHigh },
11509
+ accentSurface: {
11510
+ light: deriveAccentSurface(primary, surfaces.light, ACCENT_SURFACE_LIGHT_WEIGHT),
11511
+ dark: deriveAccentSurface(primaryDark, surfaces.dark, 0.24),
11512
+ highContrast: deriveAccentSurface(primaryHigh, surfaces.highContrast, 0.24)
11513
+ },
11514
+ textOnAccent: {
11515
+ light: textOnArm(primary),
11516
+ dark: textOnArm(primaryDark),
11517
+ highContrast: textOn(primaryHigh)
11518
+ },
11519
+ accentText: {
11520
+ light: deriveAccentText(primary, [surfaces.light, ...textSurfaces.light ?? []]),
11521
+ dark: deriveAccentText(primaryDark, [surfaces.dark, ...textSurfaces.dark ?? []]),
11522
+ highContrast: deriveAccentText(primaryHigh, [surfaces.highContrast, ...textSurfaces.highContrast ?? []])
11523
+ }
11524
+ };
11525
+ }
11526
+ var ACCENT_SURFACE_MIN_DELTA, ACCENT_SURFACE_LIGHT_WEIGHT, SECONDARY_SHADE_WEIGHT, SECONDARY_MIN_LSTAR, DARK_ARM_MIN_LIFT, DARK_ARM_TARGET_CONTRAST, DARK_ARM_FLOOR_CONTRAST, DARK_ARM_LSTAR_CAP, HIGH_CONTRAST_TARGET, ACCENT_TEXT_MIN_CONTRAST, MID_GREY_LUMINANCE, TEXT_BACKDROP_TOKENS, BRAND_COLOR_TOKENS, BRAND_GLOW_SHADOW;
11527
+ var init_brand_color_math = __esm({
11528
+ "../../core/ts/src/brand-color-math.ts"() {
11529
+ ACCENT_SURFACE_MIN_DELTA = 1.08;
11530
+ ACCENT_SURFACE_LIGHT_WEIGHT = 0.12;
11531
+ SECONDARY_SHADE_WEIGHT = 0.72;
11532
+ SECONDARY_MIN_LSTAR = 22;
11533
+ DARK_ARM_MIN_LIFT = 0.08;
11534
+ DARK_ARM_TARGET_CONTRAST = 3.4;
11535
+ DARK_ARM_FLOOR_CONTRAST = 3;
11536
+ DARK_ARM_LSTAR_CAP = 80;
11537
+ HIGH_CONTRAST_TARGET = 8.5;
11538
+ ACCENT_TEXT_MIN_CONTRAST = 4.5;
11539
+ MID_GREY_LUMINANCE = 0.179;
11540
+ TEXT_BACKDROP_TOKENS = ["surfaceSecondary", "surfaceElevated", "surfaceSubtle", "surfaceTertiary"];
11541
+ BRAND_COLOR_TOKENS = ["accentPrimary", "accentSecondary", "accentSurface", "textOnAccent", "accentText"];
11542
+ BRAND_GLOW_SHADOW = "glow";
11543
+ }
11544
+ });
11545
+
11374
11546
  // src/token-lint.ts
11375
11547
  import { existsSync as existsSync13, readFileSync as readFileSync14 } from "node:fs";
11376
11548
  import { join as join16 } from "node:path";
@@ -11537,13 +11709,71 @@ function contrastFindings(colors) {
11537
11709
  });
11538
11710
  }
11539
11711
  }
11712
+ accentOnSurfaces(colors, declared, findings);
11540
11713
  return [...findings.values()];
11541
11714
  }
11542
- var AA_TEXT, CONTRAST_PAIRS, SCHEMES, channels, toHex, mixHex;
11715
+ function sameHueClearingAll(fg, bgs, target = AA_TEXT) {
11716
+ const base = fg.slice(0, 7);
11717
+ const poles = onLightSide(bgs[0]) ? ["#000000", "#FFFFFF"] : ["#FFFFFF", "#000000"];
11718
+ for (let step = 0; step <= 100; step++) {
11719
+ for (const pole of poles) {
11720
+ const candidate = mixHex(base, pole, 1 - step / 100);
11721
+ if (bgs.every((bg) => contrastRatio(candidate, bg) >= target)) return candidate;
11722
+ }
11723
+ }
11724
+ return void 0;
11725
+ }
11726
+ function accentOnSurfaces(colors, declared, findings) {
11727
+ const hasAccentText = colors.accentText !== void 0;
11728
+ for (const fgName of hasAccentText ? ["accentText", "accentPrimary"] : ["accentPrimary"]) {
11729
+ if (colors[fgName] === void 0) continue;
11730
+ const role = fgName === "accentText" ? "the colour of accent text" : hasAccentText ? "still the colour of some accent text (the selected Tabs label on web, rich-text links on Android)" : "the colour of accent text (no accentText is declared, so accent text is painted with accentPrimary)";
11731
+ accentOnSurfacesFor(fgName, role, colors, declared, findings);
11732
+ }
11733
+ }
11734
+ function accentOnSurfacesFor(fgName, role, colors, declared, findings) {
11735
+ for (const surface of TEXT_BACKDROP_TOKENS) {
11736
+ if (colors[surface] === void 0) continue;
11737
+ for (const scheme of SCHEMES) {
11738
+ if (!declared.has(scheme)) continue;
11739
+ const fg = schemeValue(colors[fgName], scheme);
11740
+ const bg = schemeValue(colors[surface], scheme);
11741
+ if (!fg || !bg) continue;
11742
+ const painted = paintedOver(fg, bg);
11743
+ if (!painted) continue;
11744
+ const pageRaw = schemeValue(colors.surfacePrimary, scheme);
11745
+ const page = pageRaw && paintedOver(fg, pageRaw) ? pageRaw.slice(0, 7) : void 0;
11746
+ const opaqueBg = bg.slice(0, 7);
11747
+ if (page === opaqueBg) continue;
11748
+ if (page && contrastRatio(paintedOver(fg, pageRaw), page) < AA_TEXT) continue;
11749
+ const ratio = contrastRatio(painted, opaqueBg);
11750
+ if (ratio >= AA_TEXT) continue;
11751
+ const key2 = `${fgName}|${surface}|${fg}|${bg}|${page ?? ""}`;
11752
+ const same2 = findings.get(key2);
11753
+ if (same2) {
11754
+ same2.schemes.push(scheme);
11755
+ continue;
11756
+ }
11757
+ const opposite = page !== void 0 && onLightSide(page) !== onLightSide(opaqueBg);
11758
+ const noSingle = page !== void 0 && opposite && contrastRatio(page, opaqueBg) < AA_TEXT * AA_TEXT;
11759
+ const suggestion = noSingle ? void 0 : sameHueClearingAll(painted, page ? [page, opaqueBg] : [opaqueBg]);
11760
+ const measured = (c) => page ? `${contrastRatio(c, opaqueBg).toFixed(2)}:1 on ${surface}, ${contrastRatio(c, page).toFixed(2)}:1 on surfacePrimary` : `${contrastRatio(c, opaqueBg).toFixed(2)}:1`;
11761
+ 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).`;
11762
+ 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 : "");
11763
+ findings.set(key2, {
11764
+ key: key2,
11765
+ schemes: [scheme],
11766
+ render: (schemes) => `colors.${fgName} (${fg}) on colors.${surface} (${bg}) is ${ratio.toFixed(2)}:1 in ${schemes.join(" / ")} \u2014 below the WCAG AA ${AA_TEXT}:1 for text. ${fgName} is ${role}, and a transparent label \u2014 a plain or link button, an inline action \u2014 has no background of its own, so inside a card or sheet it is read against ${surface}, not the page.${fix}`
11767
+ });
11768
+ }
11769
+ }
11770
+ }
11771
+ var AA_TEXT, CONTRAST_PAIRS, SCHEMES, channels, toHex2, mixHex, onLightSide;
11543
11772
  var init_token_lint = __esm({
11544
11773
  "src/token-lint.ts"() {
11545
11774
  init_token_colors();
11546
11775
  init_idiom();
11776
+ init_brand_color_math();
11547
11777
  AA_TEXT = 4.5;
11548
11778
  CONTRAST_PAIRS = [
11549
11779
  {
@@ -11559,11 +11789,12 @@ var init_token_lint = __esm({
11559
11789
  ];
11560
11790
  SCHEMES = ["light", "dark", "highContrast"];
11561
11791
  channels = (hex2) => [1, 3, 5].map((i) => parseInt(hex2.slice(i, i + 2), 16));
11562
- toHex = (rgb) => `#${rgb.map((c) => Math.round(Math.min(255, Math.max(0, c))).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
11792
+ toHex2 = (rgb) => `#${rgb.map((c) => Math.round(Math.min(255, Math.max(0, c))).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
11563
11793
  mixHex = (a, b, w) => {
11564
11794
  const [ca, cb] = [channels(a), channels(b)];
11565
- return toHex(ca.map((c, i) => c * w + cb[i] * (1 - w)));
11795
+ return toHex2(ca.map((c, i) => c * w + cb[i] * (1 - w)));
11566
11796
  };
11797
+ onLightSide = (hex2) => contrastRatio("#000000", hex2) >= contrastRatio("#FFFFFF", hex2);
11567
11798
  }
11568
11799
  });
11569
11800
 
@@ -12332,7 +12563,7 @@ function luminance([r, g, b]) {
12332
12563
  });
12333
12564
  return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
12334
12565
  }
12335
- function contrastRatio2(a, b) {
12566
+ function contrastRatio3(a, b) {
12336
12567
  const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
12337
12568
  return (hi + 0.05) / (lo + 0.05);
12338
12569
  }
@@ -12341,11 +12572,11 @@ async function contrastingBackdrop(mark, requested) {
12341
12572
  const { dominant } = await sharp(mark).stats();
12342
12573
  const ink = [dominant.r, dominant.g, dominant.b];
12343
12574
  const want = hexToRgb(requested);
12344
- if (!want || contrastRatio2(ink, want) >= MIN_BACKDROP_CONTRAST) return requested;
12575
+ if (!want || contrastRatio3(ink, want) >= MIN_BACKDROP_CONTRAST) return requested;
12345
12576
  const towardsWhite = luminance(ink) < 0.5;
12346
12577
  for (const amount of [0.35, 0.55, 0.75, 0.9]) {
12347
- const candidate = mix(want, towardsWhite ? [255, 255, 255] : [0, 0, 0], amount);
12348
- if (contrastRatio2(ink, candidate) >= MIN_BACKDROP_CONTRAST) return rgbToHex(candidate);
12578
+ const candidate = mix2(want, towardsWhite ? [255, 255, 255] : [0, 0, 0], amount);
12579
+ if (contrastRatio3(ink, candidate) >= MIN_BACKDROP_CONTRAST) return rgbToHex(candidate);
12349
12580
  }
12350
12581
  return towardsWhite ? "#FFFFFF" : "#000000";
12351
12582
  }
@@ -12355,7 +12586,7 @@ function hexToRgb(hex2) {
12355
12586
  const n = parseInt(m[1], 16);
12356
12587
  return [n >> 16 & 255, n >> 8 & 255, n & 255];
12357
12588
  }
12358
- function mix(a, b, t) {
12589
+ function mix2(a, b, t) {
12359
12590
  return [0, 1, 2].map((i) => Math.round(a[i] + (b[i] - a[i]) * t));
12360
12591
  }
12361
12592
  function rgbToHex([r, g, b]) {
@@ -13637,7 +13868,7 @@ function listReport(opts = {}) {
13637
13868
  for (const [name, sys] of Object.entries(catalog.system)) {
13638
13869
  families.push({ family: name, category: sys.category ?? "serif", variable: false, weights: "platform", kb: 0, license: "system" });
13639
13870
  }
13640
- return { ok: true, count: families.length, totalBytes, families };
13871
+ return { ok: true, count: families.length, totalBytes, problems: [], families };
13641
13872
  }
13642
13873
  async function fontFindings(appDir2, opts = {}) {
13643
13874
  const tokensPath = join22(appDir2, configuredTokensPath(appDir2).path ?? "tokens.json");
@@ -16763,6 +16994,12 @@ var init_agent_context = __esm({
16763
16994
  });
16764
16995
 
16765
16996
  // ../../core/dev-server/src/app-module.ts
16997
+ function isFunctionsOnlyConfig(config) {
16998
+ return config.kind === "functions";
16999
+ }
17000
+ function uiConfig(config) {
17001
+ return config ?? {};
17002
+ }
16766
17003
  function deriveAppMeta(name, m, platforms = {}) {
16767
17004
  const config = m.config;
16768
17005
  return {
@@ -17044,7 +17281,10 @@ async function loadContainedDevModule(name, entry, dir) {
17044
17281
  "the contained dev-module realm is not installed, so the upload's dev/index.ts would have been imported into the host realm (which is not a security boundary)"
17045
17282
  );
17046
17283
  }
17047
- const { buildContainedDevModule: buildContainedDevModule2, CONTAINED_DEV_MODULE_SERVICE: CONTAINED_DEV_MODULE_SERVICE2, CONTAINED_DEV_MODULE_ACTION: CONTAINED_DEV_MODULE_ACTION2 } = await Promise.resolve().then(() => (init_build_lib(), build_lib_exports));
17284
+ const { buildContainedDevModule: buildContainedDevModule2, CONTAINED_DEV_MODULE_SERVICE: CONTAINED_DEV_MODULE_SERVICE2, CONTAINED_DEV_MODULE_ACTION: CONTAINED_DEV_MODULE_ACTION2 } = (
17285
+ // @ts-expect-error — .mjs sibling without types; the cast below states what this uses.
17286
+ await Promise.resolve().then(() => (init_build_lib(), build_lib_exports))
17287
+ );
17048
17288
  let js;
17049
17289
  try {
17050
17290
  js = await buildContainedDevModule2(entry, { absWorkingDir: dir, confineTo: dir });
@@ -17984,6 +18224,7 @@ __export(wix_member_login_exports, {
17984
18224
  memberDirectLogin: () => memberDirectLogin,
17985
18225
  memberDirectRegister: () => memberDirectRegister,
17986
18226
  mintVisitor: () => mintVisitor,
18227
+ readMemberId: () => readMemberId,
17987
18228
  webWixCall: () => webWixCall
17988
18229
  });
17989
18230
  import { createHash as createHash10, randomBytes as randomBytes3 } from "node:crypto";
@@ -18117,6 +18358,16 @@ async function webWixCall(method, path2, body, token, cookie, forwardedHeaders =
18117
18358
  });
18118
18359
  return { status: res.status, body: await res.text() };
18119
18360
  }
18361
+ async function readMemberId(memberToken) {
18362
+ try {
18363
+ const identity = await webWixCall("GET", "/members/v1/members/my", void 0, memberToken);
18364
+ if (identity.status < 200 || identity.status >= 300) return null;
18365
+ const parsed = JSON.parse(identity.body);
18366
+ return parsed.member?.id ?? parsed.id ?? null;
18367
+ } catch {
18368
+ return null;
18369
+ }
18370
+ }
18120
18371
  var API;
18121
18372
  var init_wix_member_login = __esm({
18122
18373
  "../../core/dev-server/src/wix-member-login.ts"() {
@@ -21506,6 +21757,7 @@ function shouldRevealError(args) {
21506
21757
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
21507
21758
  var init_validation = __esm({
21508
21759
  "../../core/ts/src/validation.ts"() {
21760
+ "use strict";
21509
21761
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
21510
21762
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
21511
21763
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -25106,6 +25358,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
25106
25358
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
25107
25359
  var init_worker_subtree = __esm({
25108
25360
  "../../core/ts/src/worker-subtree.ts"() {
25361
+ "use strict";
25109
25362
  init_worker_subtree_safe_types_generated();
25110
25363
  WORKER_SUBTREE_CEILINGS = Object.freeze({
25111
25364
  maxEncodedBytes: 65536,
@@ -33722,7 +33975,7 @@ function makeEtag(body) {
33722
33975
  return `"${createHash16("sha256").update(body).digest("hex").slice(0, 32)}"`;
33723
33976
  }
33724
33977
  function servedTokensPath(app) {
33725
- return app.module.designTokensPath ?? resolve31(app.dir, app.module.config.designTokensPath ?? "tokens.json");
33978
+ return app.module.designTokensPath ?? resolve31(app.dir, uiConfig(app.module.config).designTokensPath ?? "tokens.json");
33726
33979
  }
33727
33980
  function readAppTokens(state) {
33728
33981
  if (!state.app) return null;
@@ -33804,7 +34057,7 @@ function handleL10nRequest(req, res, state) {
33804
34057
  res.end();
33805
34058
  return;
33806
34059
  }
33807
- const sourceLocale = state.app.module.config.locale?.default ?? "en";
34060
+ const sourceLocale = uiConfig(state.app.module.config).locale?.default ?? "en";
33808
34061
  let messages;
33809
34062
  try {
33810
34063
  messages = Object.fromEntries(
@@ -33842,9 +34095,9 @@ async function runScreenDiagnostics(state, screenId, descriptor) {
33842
34095
  nativeActions,
33843
34096
  appWorkerActions,
33844
34097
  strict: false,
33845
- workerSubtreeScreens: new Set(state.app?.module.config.workerSubtreeScreens ?? []),
34098
+ workerSubtreeScreens: new Set(uiConfig(state.app?.module.config).workerSubtreeScreens ?? []),
33846
34099
  // Same a11y rung the build lint applies (RFC 0058 §5) — diagnostics must not disagree.
33847
- a11y: state.app?.module.config.a11y
34100
+ a11y: uiConfig(state.app?.module.config).a11y
33848
34101
  });
33849
34102
  const telemetry = state.telemetryEvents.filter((e) => e.screenId === screenId && (/^worker_/.test(e.type) || e.type === "bundle_load_revoked")).slice(-20);
33850
34103
  const lastError = [...telemetry].reverse().find((e) => /crash|revoked|error|fail/i.test(e.type)) ?? null;
@@ -34195,7 +34448,7 @@ async function buildDescriptorForScreenId(state, screenId, query = new URLSearch
34195
34448
  }
34196
34449
  return null;
34197
34450
  })();
34198
- return built ? ensureTransportCapability(applyTopBarDefault(attachAssignments(built, state, evalCtx, cfg?.overrides), state.app.module.config), state.tier5Dir ?? TIER5_ROOT2) : null;
34451
+ return built ? ensureTransportCapability(applyTopBarDefault(attachAssignments(built, state, evalCtx, cfg?.overrides), uiConfig(state.app.module.config)), state.tier5Dir ?? TIER5_ROOT2) : null;
34199
34452
  }
34200
34453
  return null;
34201
34454
  }
@@ -35033,15 +35286,7 @@ async function handleWebWix(req, res, state) {
35033
35286
  }
35034
35287
  if (path2 === "/v1/__webwix/identity" && (req.method === "GET" || req.method === "POST")) {
35035
35288
  if (!session.memberToken) return reply(200, { authenticated: false, userId: null, email: "" });
35036
- let userId = null;
35037
- try {
35038
- const identity = await webWixCall("GET", "/members/v1/members/my", session.memberToken);
35039
- if (identity.status >= 200 && identity.status < 300) {
35040
- const parsed = JSON.parse(identity.body);
35041
- userId = parsed.member?.id ?? parsed.id ?? null;
35042
- }
35043
- } catch {
35044
- }
35289
+ const userId = await readMemberId(session.memberToken);
35045
35290
  const preferenceIdentity = session.email?.trim().toLowerCase() || userId;
35046
35291
  return reply(200, {
35047
35292
  authenticated: true,
@@ -35616,8 +35861,8 @@ async function handleAdminPlayground(req, res, state) {
35616
35861
  nativeActions,
35617
35862
  appWorkerActions,
35618
35863
  strict,
35619
- workerSubtreeScreens: new Set(state.app?.module.config.workerSubtreeScreens ?? []),
35620
- a11y: state.app?.module.config.a11y
35864
+ workerSubtreeScreens: new Set(uiConfig(state.app?.module.config).workerSubtreeScreens ?? []),
35865
+ a11y: uiConfig(state.app?.module.config).a11y
35621
35866
  });
35622
35867
  return sendJson(res, report2.ok ? 200 : 422, report2);
35623
35868
  }
@@ -39964,7 +40209,7 @@ async function buildReleaseFromContent(contentDir2, dest, opts = {}) {
39964
40209
  }
39965
40210
  function readTokensForLint(preloaded) {
39966
40211
  try {
39967
- const tokenPath = preloaded.module.designTokensPath ?? resolve32(preloaded.dir, preloaded.module.config.designTokensPath ?? "tokens.json");
40212
+ const tokenPath = preloaded.module.designTokensPath ?? resolve32(preloaded.dir, uiConfig(preloaded.module.config).designTokensPath ?? "tokens.json");
39968
40213
  const parsed = JSON.parse(readFileSync42(tokenPath, "utf8"));
39969
40214
  delete parsed._meta;
39970
40215
  return parsed;
@@ -39976,7 +40221,8 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
39976
40221
  const app = preloaded.name;
39977
40222
  const tier5Dir = opts.tier5Dir ?? TIER5_DIR;
39978
40223
  const collect2 = opts.collect;
39979
- if (preloaded.module.config.kind === "functions") {
40224
+ const config = preloaded.module.config;
40225
+ if (isFunctionsOnlyConfig(config)) {
39980
40226
  return buildFunctionsOnlyRelease(preloaded, dest);
39981
40227
  }
39982
40228
  const store = typeof dest === "string" ? new FileReleaseStore(dest) : dest;
@@ -40002,14 +40248,14 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40002
40248
  };
40003
40249
  const LIVE_LOAD_BUDGET_MS = 3e4;
40004
40250
  const appLocalDbSchema = (() => {
40005
- const entities = preloaded.module.config.dataSchema?.entities ?? [];
40251
+ const entities = config.dataSchema?.entities ?? [];
40006
40252
  const names = entities.map((e) => e?.name).filter((n) => typeof n === "string" && n.length > 0);
40007
40253
  return names.length ? names.map((name) => ({ name })) : null;
40008
40254
  })();
40009
40255
  let liveWixCall = null;
40010
40256
  const ensureLiveWixCall = async () => {
40011
40257
  if (liveWixCall) return liveWixCall;
40012
- const auth = preloaded.module.config.auth;
40258
+ const auth = config.auth;
40013
40259
  const clientId = auth?.config?.clientId;
40014
40260
  if (auth?.providerId !== "wix-consumer" || typeof clientId !== "string" || !clientId) {
40015
40261
  throw new Error(
@@ -40094,7 +40340,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40094
40340
  };
40095
40341
  let handlers;
40096
40342
  let minBinaryVersion = null;
40097
- const requiredCapabilities = new Set(preloaded.module.config.capabilities ?? []);
40343
+ const requiredCapabilities = new Set(config.capabilities ?? []);
40098
40344
  const requiredNativeComponents = /* @__PURE__ */ new Set();
40099
40345
  const tierScreens = [];
40100
40346
  const tierBundles = /* @__PURE__ */ new Map();
@@ -40340,11 +40586,11 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40340
40586
  nativeActions,
40341
40587
  appWorkerActions,
40342
40588
  strict: collect2.strict,
40343
- workerSubtreeScreens: new Set(preloaded.module.config.workerSubtreeScreens ?? []),
40344
- l10n: l10nLintOptions(preloaded.module.config.locale, preloaded.dir),
40589
+ workerSubtreeScreens: new Set(config.workerSubtreeScreens ?? []),
40590
+ l10n: l10nLintOptions(config.locale, preloaded.dir),
40345
40591
  // RFC 0058 §5 — the app's own rung of the a11y ladder. Omitted ⇒ the validator's
40346
40592
  // default, "error"; an in-repo app with ratcheted debt declares "warn".
40347
- a11y: preloaded.module.config.a11y
40593
+ a11y: config.a11y
40348
40594
  });
40349
40595
  lint.push({ screenId, ok: report2.ok, issues: report2.issues });
40350
40596
  }
@@ -40372,8 +40618,8 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40372
40618
  nativeActions,
40373
40619
  appWorkerActions,
40374
40620
  strict: collect2.strict,
40375
- workerSubtreeScreens: new Set(preloaded.module.config.workerSubtreeScreens ?? []),
40376
- a11y: preloaded.module.config.a11y
40621
+ workerSubtreeScreens: new Set(config.workerSubtreeScreens ?? []),
40622
+ a11y: config.a11y
40377
40623
  });
40378
40624
  lint.push({ screenId: wantedRender, ok: report2.ok, issues: report2.issues });
40379
40625
  }
@@ -40407,6 +40653,10 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40407
40653
  store: preloaded.module.createStore(app),
40408
40654
  toggles: {},
40409
40655
  query: new URLSearchParams(),
40656
+ // A bake has no requesting member: the anonymous caller the dev-server also defaults to.
40657
+ // `ScreenBuildContext.caller` is documented never-absent, so a `seedIds` may read
40658
+ // `ctx.caller.entitled` unguarded, and would have thrown here without it.
40659
+ caller: { entitled: false },
40410
40660
  serverBaseUrl: base,
40411
40661
  bundleIntegrity: () => null,
40412
40662
  // Resolve for real: a baked templated route whose descriptor references an asset must
@@ -40516,7 +40766,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40516
40766
  }
40517
40767
  Object.assign(catalogs, await bakeCatalogs(
40518
40768
  app,
40519
- preloaded.module.config.locale,
40769
+ config.locale,
40520
40770
  async (tag) => {
40521
40771
  const res = await devFetch(
40522
40772
  `${base}/v1/l10n?app=${encodeURIComponent(app)}&tag=${encodeURIComponent(tag)}`,
@@ -40531,7 +40781,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40531
40781
  const served = await cfgRes.text();
40532
40782
  const prodText = withProdServeAppConfig(
40533
40783
  opts.keepDevOnlyAppConfig ? served : withoutDevOnlyAppConfig(served),
40534
- preloaded.module.config.environments?.prod
40784
+ config.environments?.prod
40535
40785
  );
40536
40786
  const streamPolicyErrors = validateReleaseStreamPolicyConfig(prodText);
40537
40787
  if (streamPolicyErrors.length) {
@@ -40603,7 +40853,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40603
40853
  playerRequirements: {
40604
40854
  capabilities: [...requiredCapabilities].sort(),
40605
40855
  nativeComponents: [...requiredNativeComponents].sort(),
40606
- authProviders: [preloaded.module.config.auth.providerId].filter(Boolean).sort()
40856
+ authProviders: [config.auth.providerId].filter(Boolean).sort()
40607
40857
  },
40608
40858
  iconHash,
40609
40859
  ...brandFonts.length ? { brandFonts } : {},
@@ -40659,7 +40909,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40659
40909
  // core identifies an oversized bundle by SRI, which is unactionable on its own, so the
40660
40910
  // name is resolved back in here (most-specific match: `integrity` is a full SRI, but a
40661
40911
  // descriptor carrying none would fall back to a bare name, and names nest).
40662
- tierCapViolations: checkTierCaps(preloaded.module.config, tierScreens, [...tierBundles.values()]).map((v) => {
40912
+ tierCapViolations: checkTierCaps(config, tierScreens, [...tierBundles.values()]).map((v) => {
40663
40913
  const named = [...tierBundles.values()].filter((b) => v.message.includes(b.integrity)).sort((a, b) => b.integrity.length - a.integrity.length)[0];
40664
40914
  return named ? { ...v, message: `${v.message} \u2014 bundle "${named.name}"` } : v;
40665
40915
  }),
@@ -40672,7 +40922,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
40672
40922
  ...renderTokens !== void 0 ? { tokens: renderTokens } : {},
40673
40923
  ...preloaded.meta.surfaces ? { surfaceNav: preloaded.meta.surfaces } : {},
40674
40924
  auth: (() => {
40675
- const a = preloaded.module.config.auth;
40925
+ const a = config.auth;
40676
40926
  const cid = a?.config?.clientId;
40677
40927
  return { ...a?.providerId ? { providerId: a.providerId } : {}, ...typeof cid === "string" && cid ? { clientId: cid } : {} };
40678
40928
  })()
@@ -40716,7 +40966,7 @@ async function buildFunctionsOnlyRelease(preloaded, dest) {
40716
40966
  const sha = gitSha();
40717
40967
  const manifest = { releaseId: contentReleaseId(content), gitSha: sha, ...content };
40718
40968
  await store.putManifest(app, manifest);
40719
- return { manifest, failedToBuild: [], missingBundles: [], auth: {} };
40969
+ return { manifest, failedToBuild: [], missingBundles: [], auth: {}, tierCapViolations: [] };
40720
40970
  }
40721
40971
  function listBuildableApps() {
40722
40972
  const appsDir = join37(REPO_ROOT7, "apps");
@@ -40729,6 +40979,7 @@ var init_build3 = __esm({
40729
40979
  init_contained_dev_module();
40730
40980
  init_tier5_dir();
40731
40981
  init_build_sha();
40982
+ init_app_module();
40732
40983
  init_app_icon();
40733
40984
  init_app_assets();
40734
40985
  init_plan_fonts();
@@ -48118,6 +48369,14 @@ function memoryStore(manifest, built) {
48118
48369
  },
48119
48370
  async getAppConfig() {
48120
48371
  return null;
48372
+ },
48373
+ // The two members the cast used to paper over. Serve-relative, as FileReleaseStore's is; and
48374
+ // nothing to prune, since this store only ever holds the one release it was built with.
48375
+ bundleUrl(_app, sha2565) {
48376
+ return `/v1/bundles/${sha2565}.bundle.js`;
48377
+ },
48378
+ async prune() {
48379
+ return 0;
48121
48380
  }
48122
48381
  };
48123
48382
  }
@@ -54671,177 +54930,6 @@ var init_tokens3 = __esm({
54671
54930
  }
54672
54931
  });
54673
54932
 
54674
- // ../../core/ts/src/brand-color-math.ts
54675
- function channel2(hex2, offset) {
54676
- return Number.parseInt(hex2.slice(offset, offset + 2), 16);
54677
- }
54678
- function toHex2(r, g, b) {
54679
- return `#${[r, g, b].map((value) => Math.round(value).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
54680
- }
54681
- function mix2(color, background, colorWeight) {
54682
- const values = [1, 3, 5].map((offset) => channel2(color, offset) * colorWeight + channel2(background, offset) * (1 - colorWeight));
54683
- return toHex2(values[0], values[1], values[2]);
54684
- }
54685
- function relativeLuminance2(color) {
54686
- const linear = [1, 3, 5].map((offset) => {
54687
- const value = channel2(color, offset) / 255;
54688
- return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
54689
- });
54690
- return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2];
54691
- }
54692
- function contrastRatio3(a, b) {
54693
- const la = relativeLuminance2(a);
54694
- const lb = relativeLuminance2(b);
54695
- return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
54696
- }
54697
- function lstar(color) {
54698
- const y = relativeLuminance2(color);
54699
- return y <= 8856e-6 ? y * 903.3 : 116 * Math.cbrt(y) - 16;
54700
- }
54701
- function deriveAccentSurface(accent, surface, weight) {
54702
- const tint = mix2(accent, surface, weight);
54703
- if (contrastRatio3(tint, surface) >= ACCENT_SURFACE_MIN_DELTA) return tint;
54704
- const away = relativeLuminance2(surface) < 0.5 ? "#FFFFFF" : "#000000";
54705
- for (let w = 0.02; w <= 0.5; w += 0.02) {
54706
- const candidate = mix2(away, surface, w);
54707
- if (contrastRatio3(candidate, surface) >= ACCENT_SURFACE_MIN_DELTA) return candidate;
54708
- }
54709
- return tint;
54710
- }
54711
- function deriveSecondaryAccent(primary) {
54712
- const shade2 = mix2(primary, "#000000", SECONDARY_SHADE_WEIGHT);
54713
- if (lstar(shade2) >= SECONDARY_MIN_LSTAR) return shade2;
54714
- return mix2(primary, "#FFFFFF", SECONDARY_SHADE_WEIGHT);
54715
- }
54716
- function textOn(color) {
54717
- const luminance2 = relativeLuminance2(color);
54718
- const whiteContrast = 1.05 / (luminance2 + 0.05);
54719
- const blackContrast = (luminance2 + 0.05) / 0.05;
54720
- return whiteContrast >= blackContrast ? "#FFFFFF" : "#000000";
54721
- }
54722
- function tuneDarkAccent(brand, darkSurface, lightSurface) {
54723
- const kitInherentlyDark = lstar(lightSurface) < 50;
54724
- if (kitInherentlyDark && contrastRatio3(brand, darkSurface) >= DARK_ARM_TARGET_CONTRAST && lstar(brand) <= DARK_ARM_LSTAR_CAP) {
54725
- return brand;
54726
- }
54727
- let lift = DARK_ARM_MIN_LIFT;
54728
- let candidate = mix2(brand, "#FFFFFF", 1 - lift);
54729
- while (contrastRatio3(candidate, darkSurface) < DARK_ARM_TARGET_CONTRAST && lift < 0.6) {
54730
- lift += 0.04;
54731
- candidate = mix2(brand, "#FFFFFF", 1 - lift);
54732
- }
54733
- while (lstar(candidate) > DARK_ARM_LSTAR_CAP) {
54734
- const darker = mix2(candidate, "#000000", 0.94);
54735
- if (contrastRatio3(darker, darkSurface) < DARK_ARM_FLOOR_CONTRAST) break;
54736
- candidate = darker;
54737
- }
54738
- return candidate;
54739
- }
54740
- function textOnArm(accent) {
54741
- return contrastRatio3("#FFFFFF", accent) >= 4.5 ? "#FFFFFF" : textOn(accent);
54742
- }
54743
- function tuneHighContrastAccent(brand, surface) {
54744
- const towards = relativeLuminance2(surface) < 0.5 ? "#FFFFFF" : "#000000";
54745
- let candidate = brand;
54746
- for (let lift = 0; lift <= 0.9 && contrastRatio3(candidate, surface) < HIGH_CONTRAST_TARGET; lift += 0.04) {
54747
- candidate = mix2(brand, towards, 1 - lift);
54748
- }
54749
- return candidate;
54750
- }
54751
- function deriveAccentText(accent, surfaces) {
54752
- const all = (typeof surfaces === "string" ? [surfaces] : [...surfaces]).map((s) => s.slice(0, 7));
54753
- const page = all[0];
54754
- const worst = (color) => Math.min(...all.map((s) => contrastRatio3(color, s)));
54755
- const opaque = accent.length === 9 ? mix2(accent.slice(0, 7), page, channel2(accent, 7) / 255) : accent;
54756
- if (worst(opaque) >= ACCENT_TEXT_MIN_CONTRAST) return opaque;
54757
- const pole = worst("#FFFFFF") >= worst("#000000") ? "#FFFFFF" : "#000000";
54758
- for (let step = 1; step <= 100; step++) {
54759
- const candidate = mix2(pole, opaque, step / 100);
54760
- if (worst(candidate) >= ACCENT_TEXT_MIN_CONTRAST) return candidate;
54761
- }
54762
- if (all.length > 1) {
54763
- const side2 = (hex2) => relativeLuminance2(hex2) > MID_GREY_LUMINANCE;
54764
- const sameSide = all.filter((s) => side2(s) === side2(page));
54765
- return deriveAccentText(accent, sameSide.length < all.length ? sameSide : [page]);
54766
- }
54767
- return pole;
54768
- }
54769
- function textSurfacesOf(colors) {
54770
- const out = { light: [], dark: [], highContrast: [] };
54771
- for (const name of TEXT_BACKDROP_TOKENS) {
54772
- const value = colors[name];
54773
- for (const arm of ["light", "dark", "highContrast"]) {
54774
- const hex2 = typeof value === "string" ? value : value && typeof value === "object" && !Array.isArray(value) ? value[arm] ?? value.light : void 0;
54775
- if (typeof hex2 === "string" && hex2.startsWith("#") && hex2.length >= 7) out[arm].push(hex2);
54776
- }
54777
- }
54778
- return out;
54779
- }
54780
- function stripBrandFromIdiom(tokens) {
54781
- const idiom = tokens.idiom;
54782
- if (!idiom || typeof idiom !== "object" || Array.isArray(idiom)) return;
54783
- for (const overlay of Object.values(idiom)) {
54784
- if (!overlay || typeof overlay !== "object" || Array.isArray(overlay)) continue;
54785
- const o = overlay;
54786
- const colors = o.colors;
54787
- if (colors && typeof colors === "object" && !Array.isArray(colors)) {
54788
- for (const key2 of BRAND_COLOR_TOKENS) delete colors[key2];
54789
- if (Object.keys(colors).length === 0) delete o.colors;
54790
- }
54791
- const shadows = o.shadows;
54792
- const glow = shadows && typeof shadows === "object" && !Array.isArray(shadows) ? shadows[BRAND_GLOW_SHADOW] : void 0;
54793
- if (glow && typeof glow === "object" && !Array.isArray(glow) && "color" in glow) {
54794
- delete glow.color;
54795
- if (Object.keys(glow).length === 0) delete shadows[BRAND_GLOW_SHADOW];
54796
- if (Object.keys(shadows).length === 0) delete o.shadows;
54797
- }
54798
- }
54799
- }
54800
- function deriveAccentFamily(primary, secondary, surfaces, textSurfaces = {}) {
54801
- const primaryDark = tuneDarkAccent(primary, surfaces.dark, surfaces.light);
54802
- const secondaryDark = tuneDarkAccent(secondary, surfaces.dark, surfaces.light);
54803
- const primaryHigh = tuneHighContrastAccent(primary, surfaces.highContrast);
54804
- const secondaryHigh = tuneHighContrastAccent(secondary, surfaces.highContrast);
54805
- return {
54806
- accentPrimary: { light: primary, dark: primaryDark, highContrast: primaryHigh },
54807
- accentSecondary: { light: secondary, dark: secondaryDark, highContrast: secondaryHigh },
54808
- accentSurface: {
54809
- light: deriveAccentSurface(primary, surfaces.light, ACCENT_SURFACE_LIGHT_WEIGHT),
54810
- dark: deriveAccentSurface(primaryDark, surfaces.dark, 0.24),
54811
- highContrast: deriveAccentSurface(primaryHigh, surfaces.highContrast, 0.24)
54812
- },
54813
- textOnAccent: {
54814
- light: textOnArm(primary),
54815
- dark: textOnArm(primaryDark),
54816
- highContrast: textOn(primaryHigh)
54817
- },
54818
- accentText: {
54819
- light: deriveAccentText(primary, [surfaces.light, ...textSurfaces.light ?? []]),
54820
- dark: deriveAccentText(primaryDark, [surfaces.dark, ...textSurfaces.dark ?? []]),
54821
- highContrast: deriveAccentText(primaryHigh, [surfaces.highContrast, ...textSurfaces.highContrast ?? []])
54822
- }
54823
- };
54824
- }
54825
- var ACCENT_SURFACE_MIN_DELTA, ACCENT_SURFACE_LIGHT_WEIGHT, SECONDARY_SHADE_WEIGHT, SECONDARY_MIN_LSTAR, DARK_ARM_MIN_LIFT, DARK_ARM_TARGET_CONTRAST, DARK_ARM_FLOOR_CONTRAST, DARK_ARM_LSTAR_CAP, HIGH_CONTRAST_TARGET, ACCENT_TEXT_MIN_CONTRAST, MID_GREY_LUMINANCE, TEXT_BACKDROP_TOKENS, BRAND_COLOR_TOKENS, BRAND_GLOW_SHADOW;
54826
- var init_brand_color_math = __esm({
54827
- "../../core/ts/src/brand-color-math.ts"() {
54828
- ACCENT_SURFACE_MIN_DELTA = 1.08;
54829
- ACCENT_SURFACE_LIGHT_WEIGHT = 0.12;
54830
- SECONDARY_SHADE_WEIGHT = 0.72;
54831
- SECONDARY_MIN_LSTAR = 22;
54832
- DARK_ARM_MIN_LIFT = 0.08;
54833
- DARK_ARM_TARGET_CONTRAST = 3.4;
54834
- DARK_ARM_FLOOR_CONTRAST = 3;
54835
- DARK_ARM_LSTAR_CAP = 80;
54836
- HIGH_CONTRAST_TARGET = 8.5;
54837
- ACCENT_TEXT_MIN_CONTRAST = 4.5;
54838
- MID_GREY_LUMINANCE = 0.179;
54839
- TEXT_BACKDROP_TOKENS = ["surfaceSecondary", "surfaceElevated", "surfaceSubtle", "surfaceTertiary"];
54840
- BRAND_COLOR_TOKENS = ["accentPrimary", "accentSecondary", "accentSurface", "textOnAccent", "accentText"];
54841
- BRAND_GLOW_SHADOW = "glow";
54842
- }
54843
- });
54844
-
54845
54933
  // src/site-analyze/branding-derive.ts
54846
54934
  function secondBrandColour(wst) {
54847
54935
  for (const name of ["--wst-color-fill-accent-2", "--wst-color-fill-accent-3"]) {
@@ -69669,7 +69757,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69669
69757
  const { detectAndroidNativeDecode: detectAndroidNativeDecode2, detectNativeDecode: detectNativeDecode2, nativeDecodePlatforms: nativeDecodePlatforms2 } = await Promise.resolve().then(() => (init_native_decode(), native_decode_exports));
69670
69758
  let platforms;
69671
69759
  try {
69672
- platforms = nativeDecodePlatforms2(flags.native === true ? true : str6(flags.native));
69760
+ platforms = nativeDecodePlatforms2(flags.native === true ? true : str6(flags.native) ?? String(flags.native));
69673
69761
  } catch (error) {
69674
69762
  io.error(JSON.stringify({ error: error.message }));
69675
69763
  return 2;
@@ -71669,10 +71757,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71669
71757
  // right below says how (or whether) the narrowing was measured.
71670
71758
  enabled: (() => {
71671
71759
  const envelope = resolveSurfaceEnvelope(platforms).enabled;
71672
- if (!produced.families) return envelope;
71760
+ const families = produced.families;
71761
+ if (!families) return envelope;
71673
71762
  return envelope.filter((id) => {
71674
71763
  const surface = surfaceById(id);
71675
- return !surface || !isWidgetFamilySurface(surface) || produced.families.has(surfaceFrozenFamily(surface));
71764
+ return !surface || !isWidgetFamilySurface(surface) || families.has(surfaceFrozenFamily(surface));
71676
71765
  });
71677
71766
  })(),
71678
71767
  // Says which question the `produced` column answered, and — when it could not
@@ -73541,7 +73630,8 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
73541
73630
  return 2;
73542
73631
  }
73543
73632
  const environment2 = str6(flags.environment) ?? "preview";
73544
- if (environment2 !== "preview" && environment2 !== "production") {
73633
+ const isResourceEnvironment = (v) => v === "preview" || v === "production";
73634
+ if (!isResourceEnvironment(environment2)) {
73545
73635
  io.error(JSON.stringify({ error: "--environment must be preview or production" }));
73546
73636
  return 2;
73547
73637
  }
@@ -73824,7 +73914,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
73824
73914
  redirectUri: redirectFlag,
73825
73915
  cwd: str6(flags.content) ?? process.cwd()
73826
73916
  });
73827
- print({ ok: true, ...r });
73917
+ print(r);
73828
73918
  return 0;
73829
73919
  } catch (e) {
73830
73920
  io.error(JSON.stringify({ error: e.message }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1051.0",
3
+ "version": "0.1053.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -65,5 +65,5 @@
65
65
  "artifactId": "himalaya-cli"
66
66
  }
67
67
  },
68
- "falconPackageHash": "58b06c2960e8754fcd0e94b0f25c485969f0365bf0292b70f6d85a19"
68
+ "falconPackageHash": "09a8cfb09738de91c22097314d5bb642195d40f284ccec170bb75968"
69
69
  }