@wix/himalaya-cli 0.891.0 → 0.893.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +110 -18
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -5241,7 +5241,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
5241
5241
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
5242
5242
  var init_bindings = __esm({
5243
5243
  "../../core/ts/src/bindings.ts"() {
5244
- "use strict";
5245
5244
  BINDING_SIGILS = ["service", "flag", "item", "t"];
5246
5245
  BINDING_SIGIL_RE = new RegExp(
5247
5246
  `^\\$(?:${BINDING_SIGILS.filter((s) => s !== "t").join("|")})\\b|^\\$t:`
@@ -28784,6 +28783,7 @@ __export(widgets_exports, {
28784
28783
  MAX_PRODUCE_CACHE_ENTRIES: () => MAX_PRODUCE_CACHE_ENTRIES,
28785
28784
  PRODUCE_CACHE_TTL_MS: () => PRODUCE_CACHE_TTL_MS,
28786
28785
  WIDGET_BUDGET_MS: () => WIDGET_BUDGET_MS,
28786
+ WidgetRealmUnavailableError: () => WidgetRealmUnavailableError,
28787
28787
  buildViewSnapshot: () => buildViewSnapshot,
28788
28788
  buildWidgetSnapshot: () => buildWidgetSnapshot,
28789
28789
  describeThrown: () => describeThrown,
@@ -28811,9 +28811,7 @@ async function widgetRealm() {
28811
28811
  try {
28812
28812
  return await realmPromise;
28813
28813
  } catch (err) {
28814
- throw new Error(
28815
- "the widget producer's isolated realm (QuickJS-in-WASM) could not be loaded, so widget bundles would have been evaluated in node:vm (which is not a security boundary): " + (err instanceof Error ? err.message : String(err))
28816
- );
28814
+ throw new WidgetRealmUnavailableError(err);
28817
28815
  }
28818
28816
  }
28819
28817
  function resetWidgetRealm() {
@@ -29433,7 +29431,7 @@ function freezeOverrideSnapshot(input, opts = {}) {
29433
29431
  }
29434
29432
  return snapshot2;
29435
29433
  }
29436
- var MAX_TIMELINE_FRAMES, WIDGET_BUDGET_MS, realmPromise, PRODUCE_CACHE_TTL_MS, MAX_PRODUCE_CACHE_ENTRIES, MAX_PRODUCE_ENTRY_BYTES, MAX_PRODUCE_CACHE_BYTES, produceCache, produceCacheGeneration, CAPTURE_SLOT, READBACK_SERVICE;
29434
+ var MAX_TIMELINE_FRAMES, WIDGET_BUDGET_MS, realmPromise, WidgetRealmUnavailableError, PRODUCE_CACHE_TTL_MS, MAX_PRODUCE_CACHE_ENTRIES, MAX_PRODUCE_ENTRY_BYTES, MAX_PRODUCE_CACHE_BYTES, produceCache, produceCacheGeneration, CAPTURE_SLOT, READBACK_SERVICE;
29437
29435
  var init_widgets = __esm({
29438
29436
  "../../core/dev-server/src/widgets.ts"() {
29439
29437
  init_validator();
@@ -29441,6 +29439,15 @@ var init_widgets = __esm({
29441
29439
  MAX_TIMELINE_FRAMES = 48;
29442
29440
  WIDGET_BUDGET_MS = 5e3;
29443
29441
  realmPromise = null;
29442
+ WidgetRealmUnavailableError = class extends Error {
29443
+ constructor(cause) {
29444
+ super(
29445
+ "the widget producer's isolated realm (QuickJS-in-WASM) could not be loaded, so widget bundles would have been evaluated in node:vm (which is not a security boundary): " + describeThrown(cause)
29446
+ );
29447
+ this.cause = cause;
29448
+ this.name = "WidgetRealmUnavailableError";
29449
+ }
29450
+ };
29444
29451
  PRODUCE_CACHE_TTL_MS = 6e4;
29445
29452
  MAX_PRODUCE_CACHE_ENTRIES = 2048;
29446
29453
  MAX_PRODUCE_ENTRY_BYTES = 4 * 1024 * 1024;
@@ -31871,18 +31878,32 @@ async function scanWidgets(dir) {
31871
31878
  out.push({ ...e, bundle: name });
31872
31879
  }
31873
31880
  } catch (err) {
31881
+ if (err instanceof WidgetRealmUnavailableError) throw err;
31874
31882
  console.warn(`[widgets] skipped bundle "${name}": ${describeThrown(err)}`);
31875
31883
  }
31876
31884
  }
31877
31885
  return out;
31878
31886
  }
31879
31887
  async function handleWidgetList(res, dir) {
31880
- sendJson(res, 200, { widgets: await scanWidgets(dir) });
31888
+ try {
31889
+ sendJson(res, 200, { widgets: await scanWidgets(dir) });
31890
+ } catch (err) {
31891
+ if (!(err instanceof WidgetRealmUnavailableError)) throw err;
31892
+ console.error(`[widgets] ${err.message}`);
31893
+ sendJson(res, 503, { error: "widget_realm_unavailable", message: err.message });
31894
+ }
31881
31895
  }
31882
31896
  async function buildSnapshotForId(widgetId, dir) {
31883
31897
  const override = widgetOverrides.get(widgetId);
31884
31898
  if (override) return { ok: true, snapshot: override };
31885
- const entry = (await scanWidgets(dir)).find((w) => w.widgetId === widgetId);
31899
+ let entry;
31900
+ try {
31901
+ entry = (await scanWidgets(dir)).find((w) => w.widgetId === widgetId);
31902
+ } catch (err) {
31903
+ if (!(err instanceof WidgetRealmUnavailableError)) throw err;
31904
+ console.error(`[widgets] ${err.message}`);
31905
+ return { ok: false, status: 503, error: "widget_realm_unavailable", message: err.message };
31906
+ }
31886
31907
  if (!entry) return { ok: false, status: 404, error: "widget_not_found" };
31887
31908
  const loaded = await readBundleFile(entry.bundle, dir);
31888
31909
  if (!loaded) return { ok: false, status: 404, error: "widget_not_found" };
@@ -54301,7 +54322,7 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
54301
54322
  himi deeplinks --app <app> --release <id>
54302
54323
  nav map + web/player deeplinks for a release
54303
54324
  himi logs --app <app> [--release <id>] [--screen <id>] [--level <l>] [--follow] worker console read-back
54304
- himi promote --app <app> --release <id> [--force] flip a draft LIVE (then ring-promote via himi-serve)
54325
+ himi promote --app <app> --release <id> [--ring <r>] [--percent 0-100] [--force] flip a draft LIVE; --ring also points a rollout ring at it
54305
54326
  himi patch screen <id> --app <a> --file <descriptor.json> [--bundle <sha256|none>]
54306
54327
  himi patch bundle --app <a> --file <bundle.js> (--for-screen <id>[,<id>] | --no-rebind)
54307
54328
  himi patch tokens --app <a> --file <tokens.json>
@@ -54601,7 +54622,7 @@ var COMMANDS = [
54601
54622
  { name: "diagnose", summary: "remote screen triage against a pushed release (GET /v1/preview/\u2026/diagnose) \u2014 bindings/schema/boot; exit 1 if ok:false", required: "<screenId> --app --release" },
54602
54623
  { name: "deeplinks", summary: "nav map + web/player deeplinks for a release (GET /v1/preview/\u2026/deeplinks)", required: "--app --release" },
54603
54624
  { name: "logs", summary: "worker console read-back (GET /v1/telemetry/logs); --follow tails via the seq cursor", required: "--app [--release] [--screen] [--level] [--follow]" },
54604
- { name: "promote", summary: "flip a stored draft LIVE (POST /v1/admin/release/activate); a release pushed past errors needs an explicit --force override", required: "--app --release [--force] (--control-token)" },
54625
+ { name: "promote", summary: "flip a stored draft LIVE (POST /v1/admin/release/activate); --ring also points a rollout ring at it in the same command (POST /v1/admin/promote), which ring-based clients need in order to resolve at all \u2014 without it they stay unresolved and the warning goes to stderr; --percent sets that ring's rollout share; a release pushed past errors needs an explicit --force override", required: "--app --release [--ring <ring>] [--percent 0-100] [--force] (--control-token)" },
54605
54626
  { name: "patch", summary: 'change ONE item of a published app \u2014 a screen descriptor, a Tier-5 bundle, tokens, or the app-config baseline \u2014 from a RAW FILE, with no content package and no build. The low-level door: it does not lint your package, because there is no package here; `himi push --only <selector>` is the same edit with the full build + lint gate and is what to reach for when the package is in hand. Derives a new release from the app\'s current LIVE release, or from `--from-release <id>`. An explicit `--draft` or `--live` is REQUIRED \u2014 a patch has no default blast radius \u2014 and `--notes` is required with `--live` on a screen or app-config patch, because off-repo `gitSha` is "dev" and the ledger notes are then the only durable record of bytes that correspond to no commit. A branch does not merge: activating one derived from an older release reverts every item the patch does not move, so that case is refused until `--accept-regressions`. A bundle patch must repoint a screen (`--for-screen`) or the bytes land under a manifest key nothing reads. It cannot ADD a screen (screens are git-only, RFC 0012), never claims a slug \u2014 the app must already be published \u2014 and carries no attestation, source archive, assets, icon or minBinaryVersion, each named in the output.', required: "screen <id> --file <descriptor.json> [--bundle <sha256|none>] | bundle --file <bundle.js> (--for-screen <id>[,<id>] | --no-rebind) | tokens --file <tokens.json> | app-config --file <app-config.json> ; (--draft|--live) --app <a> [--from-release <id>] [--accept-regressions] [--label <n>] [--notes <t>] [--force] [--target <t>] (--control-token)" },
54606
54627
  { name: "visibility", summary: "read (no value) or set whether the public registry (/v1/apps + the Himi Player picker) LISTS the app. `public` lists it for everyone; `unlisted` hides it from listings while direct reads by app id \u2014 deep links, previews \u2014 keep working. App from --app or the content dir. Setting it needs no republish.", required: "[public|unlisted] [--app <a>] [--content <dir>] (--control-token)" },
54607
54628
  { name: "dist", summary: "build an INSTALLABLE QA binary (signed IPA / APK) on the Wix Job Service Mac pool and return an install link \u2014 the serve plane brokers the job, the pool builds it. WAITS for the build by default (~4 min) and prints the install link; `--no-wait` returns a jobId immediately instead. Omitting `--release` builds whatever is LIVE for the app. A named draft is refused unless `--allow-draft` makes that exceptional build explicit. `status <jobId>` / `watch <jobId>` / `list --app <a>` / `cancel <jobId>` inspect and stop builds (`watch` reports up front whether the job mints its own signing identity or borrows one, so a doomed iOS build is cancellable in seconds rather than after the archive). Off-repo, name a target \u2014 `--target wix` for a self-serve Wix login or `--target bo` for the internal employee path (the in-repo default is a local dev plane that isn't reachable off-repo); self-serve callers may build their OWN apps under a tighter budget (2 in flight / 5 per 24h), while a manage-only owner-role credential is refused. `--shell` picks the native host to sign into (branded-lite | owner-lite | player | demo-app). `--signing` defaults to `wix-managed` \u2014 the Wix Apple account mints a per-app signing identity for the build, so the caller provisions no Apple credentials of their own; `--signing byo` (sign with your own Apple account) is reserved and 501s. `--android-signing` is the Android counterpart and a SEPARATE axis (a keystore, not an Apple account): `qa-keystore` (default \u2014 the repo's committed QA key, identical on every pool machine, so a build installs over the previous one), `wix-keystore` (the Wix key the pool machine carries; the build FAILS naming the paths searched if it carries none, rather than quietly signing with something else), or `debug` (the old per-machine key \u2014 QA must uninstall any previous build first). NOT the normal way to ship: screens, Tier-5 bundles, tokens and app-config all go over the air with `himi push` in seconds \u2014 a binary trip is only needed for a NATIVE delta (a new SDUI component, a new declared capability/entitlement, a new bundle id, or a minBinaryVersion floor bump). Refuses to build an app whose launcher icon is missing, a placeholder, or one a store would reject \u2014 the one thing a binary carries that no later OTA push can fix; `--force` builds anyway", required: "[--app <a>] [--platform ios,android] [--release <id>] [--allow-draft] [--target <t>] [--shell branded-lite|owner-lite|player|demo-app] [--signing wix-managed|byo] [--android-signing qa-keystore|wix-keystore|debug] [--borrow-ios-id <bundleId>] [--no-wait] [--force] | status <jobId> [--target <t>] | watch <jobId> [--target <t>] [--interval <ms>] | list --app <a> [--target <t>] | cancel <jobId> [--target <t>]" },
@@ -55581,7 +55602,10 @@ async function producedWidgetFamilies(dir) {
55581
55602
  let extracted;
55582
55603
  try {
55583
55604
  extracted = await widgetsModule.extractWidgets(js);
55584
- } catch {
55605
+ } catch (err) {
55606
+ if (err instanceof widgetsModule.WidgetRealmUnavailableError) {
55607
+ return { families: void 0, source: "producer-unavailable" };
55608
+ }
55585
55609
  continue;
55586
55610
  }
55587
55611
  for (const [widgetId, def] of Object.entries(extracted.widgets)) {
@@ -58539,18 +58563,86 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
58539
58563
  return 2;
58540
58564
  }
58541
58565
  const target = targetName();
58542
- const r = await client2().activateRelease(app, release, flags.force === true);
58566
+ const force = flags.force === true;
58567
+ const ringGiven = flags.ring !== void 0;
58568
+ const ring = str5(flags.ring);
58569
+ if (ringGiven && !ring) {
58570
+ io.error(JSON.stringify({ error: "--ring needs a ring name, e.g. --ring ga" }));
58571
+ return 2;
58572
+ }
58573
+ const percentGiven = flags.percent !== void 0;
58574
+ const percent = num(flags.percent);
58575
+ if (percentGiven && (percent === void 0 || percent < 0 || percent > 100)) {
58576
+ io.error(JSON.stringify({ error: `--percent must be 0..100, got ${String(flags.percent)}` }));
58577
+ return 2;
58578
+ }
58579
+ if (percentGiven && !ring) {
58580
+ io.error(JSON.stringify({ error: "--percent sets a ring's rollout share, so it needs --ring" }));
58581
+ return 2;
58582
+ }
58583
+ const r = await client2().activateRelease(app, release, force);
58584
+ if (!r.ok || !ring) {
58585
+ if (r.ok) {
58586
+ io.error(JSON.stringify({
58587
+ warning: "no rollout ring was promoted \u2014 ring-based clients stay unresolved",
58588
+ next: `himi promote --app ${app} --release ${release} --ring <ring>${force ? " --force" : ""} --target ${target}`
58589
+ }));
58590
+ }
58591
+ print({
58592
+ status: r.status,
58593
+ ...r.json ?? {},
58594
+ ...r.ok ? {
58595
+ ringPromoted: false,
58596
+ note: "activation updates the live release but does not select a rollout ring; ring-based clients remain unresolved until a ring is promoted",
58597
+ // No `nextAuth` here: this `next` is a plain `himi promote`, which resolves its own
58598
+ // credential. It used to name `$TOK` because the hint was a `himi-serve` command.
58599
+ next: `himi promote --app ${app} --release ${release} --ring <ring>${force ? " --force" : ""} --target ${target}`
58600
+ } : {}
58601
+ });
58602
+ return r.ok ? 0 : 1;
58603
+ }
58604
+ const flagsFor = (o = {}) => `${o.percent !== void 0 ? ` --percent ${o.percent}` : ""}${force ? " --force" : ""} --target ${target}`;
58605
+ if (!percentGiven) {
58606
+ io.error(JSON.stringify({
58607
+ warning: `no --percent given: an existing ring "${ring}" keeps its current share, and a NEW one is created at the plane's default (100% on the hosted plane)`,
58608
+ next: `himi promote --app ${app} --release ${release} --ring ${ring}${flagsFor({ percent: "<0-100>" })}`
58609
+ }));
58610
+ }
58611
+ let p;
58612
+ const ringOnlyRetry = `himi-serve release promote --app ${app} --ring ${ring} --release ${release}${percent !== void 0 ? ` --percent ${percent}` : ""}${force ? " --force" : ""} --target ${target} --control-token "$TOK"`;
58613
+ try {
58614
+ p = await client2().promote(app, ring, release, percent, force);
58615
+ } catch (e) {
58616
+ io.error(JSON.stringify({
58617
+ error: `the release was ACTIVATED, but the ring request did not complete: ${e.message}`,
58618
+ activated: true,
58619
+ ringPromoted: false,
58620
+ ring,
58621
+ next: ringOnlyRetry,
58622
+ nextAuth: `set TOK to a serve-control token for target ${target}`
58623
+ }));
58624
+ return 1;
58625
+ }
58543
58626
  print({
58544
- status: r.status,
58627
+ status: p.status,
58628
+ // BOTH responses: the activation's own fields (`visibility`, any warning it carries)
58629
+ // are part of what happened and were being dropped on this path.
58545
58630
  ...r.json ?? {},
58546
- ...r.ok ? {
58547
- ringPromoted: false,
58548
- note: "activation updates the live release but does not select a rollout ring; ring-based clients remain unresolved until a ring is promoted",
58549
- next: `himi-serve release promote --app ${app} --ring <ring> --release ${release}${flags.force === true ? " --force" : ""} --target ${target} --control-token "$TOK"`,
58631
+ ...p.json ?? {},
58632
+ activated: true,
58633
+ ringPromoted: p.ok,
58634
+ ring,
58635
+ // REQUESTED, not applied. The git-backed `applyPromote` takes no percent at all
58636
+ // (promote.ts), so echoing `percent` as fact would report a share that plane never
58637
+ // set. Only the hosted CloudStore path honours it.
58638
+ ...percent !== void 0 ? { percentRequested: percent } : {},
58639
+ ...p.ok ? {} : {
58640
+ note: `the release was ACTIVATED, but promoting ring "${ring}" failed \u2014 retry the RING half alone with \`himi-serve release promote\`, which does not re-activate`,
58641
+ next: ringOnlyRetry,
58550
58642
  nextAuth: `set TOK to a serve-control token for target ${target}`
58551
- } : {}
58643
+ }
58552
58644
  });
58553
- return r.ok ? 0 : 1;
58645
+ return p.ok ? 0 : 1;
58554
58646
  }
58555
58647
  // The RAW-FILE patch door. No content package, no build, no package lint — that is the whole
58556
58648
  // reason it exists, and the reason `himi push --only` is the one to reach for when a package
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.891.0",
3
+ "version": "0.893.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": [
@@ -64,5 +64,5 @@
64
64
  "artifactId": "himalaya-cli"
65
65
  }
66
66
  },
67
- "falconPackageHash": "1043e5fb989a028cf5dffffc6f8a9aef5487b1e6464b630da5270431"
67
+ "falconPackageHash": "f38e92c2fdf1c758a5d00cb4fa45c3ff0f9945647a1ef9932642c7f0"
68
68
  }