@wix/himalaya-cli 0.1251.0 → 0.1253.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 (24) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/wix-member-login.d.ts +6 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/base44/src/index.d.ts +3 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/sdk-types/src/capabilities.generated.d.ts +1 -1
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/state/src/index.d.ts +2 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/serve-runtime/src/wix-bridge.d.ts +144 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/serve-storage/src/sha256.d.ts +13 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/audio-record.mjs +1 -1
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/base44.mjs +4 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calendar.mjs +1 -1
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/calls.mjs +1 -1
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/contacts.mjs +1 -1
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/location.mjs +1 -1
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/motion.mjs +1 -1
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/photos.mjs +1 -1
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdk-types.mjs +1 -1
  16. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/tasks.mjs +1 -1
  17. package/dist/cli.mjs +542 -284
  18. package/dist/preview-assets.generated.bin +0 -0
  19. package/guides/base44-app-to-himi-app.md +30 -8
  20. package/guides/getting-started.md +8 -0
  21. package/guides/himi-skill.md +8 -0
  22. package/guides/sdk-reference.json +8 -2
  23. package/guides/skill.md +2 -1
  24. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -22821,7 +22821,7 @@ var init_capabilities_generated = __esm({
22821
22821
  CAPABILITY_REGISTRY = {
22822
22822
  "app_info": ["get"],
22823
22823
  "audio_record": ["cancel", "cancelSession", "getPermissionStatus", "getSession", "listSegments", "listSessions", "pause", "requestPermission", "resume", "start", "startSession", "stop", "stopSession", "subscribeLevel", "watchNext", "watchStart", "watchStop"],
22824
- "base44": ["authLoginViaEmailPassword", "authLoginViaOtp", "authLogout", "authMe", "authRegister", "authResetPassword", "authStatus", "authUpdateMe", "authVerifyOtp", "entitiesBulkCreate", "entitiesCreate", "entitiesDelete", "entitiesFilter", "entitiesGet", "entitiesList", "entitiesUpdate", "functionsInvoke"],
22824
+ "base44": ["authLoginViaEmailPassword", "authLoginViaOtp", "authLoginWithProvider", "authLogout", "authMe", "authRegister", "authResetPassword", "authStatus", "authUpdateMe", "authVerifyOtp", "entitiesBulkCreate", "entitiesCreate", "entitiesDelete", "entitiesFilter", "entitiesGet", "entitiesList", "entitiesUpdate", "functionsInvoke"],
22825
22825
  "biometrics": ["available", "confirm"],
22826
22826
  "browser_auth": ["start"],
22827
22827
  "calendar": ["createEvent", "deleteEvent", "getPermissionStatus", "listEvents", "requestPermission", "updateEvent"],
@@ -28920,6 +28920,269 @@ var init_derive_transport_capability = __esm({
28920
28920
  }
28921
28921
  });
28922
28922
 
28923
+ // ../../core/serve-runtime/src/wix-bridge.ts
28924
+ function isAllowedConsumerStreamTarget(raw) {
28925
+ let url;
28926
+ try {
28927
+ url = new URL(raw);
28928
+ } catch {
28929
+ return false;
28930
+ }
28931
+ if (url.protocol !== "https:" || url.port !== "" && url.port !== "443" || url.username || url.password) return false;
28932
+ if (url.hostname.toLowerCase() === "www.wixapis.com") return true;
28933
+ return url.hostname.toLowerCase() === "apps.wix.com" && url.pathname === "/_api/nano-brain/v1/llm-sdk/generate-text";
28934
+ }
28935
+ function absoluteUrlRejection(path2) {
28936
+ let host = "";
28937
+ try {
28938
+ host = new URL(path2).host;
28939
+ } catch {
28940
+ }
28941
+ return OWNER_SCOPED_WIX_HOST.test(host) ? "owner-only" : "open-proxy";
28942
+ }
28943
+ async function mintAnonymousVisitor(clientId, fetchImpl = fetch) {
28944
+ const res = await fetchImpl(`${WIX_API}/oauth2/token`, {
28945
+ method: "POST",
28946
+ headers: { "Content-Type": "application/json" },
28947
+ body: JSON.stringify({ clientId, grantType: "anonymous" })
28948
+ });
28949
+ const text8 = await res.text();
28950
+ let j2 = {};
28951
+ try {
28952
+ j2 = text8 ? JSON.parse(text8) : {};
28953
+ } catch {
28954
+ }
28955
+ if (res.status !== 200 || !j2.access_token) throw new Error(`visitor mint failed (${res.status})`);
28956
+ return j2.access_token;
28957
+ }
28958
+ async function wixVisitorCall(method, path2, body, token, fetchImpl = fetch, forwardedHeaders = {}) {
28959
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(path2)) throw new Error("absolute URLs are not allowed");
28960
+ const url = `${WIX_API}${path2.startsWith("/") ? path2 : `/${path2}`}`;
28961
+ const res = await fetchImpl(url, {
28962
+ method,
28963
+ // Same header shape the dev-server bridge has always sent (wixapis accepts the
28964
+ // raw access token as the Authorization value).
28965
+ headers: { ...forwardedHeaders, Authorization: token, ...body ? { "Content-Type": "application/json" } : {} },
28966
+ body: method === "GET" || method === "HEAD" ? void 0 : body
28967
+ });
28968
+ return { status: res.status, body: await res.text() };
28969
+ }
28970
+ function isChatWebTarget(path2) {
28971
+ if (!path2 || !/^https:\/\//i.test(path2)) return false;
28972
+ try {
28973
+ const url = new URL(path2);
28974
+ return url.host === "manage.wix.com" && url.pathname.startsWith("/_api/chat-web/");
28975
+ } catch {
28976
+ return false;
28977
+ }
28978
+ }
28979
+ function normalizeWixForwardedHeaders(input, path2) {
28980
+ const chatWeb = isChatWebTarget(path2);
28981
+ let value = input;
28982
+ if (typeof value === "string") {
28983
+ try {
28984
+ value = JSON.parse(value);
28985
+ } catch {
28986
+ return {};
28987
+ }
28988
+ }
28989
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
28990
+ const out2 = {};
28991
+ for (const [name, headerValue] of Object.entries(value)) {
28992
+ const lower = name.toLowerCase();
28993
+ const allowed2 = WIX_FORWARDABLE_HEADERS.has(lower) || chatWeb && lower === "chattoken";
28994
+ if (!/^[a-z0-9-]+$/i.test(name) || !allowed2 || typeof headerValue !== "string") continue;
28995
+ if (/[\r\n\0]/.test(headerValue)) continue;
28996
+ out2[name] = headerValue;
28997
+ }
28998
+ return out2;
28999
+ }
29000
+ function isSafeMsid(msid) {
29001
+ return /^[0-9a-fA-F-]{10,64}$/.test(msid);
29002
+ }
29003
+ function isOwnerSitesPath(path2) {
29004
+ return path2 === OWNER_SITES_QUERY_PATH || path2 === OWNER_SITES_COUNT_PATH;
29005
+ }
29006
+ async function wixOwnerSitesCall(path2, cookieHeader, body, fetchImpl = fetch) {
29007
+ if (!isOwnerSitesPath(path2)) throw new Error("not an owner-sites path");
29008
+ const xsrf = /(?:^|;\s*)XSRF-TOKEN=([^;]+)/.exec(cookieHeader)?.[1];
29009
+ const res = await fetchImpl(`${WIX_API}${path2}`, {
29010
+ method: "POST",
29011
+ headers: {
29012
+ Cookie: cookieHeader,
29013
+ "Content-Type": "application/json",
29014
+ Accept: "application/json",
29015
+ ...xsrf ? { "x-xsrf-token": xsrf } : {}
29016
+ },
29017
+ body: body ?? "{}"
29018
+ });
29019
+ return { status: res.status, body: await res.text() };
29020
+ }
29021
+ function wixClientIdOf(appConfigJson) {
29022
+ let cfg;
29023
+ try {
29024
+ cfg = JSON.parse(appConfigJson);
29025
+ } catch {
29026
+ return { error: "no_client_id" };
29027
+ }
29028
+ const providerId = cfg.authConfig?.providerId ?? "";
29029
+ if (!providerId.startsWith("wix")) return { error: "unsupported_auth" };
29030
+ const clientId = cfg.authConfig?.config?.clientId;
29031
+ if (typeof clientId !== "string" || !clientId) return { error: "no_client_id" };
29032
+ return { clientId };
29033
+ }
29034
+ var WIX_API, ONE_APP_SESSION, OWNER_SCOPED_WIX_HOST, WIX_FORWARDABLE_HEADERS, OWNER_REFRESH_MARGIN_MS, OWNER_RENEW_MIN_AGE_MS, OWNER_MINT_TIMEOUT_MS, OwnerInstanceCache, OWNER_SITES_QUERY_PATH, OWNER_SITES_COUNT_PATH, OwnerSitesCache;
29035
+ var init_wix_bridge = __esm({
29036
+ "../../core/serve-runtime/src/wix-bridge.ts"() {
29037
+ init_sha256();
29038
+ WIX_API = "https://www.wixapis.com";
29039
+ ONE_APP_SESSION = "https://apps.wix.com/_api/one-app-session-web/v3/businesses";
29040
+ OWNER_SCOPED_WIX_HOST = /(^|\.)wix\.com$/i;
29041
+ WIX_FORWARDABLE_HEADERS = /* @__PURE__ */ new Set([
29042
+ "x-wix-language",
29043
+ "x-wix-linguist",
29044
+ "wixlanguage",
29045
+ "accept-language",
29046
+ "x-wix-currency",
29047
+ "x-wix-consistent",
29048
+ "x-wix-client-api-revision",
29049
+ "x-wix-site-revision",
29050
+ "x-wix-bi-gateway",
29051
+ "x-wix-brand",
29052
+ "consent-policy",
29053
+ "commonconfig",
29054
+ "x-wix-client-artifact-id",
29055
+ "wix-site-id"
29056
+ ]);
29057
+ OWNER_REFRESH_MARGIN_MS = 6e4;
29058
+ OWNER_RENEW_MIN_AGE_MS = 5e3;
29059
+ OWNER_MINT_TIMEOUT_MS = 8e3;
29060
+ OwnerInstanceCache = class {
29061
+ constructor(fetchImpl = fetch, ttlMs = 5 * 6e4, mintTimeoutMs = OWNER_MINT_TIMEOUT_MS, elapsedClock = () => performance.now()) {
29062
+ this.fetchImpl = fetchImpl;
29063
+ this.ttlMs = ttlMs;
29064
+ this.mintTimeoutMs = mintTimeoutMs;
29065
+ this.elapsedClock = elapsedClock;
29066
+ }
29067
+ cache = /* @__PURE__ */ new Map();
29068
+ /** The one mint on its way per (business, session). Every caller, plain or renewing, joins it:
29069
+ * it started after whatever is cached was minted, so it is the fresh instance. */
29070
+ inflight = /* @__PURE__ */ new Map();
29071
+ /** Keyed on the SESSION (`wixSession2`), not the whole cookie header: manage.wix.com's jar churns
29072
+ * (analytics, experiments), and keying on all of it re-minted on nearly every call (#3044). A
29073
+ * header with no session cookie falls back to itself — callers refuse that case before here. */
29074
+ key(msid, cookieHeader) {
29075
+ const session = /(?:^|;\s*)wixSession2=([^;]*)/.exec(cookieHeader)?.[1] ?? cookieHeader;
29076
+ return sha256Hex(`${msid}
29077
+ ${session}`);
29078
+ }
29079
+ async instance(msid, cookieHeader, now = Date.now()) {
29080
+ return (await this.entry(msid, cookieHeader, now)).instance;
29081
+ }
29082
+ /** The instance and the time this cache stops trusting it (#3044: the web host caches until then).
29083
+ * `renew`: Wix refused the instance this cache handed out (the route's `renew=1`, #3140) — mint a
29084
+ * new one, unless the cached one is younger than OWNER_RENEW_MIN_AGE_MS or a mint is already on
29085
+ * its way, in which case that IS the new one. */
29086
+ async entry(msid, cookieHeader, now = Date.now(), opts = {}) {
29087
+ const key2 = this.key(msid, cookieHeader);
29088
+ const pending = this.inflight.get(key2);
29089
+ if (pending) return { ...await pending };
29090
+ let hit = this.cache.get(key2);
29091
+ if (opts.renew && hit && now - hit.mintedAt >= OWNER_RENEW_MIN_AGE_MS) hit = void 0;
29092
+ if (hit && hit.expiresAt - OWNER_REFRESH_MARGIN_MS > now) return { instance: hit.instance, expiresAt: hit.expiresAt };
29093
+ const mint = this.mint(key2, msid, cookieHeader, now).finally(() => this.inflight.delete(key2));
29094
+ this.inflight.set(key2, mint);
29095
+ return { ...await mint };
29096
+ }
29097
+ async mint(key2, msid, cookieHeader, now) {
29098
+ const startedAt = this.elapsedClock();
29099
+ const abort = new AbortController();
29100
+ let timer;
29101
+ const timedOut = new Promise((_2, reject) => {
29102
+ timer = setTimeout(() => {
29103
+ abort.abort();
29104
+ reject(new Error(`owner mint timed out after ${this.mintTimeoutMs}ms`));
29105
+ }, this.mintTimeoutMs);
29106
+ });
29107
+ try {
29108
+ const res = await Promise.race([
29109
+ this.fetchImpl(`${ONE_APP_SESSION}/${msid}/signed-instance?signed_instance_type=User`, {
29110
+ method: "GET",
29111
+ headers: { Cookie: cookieHeader },
29112
+ signal: abort.signal
29113
+ }),
29114
+ timedOut
29115
+ ]);
29116
+ const text8 = await Promise.race([res.text(), timedOut]);
29117
+ let j2 = {};
29118
+ try {
29119
+ j2 = text8 ? JSON.parse(text8) : {};
29120
+ } catch {
29121
+ }
29122
+ if (res.status < 200 || res.status >= 300 || !j2.signedInstance)
29123
+ throw new Error(`owner mint failed (${res.status})`);
29124
+ const elapsed = Math.max(0, this.elapsedClock() - startedAt);
29125
+ const mintedAt = now + elapsed;
29126
+ const entry = { instance: j2.signedInstance, expiresAt: now + Math.floor(elapsed / 1e3) * 1e3 + this.ttlMs };
29127
+ this.cache.set(key2, { ...entry, mintedAt });
29128
+ return entry;
29129
+ } catch (e2) {
29130
+ this.cache.delete(key2);
29131
+ throw e2;
29132
+ } finally {
29133
+ clearTimeout(timer);
29134
+ }
29135
+ }
29136
+ /** Drop a cached instance so the next call re-mints. The envelope owner branch's own 401 retry;
29137
+ * the route's `renew=1` goes through `entry(…, { renew: true })` instead. */
29138
+ invalidate(msid, cookieHeader) {
29139
+ this.cache.delete(this.key(msid, cookieHeader));
29140
+ }
29141
+ };
29142
+ OWNER_SITES_QUERY_PATH = "/site-list/v2/sites/query";
29143
+ OWNER_SITES_COUNT_PATH = "/site-list/v2/sites/count";
29144
+ OwnerSitesCache = class {
29145
+ constructor(fetchImpl = fetch, ttlMs = 5 * 6e4) {
29146
+ this.fetchImpl = fetchImpl;
29147
+ this.ttlMs = ttlMs;
29148
+ }
29149
+ cache = /* @__PURE__ */ new Map();
29150
+ key(cookieHeader, limit) {
29151
+ return sha256Hex(`${limit}
29152
+ ${cookieHeader}`);
29153
+ }
29154
+ async sites(cookieHeader, limit, now = Date.now()) {
29155
+ const key2 = this.key(cookieHeader, limit);
29156
+ const hit = this.cache.get(key2);
29157
+ if (hit && hit.expiresAt > now) return hit.sites;
29158
+ const env = await wixOwnerSitesCall(
29159
+ OWNER_SITES_QUERY_PATH,
29160
+ cookieHeader,
29161
+ JSON.stringify({ query: { cursorPaging: { limit } } }),
29162
+ this.fetchImpl
29163
+ );
29164
+ if (env.status < 200 || env.status >= 300) throw new Error(`owner sites failed (${env.status})`);
29165
+ let parsed = {};
29166
+ try {
29167
+ parsed = env.body ? JSON.parse(env.body) : {};
29168
+ } catch {
29169
+ }
29170
+ const sites = (Array.isArray(parsed.sites) ? parsed.sites : []).filter((s2) => typeof s2?.id === "string" && !!s2.id).map((s2) => ({
29171
+ id: s2.id,
29172
+ displayName: typeof s2.displayName === "string" && s2.displayName ? s2.displayName : typeof s2.name === "string" && s2.name ? s2.name : s2.id,
29173
+ ...typeof s2.namespace === "string" && s2.namespace ? { namespace: s2.namespace } : {}
29174
+ }));
29175
+ this.cache.set(key2, { sites, expiresAt: now + this.ttlMs });
29176
+ return sites;
29177
+ }
29178
+ /** Drop a cached listing (e.g. after an upstream 401) so the next call re-fetches. */
29179
+ invalidate(cookieHeader, limit) {
29180
+ this.cache.delete(this.key(cookieHeader, limit));
29181
+ }
29182
+ };
29183
+ }
29184
+ });
29185
+
28923
29186
  // ../../core/dev-server/src/wix-hosts.ts
28924
29187
  function targetHost(raw) {
28925
29188
  try {
@@ -28950,11 +29213,13 @@ var init_wix_hosts = __esm({
28950
29213
  // ../../core/dev-server/src/wix-member-login.ts
28951
29214
  var wix_member_login_exports = {};
28952
29215
  __export(wix_member_login_exports, {
29216
+ isAllowedConsumerStreamTarget: () => isAllowedConsumerStreamTarget,
28953
29217
  memberDirectLogin: () => memberDirectLogin,
28954
29218
  memberDirectRegister: () => memberDirectRegister,
28955
29219
  mintVisitor: () => mintVisitor,
28956
29220
  readMemberId: () => readMemberId,
28957
- webWixCall: () => webWixCall
29221
+ webWixCall: () => webWixCall,
29222
+ webWixStream: () => webWixStream
28958
29223
  });
28959
29224
  import { createHash as createHash12, randomBytes as randomBytes3 } from "node:crypto";
28960
29225
  function b64url(buf) {
@@ -29087,6 +29352,25 @@ async function webWixCall(method, path2, body, token, cookie, forwardedHeaders =
29087
29352
  });
29088
29353
  return { status: res.status, body: await res.text() };
29089
29354
  }
29355
+ async function webWixStream(method, path2, body, token, cookie, forwardedHeaders = {}, signal) {
29356
+ const url = /^https?:\/\//i.test(path2) ? path2 : `${API}${path2.startsWith("/") ? path2 : "/" + path2}`;
29357
+ if (!isAllowedConsumerStreamTarget(url)) {
29358
+ throw new Error(`refusing to send Wix credentials to an unapproved consumer endpoint (${targetHost(url) || "no host"})`);
29359
+ }
29360
+ return fetch(url, {
29361
+ method,
29362
+ headers: {
29363
+ ...forwardedHeaders,
29364
+ Accept: "text/event-stream",
29365
+ Authorization: token,
29366
+ ...cookie ? { Cookie: cookie } : {},
29367
+ ...body ? { "Content-Type": "application/json" } : {}
29368
+ },
29369
+ body: method === "GET" || method === "HEAD" ? void 0 : body,
29370
+ redirect: "manual",
29371
+ signal
29372
+ });
29373
+ }
29090
29374
  async function readMemberId(memberToken) {
29091
29375
  try {
29092
29376
  const identity = await webWixCall("GET", "/members/v1/members/my", void 0, memberToken);
@@ -29100,7 +29384,9 @@ async function readMemberId(memberToken) {
29100
29384
  var API;
29101
29385
  var init_wix_member_login = __esm({
29102
29386
  "../../core/dev-server/src/wix-member-login.ts"() {
29387
+ init_wix_bridge();
29103
29388
  init_wix_hosts();
29389
+ init_wix_bridge();
29104
29390
  API = "https://www.wixapis.com";
29105
29391
  }
29106
29392
  });
@@ -29569,258 +29855,6 @@ var init_wix_gateway = __esm({
29569
29855
  }
29570
29856
  });
29571
29857
 
29572
- // ../../core/serve-runtime/src/wix-bridge.ts
29573
- function absoluteUrlRejection(path2) {
29574
- let host = "";
29575
- try {
29576
- host = new URL(path2).host;
29577
- } catch {
29578
- }
29579
- return OWNER_SCOPED_WIX_HOST.test(host) ? "owner-only" : "open-proxy";
29580
- }
29581
- async function mintAnonymousVisitor(clientId, fetchImpl = fetch) {
29582
- const res = await fetchImpl(`${WIX_API}/oauth2/token`, {
29583
- method: "POST",
29584
- headers: { "Content-Type": "application/json" },
29585
- body: JSON.stringify({ clientId, grantType: "anonymous" })
29586
- });
29587
- const text8 = await res.text();
29588
- let j2 = {};
29589
- try {
29590
- j2 = text8 ? JSON.parse(text8) : {};
29591
- } catch {
29592
- }
29593
- if (res.status !== 200 || !j2.access_token) throw new Error(`visitor mint failed (${res.status})`);
29594
- return j2.access_token;
29595
- }
29596
- async function wixVisitorCall(method, path2, body, token, fetchImpl = fetch, forwardedHeaders = {}) {
29597
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(path2)) throw new Error("absolute URLs are not allowed");
29598
- const url = `${WIX_API}${path2.startsWith("/") ? path2 : `/${path2}`}`;
29599
- const res = await fetchImpl(url, {
29600
- method,
29601
- // Same header shape the dev-server bridge has always sent (wixapis accepts the
29602
- // raw access token as the Authorization value).
29603
- headers: { ...forwardedHeaders, Authorization: token, ...body ? { "Content-Type": "application/json" } : {} },
29604
- body: method === "GET" || method === "HEAD" ? void 0 : body
29605
- });
29606
- return { status: res.status, body: await res.text() };
29607
- }
29608
- function isChatWebTarget(path2) {
29609
- if (!path2 || !/^https:\/\//i.test(path2)) return false;
29610
- try {
29611
- const url = new URL(path2);
29612
- return url.host === "manage.wix.com" && url.pathname.startsWith("/_api/chat-web/");
29613
- } catch {
29614
- return false;
29615
- }
29616
- }
29617
- function normalizeWixForwardedHeaders(input, path2) {
29618
- const chatWeb = isChatWebTarget(path2);
29619
- let value = input;
29620
- if (typeof value === "string") {
29621
- try {
29622
- value = JSON.parse(value);
29623
- } catch {
29624
- return {};
29625
- }
29626
- }
29627
- if (!value || typeof value !== "object" || Array.isArray(value)) return {};
29628
- const out2 = {};
29629
- for (const [name, headerValue] of Object.entries(value)) {
29630
- const lower = name.toLowerCase();
29631
- const allowed2 = WIX_FORWARDABLE_HEADERS.has(lower) || chatWeb && lower === "chattoken";
29632
- if (!/^[a-z0-9-]+$/i.test(name) || !allowed2 || typeof headerValue !== "string") continue;
29633
- if (/[\r\n\0]/.test(headerValue)) continue;
29634
- out2[name] = headerValue;
29635
- }
29636
- return out2;
29637
- }
29638
- function isSafeMsid(msid) {
29639
- return /^[0-9a-fA-F-]{10,64}$/.test(msid);
29640
- }
29641
- function isOwnerSitesPath(path2) {
29642
- return path2 === OWNER_SITES_QUERY_PATH || path2 === OWNER_SITES_COUNT_PATH;
29643
- }
29644
- async function wixOwnerSitesCall(path2, cookieHeader, body, fetchImpl = fetch) {
29645
- if (!isOwnerSitesPath(path2)) throw new Error("not an owner-sites path");
29646
- const xsrf = /(?:^|;\s*)XSRF-TOKEN=([^;]+)/.exec(cookieHeader)?.[1];
29647
- const res = await fetchImpl(`${WIX_API}${path2}`, {
29648
- method: "POST",
29649
- headers: {
29650
- Cookie: cookieHeader,
29651
- "Content-Type": "application/json",
29652
- Accept: "application/json",
29653
- ...xsrf ? { "x-xsrf-token": xsrf } : {}
29654
- },
29655
- body: body ?? "{}"
29656
- });
29657
- return { status: res.status, body: await res.text() };
29658
- }
29659
- function wixClientIdOf(appConfigJson) {
29660
- let cfg;
29661
- try {
29662
- cfg = JSON.parse(appConfigJson);
29663
- } catch {
29664
- return { error: "no_client_id" };
29665
- }
29666
- const providerId = cfg.authConfig?.providerId ?? "";
29667
- if (!providerId.startsWith("wix")) return { error: "unsupported_auth" };
29668
- const clientId = cfg.authConfig?.config?.clientId;
29669
- if (typeof clientId !== "string" || !clientId) return { error: "no_client_id" };
29670
- return { clientId };
29671
- }
29672
- var WIX_API, ONE_APP_SESSION, OWNER_SCOPED_WIX_HOST, WIX_FORWARDABLE_HEADERS, OWNER_REFRESH_MARGIN_MS, OWNER_RENEW_MIN_AGE_MS, OWNER_MINT_TIMEOUT_MS, OwnerInstanceCache, OWNER_SITES_QUERY_PATH, OWNER_SITES_COUNT_PATH, OwnerSitesCache;
29673
- var init_wix_bridge = __esm({
29674
- "../../core/serve-runtime/src/wix-bridge.ts"() {
29675
- init_sha256();
29676
- WIX_API = "https://www.wixapis.com";
29677
- ONE_APP_SESSION = "https://apps.wix.com/_api/one-app-session-web/v3/businesses";
29678
- OWNER_SCOPED_WIX_HOST = /(^|\.)wix\.com$/i;
29679
- WIX_FORWARDABLE_HEADERS = /* @__PURE__ */ new Set([
29680
- "x-wix-language",
29681
- "x-wix-linguist",
29682
- "wixlanguage",
29683
- "accept-language",
29684
- "x-wix-currency",
29685
- "x-wix-consistent",
29686
- "x-wix-client-api-revision",
29687
- "x-wix-site-revision",
29688
- "x-wix-bi-gateway",
29689
- "x-wix-brand",
29690
- "consent-policy",
29691
- "commonconfig",
29692
- "x-wix-client-artifact-id",
29693
- "wix-site-id"
29694
- ]);
29695
- OWNER_REFRESH_MARGIN_MS = 6e4;
29696
- OWNER_RENEW_MIN_AGE_MS = 5e3;
29697
- OWNER_MINT_TIMEOUT_MS = 8e3;
29698
- OwnerInstanceCache = class {
29699
- constructor(fetchImpl = fetch, ttlMs = 5 * 6e4, mintTimeoutMs = OWNER_MINT_TIMEOUT_MS, elapsedClock = () => performance.now()) {
29700
- this.fetchImpl = fetchImpl;
29701
- this.ttlMs = ttlMs;
29702
- this.mintTimeoutMs = mintTimeoutMs;
29703
- this.elapsedClock = elapsedClock;
29704
- }
29705
- cache = /* @__PURE__ */ new Map();
29706
- /** The one mint on its way per (business, session). Every caller, plain or renewing, joins it:
29707
- * it started after whatever is cached was minted, so it is the fresh instance. */
29708
- inflight = /* @__PURE__ */ new Map();
29709
- /** Keyed on the SESSION (`wixSession2`), not the whole cookie header: manage.wix.com's jar churns
29710
- * (analytics, experiments), and keying on all of it re-minted on nearly every call (#3044). A
29711
- * header with no session cookie falls back to itself — callers refuse that case before here. */
29712
- key(msid, cookieHeader) {
29713
- const session = /(?:^|;\s*)wixSession2=([^;]*)/.exec(cookieHeader)?.[1] ?? cookieHeader;
29714
- return sha256Hex(`${msid}
29715
- ${session}`);
29716
- }
29717
- async instance(msid, cookieHeader, now = Date.now()) {
29718
- return (await this.entry(msid, cookieHeader, now)).instance;
29719
- }
29720
- /** The instance and the time this cache stops trusting it (#3044: the web host caches until then).
29721
- * `renew`: Wix refused the instance this cache handed out (the route's `renew=1`, #3140) — mint a
29722
- * new one, unless the cached one is younger than OWNER_RENEW_MIN_AGE_MS or a mint is already on
29723
- * its way, in which case that IS the new one. */
29724
- async entry(msid, cookieHeader, now = Date.now(), opts = {}) {
29725
- const key2 = this.key(msid, cookieHeader);
29726
- const pending = this.inflight.get(key2);
29727
- if (pending) return { ...await pending };
29728
- let hit = this.cache.get(key2);
29729
- if (opts.renew && hit && now - hit.mintedAt >= OWNER_RENEW_MIN_AGE_MS) hit = void 0;
29730
- if (hit && hit.expiresAt - OWNER_REFRESH_MARGIN_MS > now) return { instance: hit.instance, expiresAt: hit.expiresAt };
29731
- const mint = this.mint(key2, msid, cookieHeader, now).finally(() => this.inflight.delete(key2));
29732
- this.inflight.set(key2, mint);
29733
- return { ...await mint };
29734
- }
29735
- async mint(key2, msid, cookieHeader, now) {
29736
- const startedAt = this.elapsedClock();
29737
- const abort = new AbortController();
29738
- let timer;
29739
- const timedOut = new Promise((_2, reject) => {
29740
- timer = setTimeout(() => {
29741
- abort.abort();
29742
- reject(new Error(`owner mint timed out after ${this.mintTimeoutMs}ms`));
29743
- }, this.mintTimeoutMs);
29744
- });
29745
- try {
29746
- const res = await Promise.race([
29747
- this.fetchImpl(`${ONE_APP_SESSION}/${msid}/signed-instance?signed_instance_type=User`, {
29748
- method: "GET",
29749
- headers: { Cookie: cookieHeader },
29750
- signal: abort.signal
29751
- }),
29752
- timedOut
29753
- ]);
29754
- const text8 = await Promise.race([res.text(), timedOut]);
29755
- let j2 = {};
29756
- try {
29757
- j2 = text8 ? JSON.parse(text8) : {};
29758
- } catch {
29759
- }
29760
- if (res.status < 200 || res.status >= 300 || !j2.signedInstance)
29761
- throw new Error(`owner mint failed (${res.status})`);
29762
- const elapsed = Math.max(0, this.elapsedClock() - startedAt);
29763
- const mintedAt = now + elapsed;
29764
- const entry = { instance: j2.signedInstance, expiresAt: now + Math.floor(elapsed / 1e3) * 1e3 + this.ttlMs };
29765
- this.cache.set(key2, { ...entry, mintedAt });
29766
- return entry;
29767
- } catch (e2) {
29768
- this.cache.delete(key2);
29769
- throw e2;
29770
- } finally {
29771
- clearTimeout(timer);
29772
- }
29773
- }
29774
- /** Drop a cached instance so the next call re-mints. The envelope owner branch's own 401 retry;
29775
- * the route's `renew=1` goes through `entry(…, { renew: true })` instead. */
29776
- invalidate(msid, cookieHeader) {
29777
- this.cache.delete(this.key(msid, cookieHeader));
29778
- }
29779
- };
29780
- OWNER_SITES_QUERY_PATH = "/site-list/v2/sites/query";
29781
- OWNER_SITES_COUNT_PATH = "/site-list/v2/sites/count";
29782
- OwnerSitesCache = class {
29783
- constructor(fetchImpl = fetch, ttlMs = 5 * 6e4) {
29784
- this.fetchImpl = fetchImpl;
29785
- this.ttlMs = ttlMs;
29786
- }
29787
- cache = /* @__PURE__ */ new Map();
29788
- key(cookieHeader, limit) {
29789
- return sha256Hex(`${limit}
29790
- ${cookieHeader}`);
29791
- }
29792
- async sites(cookieHeader, limit, now = Date.now()) {
29793
- const key2 = this.key(cookieHeader, limit);
29794
- const hit = this.cache.get(key2);
29795
- if (hit && hit.expiresAt > now) return hit.sites;
29796
- const env = await wixOwnerSitesCall(
29797
- OWNER_SITES_QUERY_PATH,
29798
- cookieHeader,
29799
- JSON.stringify({ query: { cursorPaging: { limit } } }),
29800
- this.fetchImpl
29801
- );
29802
- if (env.status < 200 || env.status >= 300) throw new Error(`owner sites failed (${env.status})`);
29803
- let parsed = {};
29804
- try {
29805
- parsed = env.body ? JSON.parse(env.body) : {};
29806
- } catch {
29807
- }
29808
- const sites = (Array.isArray(parsed.sites) ? parsed.sites : []).filter((s2) => typeof s2?.id === "string" && !!s2.id).map((s2) => ({
29809
- id: s2.id,
29810
- displayName: typeof s2.displayName === "string" && s2.displayName ? s2.displayName : typeof s2.name === "string" && s2.name ? s2.name : s2.id,
29811
- ...typeof s2.namespace === "string" && s2.namespace ? { namespace: s2.namespace } : {}
29812
- }));
29813
- this.cache.set(key2, { sites, expiresAt: now + this.ttlMs });
29814
- return sites;
29815
- }
29816
- /** Drop a cached listing (e.g. after an upstream 401) so the next call re-fetches. */
29817
- invalidate(cookieHeader, limit) {
29818
- this.cache.delete(this.key(cookieHeader, limit));
29819
- }
29820
- };
29821
- }
29822
- });
29823
-
29824
29858
  // ../../core/serve-runtime/src/flag-assignments.ts
29825
29859
  function bad(error) {
29826
29860
  return new Response(JSON.stringify({ error }), { status: 400, headers: { "content-type": JSON_CT, "cache-control": "no-store" } });
@@ -32482,6 +32516,8 @@ function createRicosHostSession(o2) {
32482
32516
  let seq = 0;
32483
32517
  const newId = o2.newId ?? (() => `rq${Date.now().toString(36)}${++seq}`);
32484
32518
  const state = { ready: false, unavailable: null, dirty: false, toolbar: EMPTY_TOOLBAR, focused: false };
32519
+ let locale = o2.locale;
32520
+ let rtl = o2.rtl;
32485
32521
  let seed2 = "";
32486
32522
  let committed = o2.seedMode === "none";
32487
32523
  let initSent = false, awaitingBaseline = false, disposed = false;
@@ -32513,8 +32549,8 @@ function createRicosHostSession(o2) {
32513
32549
  type: "init",
32514
32550
  contentJson: seed2,
32515
32551
  ...o2.placeholder ? { placeholder: o2.placeholder } : {},
32516
- ...o2.locale ? { locale: o2.locale } : {},
32517
- ...o2.rtl !== void 0 ? { rtl: o2.rtl } : {},
32552
+ ...locale ? { locale } : {},
32553
+ ...rtl !== void 0 ? { rtl } : {},
32518
32554
  ...o2.palette ? { palette: o2.palette } : {}
32519
32555
  });
32520
32556
  };
@@ -32607,6 +32643,13 @@ function createRicosHostSession(o2) {
32607
32643
  else sendInit();
32608
32644
  }
32609
32645
  },
32646
+ updateLocale(nextLocale, nextRtl) {
32647
+ if (locale === nextLocale && rtl === nextRtl) return;
32648
+ locale = nextLocale;
32649
+ rtl = nextRtl;
32650
+ if (!initSent || state.unavailable || disposed) return;
32651
+ o2.send({ v: 1, type: "updateLocale", locale: nextLocale ?? "en", rtl: nextRtl ?? false });
32652
+ },
32610
32653
  currentContent() {
32611
32654
  return !state.unavailable && !disposed && writes() ? lastContent : null;
32612
32655
  },
@@ -32983,6 +33026,9 @@ function RichContentEditorView({ props, ctx, wrap, nodeId, renderChild, ...dom }
32983
33026
  sessionRef.current = null;
32984
33027
  };
32985
33028
  }, [live3, src]);
33029
+ React3.useEffect(() => {
33030
+ sessionRef.current?.updateLocale(ctx.localeTag ?? "en", ctx.isRTL ?? document.documentElement.dir === "rtl");
33031
+ }, [ctx.localeTag, ctx.isRTL]);
32986
33032
  React3.useEffect(() => {
32987
33033
  sessionRef.current?.setSeed(seedMode === "literal" ? props.documentJson : seedMode === "binding" ? boundSeed : "");
32988
33034
  }, [seedMode, props.documentJson, boundSeed, src]);
@@ -125503,6 +125549,155 @@ async function handleWebWix(req, res, state) {
125503
125549
  res.setHeader("access-control-allow-origin", "*");
125504
125550
  res.end(JSON.stringify(obj3));
125505
125551
  };
125552
+ if (path2 === "/v1/__webwix/stream" && req.method === "POST") {
125553
+ const b2 = await readJson8();
125554
+ const method = String(b2.method ?? "GET").toUpperCase();
125555
+ const requestedUrl = String(b2.url ?? "");
125556
+ const body = b2.body === void 0 ? void 0 : typeof b2.body === "string" ? b2.body : JSON.stringify(b2.body);
125557
+ const forwardedHeaders = normalizeWixForwardedHeaders(b2.headers, requestedUrl);
125558
+ const target = (() => {
125559
+ try {
125560
+ return new URL(requestedUrl, "https://www.wixapis.com");
125561
+ } catch {
125562
+ return void 0;
125563
+ }
125564
+ })();
125565
+ const started = Date.now();
125566
+ const recordStream = (source, status, mockRuleId, headers2) => {
125567
+ const redactBodies = source === "proxy" || mockRuleId === UNMATCHED_FAIL_CLOSED_ID;
125568
+ recordWixTraffic(state.wix, state.appName, state.adminUrl, {
125569
+ ts: started,
125570
+ source,
125571
+ capability: "wix_api.web",
125572
+ host: target?.host ?? "unknown",
125573
+ method,
125574
+ // Traffic is retained and broadcast to the devtools/admin log. Keep only
125575
+ // the origin and path: rejected URLs may contain userinfo, and valid Wix
125576
+ // endpoints can carry credential-like query parameters.
125577
+ url: target ? `${target.origin}${target.pathname}` : "[unparseable URL]",
125578
+ path: target?.pathname ?? "[unparseable URL]",
125579
+ status,
125580
+ durationMs: Date.now() - started,
125581
+ reqBody: redactBodies ? void 0 : body,
125582
+ respHeaders: redactBodies ? void 0 : headers2,
125583
+ mockRuleId
125584
+ });
125585
+ };
125586
+ if (!target || !isAllowedConsumerStreamTarget(target.toString()) || !["GET", "POST"].includes(method)) {
125587
+ recordStream("proxy", 400);
125588
+ res.statusCode = 400;
125589
+ res.setHeader("content-type", "application/json; charset=utf-8");
125590
+ res.end(JSON.stringify({ error: "webwix_stream_bad_request" }));
125591
+ return;
125592
+ }
125593
+ if (state.wix.mode === "mock") {
125594
+ const matchCtx = { host: target.host, query: Object.fromEntries(target.searchParams.entries()), headers: forwardedHeaders, body };
125595
+ let match = matchRuleEx(state.wix.rules, method, target.pathname, matchCtx);
125596
+ if (match && (match.rule.kind ?? "mock") !== "mock") {
125597
+ console.error(`[himalaya-dev] net-mocks: rule "${match.rule.id}" is kind:"${match.rule.kind}", which the web bridge cannot apply \u2014 ignoring it for ${method} ${target.pathname}`);
125598
+ match = matchRuleEx(state.wix.rules.filter((r2) => (r2.kind ?? "mock") === "mock"), method, target.pathname, matchCtx);
125599
+ }
125600
+ if (match) {
125601
+ const mocked = match.rule.response;
125602
+ if (mocked.delayMs) await new Promise((resolve57) => setTimeout(resolve57, mocked.delayMs));
125603
+ const payload = mocked.template ? renderTemplate(mocked.body, match.captures) : mocked.body;
125604
+ recordStream("mock", mocked.status, match.rule.id, mocked.headers);
125605
+ res.statusCode = mocked.status;
125606
+ res.setHeader("content-type", mocked.headers?.["content-type"] ?? "text/event-stream; charset=utf-8");
125607
+ res.setHeader("cache-control", "no-cache");
125608
+ res.end(payload ?? "");
125609
+ return;
125610
+ }
125611
+ if (failsClosed(state.wix)) {
125612
+ const refusal = hermeticRefusal(method, target.pathname);
125613
+ recordStream("mock", refusal.status, UNMATCHED_FAIL_CLOSED_ID, refusal.headers);
125614
+ res.statusCode = refusal.status;
125615
+ res.setHeader("content-type", "application/json; charset=utf-8");
125616
+ res.end(refusal.body);
125617
+ return;
125618
+ }
125619
+ }
125620
+ const abort = new AbortController();
125621
+ const onResponseClose = () => {
125622
+ if (!res.writableFinished) abort.abort();
125623
+ };
125624
+ res.once("close", onResponseClose);
125625
+ try {
125626
+ if (!session.visitorToken && !session.memberToken) {
125627
+ try {
125628
+ session.visitorToken = await mintVisitor(resolveWixClientId(state.app) ?? WEBWIX_CLIENT_ID);
125629
+ } catch {
125630
+ }
125631
+ }
125632
+ const isMember = !!session.memberToken;
125633
+ let token = session.memberToken ?? session.visitorToken ?? "";
125634
+ let upstream = await webWixStream(method, target.toString(), body, token, void 0, forwardedHeaders, abort.signal);
125635
+ if ((upstream.status === 401 || upstream.status === 403) && !isMember && !abort.signal.aborted) {
125636
+ try {
125637
+ await upstream.body?.cancel();
125638
+ } catch {
125639
+ }
125640
+ try {
125641
+ token = session.visitorToken = await mintVisitor(resolveWixClientId(state.app) ?? WEBWIX_CLIENT_ID);
125642
+ upstream = await webWixStream(method, target.toString(), body, token, void 0, forwardedHeaders, abort.signal);
125643
+ } catch {
125644
+ }
125645
+ }
125646
+ res.statusCode = upstream.status;
125647
+ for (const name of ["content-type", "cache-control", "retry-after"]) {
125648
+ const value = upstream.headers.get(name);
125649
+ if (value) res.setHeader(name, value);
125650
+ }
125651
+ res.setHeader("access-control-allow-origin", "*");
125652
+ res.setHeader("cache-control", res.getHeader("cache-control") ?? "no-cache");
125653
+ recordStream("proxy", upstream.status);
125654
+ if (!upstream.body) {
125655
+ res.end();
125656
+ return;
125657
+ }
125658
+ const reader = upstream.body.getReader();
125659
+ for (; ; ) {
125660
+ if (abort.signal.aborted) {
125661
+ await reader.cancel();
125662
+ break;
125663
+ }
125664
+ const chunk = await reader.read();
125665
+ if (chunk.done) break;
125666
+ if (chunk.value && !res.write(chunk.value)) {
125667
+ await new Promise((resolve57) => {
125668
+ let settled = false;
125669
+ const finish2 = () => {
125670
+ if (settled) return;
125671
+ settled = true;
125672
+ res.off("drain", finish2);
125673
+ res.off("close", finish2);
125674
+ resolve57();
125675
+ };
125676
+ if (res.destroyed || !res.writable) finish2();
125677
+ else {
125678
+ res.once("drain", finish2);
125679
+ res.once("close", finish2);
125680
+ }
125681
+ });
125682
+ }
125683
+ }
125684
+ if (!res.destroyed) res.end();
125685
+ } catch {
125686
+ if (!abort.signal.aborted) {
125687
+ recordStream("proxy", 502);
125688
+ if (!res.headersSent) {
125689
+ res.statusCode = 502;
125690
+ res.setHeader("content-type", "application/json; charset=utf-8");
125691
+ res.end(JSON.stringify({ error: "webwix_stream_failed" }));
125692
+ } else if (!res.destroyed) {
125693
+ res.destroy(new Error("webwix_stream_failed"));
125694
+ }
125695
+ }
125696
+ } finally {
125697
+ res.off("close", onResponseClose);
125698
+ }
125699
+ return;
125700
+ }
125506
125701
  if (path2 === "/v1/__webwix/login" && req.method === "POST") {
125507
125702
  const b2 = await readJson8();
125508
125703
  try {
@@ -142401,7 +142596,7 @@ function json4(status, body, headers2 = {}) {
142401
142596
  return new Response(JSON.stringify(body), { status, headers: { "content-type": JSON_CT2, ...headers2 } });
142402
142597
  }
142403
142598
  function isCookieAuthorizedPath(path2) {
142404
- return path2 === "/v1/__webwix/call" || path2 === "/v1/owner" || path2.startsWith("/v1/owner/") || path2 === "/v1/__webwix/identity" || path2 === "/v1/__webwix/login" || path2 === "/v1/__webwix/register" || path2 === "/v1/__webwix/logout" || path2 === MEMBER_CALLBACK_PATH;
142599
+ return path2 === "/v1/__webwix/call" || path2 === "/v1/__webwix/stream" || path2 === "/v1/owner" || path2.startsWith("/v1/owner/") || path2 === "/v1/__webwix/identity" || path2 === "/v1/__webwix/login" || path2 === "/v1/__webwix/register" || path2 === "/v1/__webwix/logout" || path2 === MEMBER_CALLBACK_PATH;
142405
142600
  }
142406
142601
  function webwixCallLog(served, status) {
142407
142602
  const directRung = served.rung === "visitor" || served.rung === "owner";
@@ -142767,6 +142962,48 @@ ${releaseId}`;
142767
142962
  }
142768
142963
  return json4(200, { authenticated: false, userId: null, email: null, webwixDirect }, { "cache-control": "no-store" });
142769
142964
  }
142965
+ if (request.method === "POST" && path2 === "/v1/__webwix/stream") {
142966
+ if (opts.webwixBridge === false) return json4(503, { error: "webwix_bridge_disabled" }, { "cache-control": "no-store" });
142967
+ const app2 = resolveApp(request, url);
142968
+ const hostApp = memberApp(request);
142969
+ if (!app2) return json4(400, { error: "app_required" }, { "cache-control": "no-store" });
142970
+ if (!hostApp || app2 !== hostApp) return json4(403, { error: "webwix_stream_host_mismatch" }, { "cache-control": "no-store" });
142971
+ const resolvedCfg = await appConfigFor(request, url, app2);
142972
+ if (!resolvedCfg.ok) return resolvedCfg.response;
142973
+ let call;
142974
+ try {
142975
+ call = await request.json();
142976
+ } catch {
142977
+ return json4(400, { error: "invalid_json" }, { "cache-control": "no-store" });
142978
+ }
142979
+ const method = String(call.method ?? "GET").toUpperCase();
142980
+ if (method !== "GET" && method !== "POST") return json4(400, { error: "invalid_stream_method" }, { "cache-control": "no-store" });
142981
+ const rawTarget = String(call.url ?? "");
142982
+ const target = /^https:\/\//i.test(rawTarget) ? rawTarget : `https://www.wixapis.com${rawTarget.startsWith("/") ? rawTarget : `/${rawTarget}`}`;
142983
+ if (!isAllowedConsumerStreamTarget(target)) return json4(400, { error: "invalid_stream_target" }, { "cache-control": "no-store" });
142984
+ const cfg = wixMemberAuthOf(utf8Decode(resolvedCfg.bytes), callbackUrlFor(url));
142985
+ if (!cfg.ok) return json4(501, { error: "webwix_visitor_direct_only", app: app2 }, { "cache-control": "no-store" });
142986
+ const store2 = memberSessions();
142987
+ const token = memberAuthOn() && store2 ? await memberTokenFor(store2, readSessionCookie(request.headers.get("cookie")), hostApp, cfg.config.clientId) : null;
142988
+ if (!token) return json4(501, { error: "webwix_visitor_direct_only", app: app2 }, { "cache-control": "no-store" });
142989
+ const headers2 = normalizeWixForwardedHeaders(call.headers);
142990
+ headers2.Accept = "text/event-stream";
142991
+ headers2.Authorization = token;
142992
+ const body = call.body === void 0 ? void 0 : typeof call.body === "string" ? call.body : JSON.stringify(call.body);
142993
+ if (body !== void 0) headers2["Content-Type"] ??= "application/json";
142994
+ let upstream;
142995
+ try {
142996
+ upstream = await webwixFetch(target, { method, headers: headers2, body: method === "GET" ? void 0 : body, redirect: "manual", signal: request.signal });
142997
+ } catch {
142998
+ return json4(502, { error: "webwix_stream_upstream_failed" }, { "cache-control": "no-store" });
142999
+ }
143000
+ const responseHeaders = new Headers({ "cache-control": "no-store, no-transform" });
143001
+ for (const name of ["content-type", "retry-after"]) {
143002
+ const value = upstream.headers.get(name);
143003
+ if (value) responseHeaders.set(name, value);
143004
+ }
143005
+ return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
143006
+ }
142770
143007
  if (request.method === "POST" && path2 === "/v1/__webwix/call") {
142771
143008
  const served = { rung: "none" };
142772
143009
  let status = 500;
@@ -143690,7 +143927,7 @@ var init_run_handler = __esm({
143690
143927
 
143691
143928
  // ../../core/serve-runtime/src/node-adapter.ts
143692
143929
  import { createServer as createServer6 } from "node:http";
143693
- async function toRequest(req) {
143930
+ async function toRequest(req, signal) {
143694
143931
  const method = (req.method ?? "GET").toUpperCase();
143695
143932
  const host = typeof req.headers.host === "string" && req.headers.host || "serve.local";
143696
143933
  const url = new URL(req.url ?? "/", `http://${host}`);
@@ -143706,7 +143943,7 @@ async function toRequest(req) {
143706
143943
  for await (const chunk of req) chunks.push(chunk);
143707
143944
  if (chunks.length) body = Buffer.concat(chunks);
143708
143945
  }
143709
- return new Request(url, { method, headers: headers2, body });
143946
+ return new Request(url, { method, headers: headers2, body, signal });
143710
143947
  }
143711
143948
  async function writeResponse(res, response) {
143712
143949
  res.statusCode = response.status;
@@ -143714,10 +143951,15 @@ async function writeResponse(res, response) {
143714
143951
  if (response.body && /^text\/event-stream\b/i.test(response.headers.get("content-type") ?? "")) {
143715
143952
  res.flushHeaders();
143716
143953
  const reader = response.body.getReader();
143717
- res.on("close", () => {
143718
- void reader.cancel().catch(() => {
143719
- });
143720
- });
143954
+ let clientDisconnected = false;
143955
+ const onClose = () => {
143956
+ if (!res.writableEnded) {
143957
+ clientDisconnected = true;
143958
+ void reader.cancel().catch(() => {
143959
+ });
143960
+ }
143961
+ };
143962
+ res.on("close", onClose);
143721
143963
  try {
143722
143964
  for (; ; ) {
143723
143965
  const { done, value } = await reader.read();
@@ -143725,7 +143967,10 @@ async function writeResponse(res, response) {
143725
143967
  res.write(value);
143726
143968
  }
143727
143969
  } catch {
143970
+ if (!clientDisconnected && !res.destroyed) res.destroy();
143971
+ return;
143728
143972
  }
143973
+ res.off("close", onClose);
143729
143974
  res.end();
143730
143975
  return;
143731
143976
  }
@@ -143734,10 +143979,23 @@ async function writeResponse(res, response) {
143734
143979
  }
143735
143980
  function nodeAdapter(handler) {
143736
143981
  return createServer6(async (req, res) => {
143982
+ const controller = new AbortController();
143983
+ const abortForDisconnect = () => {
143984
+ if (!res.writableEnded) controller.abort();
143985
+ };
143986
+ req.once("aborted", abortForDisconnect);
143987
+ res.once("close", abortForDisconnect);
143737
143988
  try {
143738
- const response = await handler(await toRequest(req));
143989
+ const request = await toRequest(req, controller.signal);
143990
+ const response = await handler(request);
143991
+ if (controller.signal.aborted || res.destroyed) {
143992
+ await response.body?.cancel().catch(() => {
143993
+ });
143994
+ return;
143995
+ }
143739
143996
  await writeResponse(res, response);
143740
143997
  } catch (err) {
143998
+ if (controller.signal.aborted || res.destroyed) return;
143741
143999
  if (!res.headersSent) res.writeHead(500, { "content-type": "application/json; charset=utf-8" });
143742
144000
  res.end(JSON.stringify({ error: "internal", message: err.message }));
143743
144001
  }
@@ -143817,23 +144075,23 @@ var init_preview_assets_generated = __esm({
143817
144075
  "../../packages/serve/src/preview-assets.generated.ts"() {
143818
144076
  init_preview_assets_format();
143819
144077
  PREVIEW_ASSET_MANIFEST = [
143820
- { key: "assets/index-B3gStBoX.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1194813, sha256: "a8361f44552b20fad081eb4707f42209ab09ea01748a7b4196fa1c75a99ed1f3" },
143821
- { key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset: 1194821, length: 1317034, sha256: "a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e" },
143822
- { key: "assets/pdfDocument-BvYBOu_9.js", contentType: "application/javascript; charset=utf-8", offset: 2511855, length: 489166, sha256: "af9c9fd16776cb30cb4ee0239446d24abd3014a29dd22149c4efe7e7b845c14e" },
143823
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 3001021, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
143824
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 3005631, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
143825
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 3494215, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
143826
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 3585139, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
143827
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 3676123, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
143828
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 3767103, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
143829
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 3857975, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
143830
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 3948955, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
143831
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 4057591, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
143832
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 4166675, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
143833
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 4275723, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
143834
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 4383423, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
143835
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 4471531, length: 4947, sha256: "9336643c44e9f5510418c1013227cd7253cb997f4f2070693b2e53b9ce4422d4" },
143836
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 4476478, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
144078
+ { key: "assets/index-C0x7URuL.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1199950, sha256: "4de9d34adcbb975055f0d4b1eb9d2c5ed0dfeaa5662ec185f03d27c531b55967" },
144079
+ { key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset: 1199958, length: 1317034, sha256: "a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e" },
144080
+ { key: "assets/pdfDocument-ISPThQWG.js", contentType: "application/javascript; charset=utf-8", offset: 2516992, length: 489166, sha256: "e451e1b12e3fa92e9f9bef57b112fdffdc2388fa88c3b2596262ba2b8f53ded3" },
144081
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 3006158, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
144082
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 3010768, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
144083
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 3499352, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
144084
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 3590276, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
144085
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 3681260, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
144086
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 3772240, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
144087
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 3863112, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
144088
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 3954092, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
144089
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 4062728, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
144090
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 4171812, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
144091
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 4280860, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
144092
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 4388560, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
144093
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 4476668, length: 4947, sha256: "705132e1df4e35dc5c74aff257c2791196c9d7aa43de5841b1abba997a645bd5" },
144094
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 4481615, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
143837
144095
  ];
143838
144096
  }
143839
144097
  });
@@ -162139,7 +162397,7 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
162139
162397
  carries no attestation, source archive, assets, icon or
162140
162398
  minBinaryVersion \u2014 all named in the output.
162141
162399
  himi dist [--app <a>] [--platform ios,android] [--release <id>] [--allow-draft] [--target <t>]
162142
- [--shell branded-lite|owner-lite|brandy|player|demo-app] [--signing wix-managed|byo]
162400
+ [--shell branded-lite|owner-lite|brandy|base44|player|demo-app] [--signing wix-managed|byo]
162143
162401
  [--android-signing qa-keystore|wix-keystore|debug]
162144
162402
  [--borrow-ios-id <bundleId>] [--no-wait] [--force] [--plan]
162145
162403
  build an INSTALLABLE QA binary (signed IPA / APK) on
@@ -162151,7 +162409,7 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
162151
162409
  the in-repo default is a local dev plane that isn't
162152
162410
  reachable off-repo.
162153
162411
  --shell picks the native host to sign into (branded-lite |
162154
- owner-lite | brandy | player | demo-app). branded-lite,
162412
+ owner-lite | brandy | base44 | player | demo-app). branded-lite,
162155
162413
  owner-lite, brandy and base44 ADD the release's declared
162156
162414
  capabilities to the binary; player and demo-app refuse a
162157
162415
  release declaring one they lack. --plan shows what a build
@@ -162529,7 +162787,7 @@ var COMMANDS2 = [
162529
162787
  { 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)" },
162530
162788
  { name: "flags", summary: "read and change the app's OPERATIONAL flags (RFC 0013) \u2014 `list` prints what the DEVICE door serves, `set <key> --definition` creates or replaces one, `ramp <key>` moves its rollout share or kills it, `rm <key>` deletes it and echoes the definition back so the removal is reversible. Every write is followed by a read of the same door the device uses, and the answer is reported as `servedNow`: flags are stored in the app's git-committed flags.json, so a control plane with no checkout accepts a write it will never serve.", required: "list|set|ramp|rm [<key>] [--definition <json|@file>] [--percent 0-100] [--enabled true|false] [--app <a>] (--control-token)" },
162531
162789
  { 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)" },
162532
- { 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 | brandy | player | demo-app) and defaults to the content package's himalaya.content.json `shell` (templates carry the one they were written for), else branded-lite. The per-app shells ADD the release's declared capabilities to the binary, while Himi Player and demo-app refuse a release declaring one they lack; `--plan` previews it without building; `brandy` is the site shell \u2014 the customer's own website as native-framed tabs. `--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 --site <id>` reuses an existing customer ad-hoc profile and certificate for iOS; requires an owned site and a completed personal signing binding. `--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|brandy|player|demo-app] [--signing wix-managed|byo] [--site <id>] [--android-signing qa-keystore|wix-keystore|debug] [--borrow-ios-id <bundleId>] [--no-wait] [--force] [--plan] | status <jobId> [--target <t>] | watch <jobId> [--target <t>] [--interval <ms>] | list --app <a> [--target <t>] | cancel <jobId> [--target <t>]" },
162790
+ { 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 | brandy | base44 | player | demo-app) and defaults to the content package's himalaya.content.json `shell` (templates carry the one they were written for), else branded-lite. The per-app shells ADD the release's declared capabilities to the binary, while Himi Player and demo-app refuse a release declaring one they lack; `--plan` previews it without building; `brandy` is the site shell \u2014 the customer's own website as native-framed tabs. `--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 --site <id>` reuses an existing customer ad-hoc profile and certificate for iOS; requires an owned site and a completed personal signing binding. `--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|brandy|base44|player|demo-app] [--signing wix-managed|byo] [--site <id>] [--android-signing qa-keystore|wix-keystore|debug] [--borrow-ios-id <bundleId>] [--no-wait] [--force] [--plan] | status <jobId> [--target <t>] | watch <jobId> [--target <t>] [--interval <ms>] | list --app <a> [--target <t>] | cancel <jobId> [--target <t>]" },
162533
162791
  { name: "testflight", summary: 'build, sign and upload an app to TestFlight (internal testers \u2014 no Beta App Review), then WAIT until App Store Connect finishes processing it. Sugar for `store upload --track testflight`. Prompts first: a build number is consumed permanently on a real Apple account and cannot be reused \u2014 `--yes` for CI, `--dry-run` to see the plan without touching anything. The wait is the real check, not a nicety: fastlane prints "Successfully uploaded" for a binary App Store Connect then rejects in processing, and a rejected binary leaves NO build record at all (the reason is emailed). Public App Store review is deliberately NOT automatable here \u2014 `--track appstore` is refused; use /submit-ios-app when you mean to go public', required: "<app> [--track testflight|internal] [--profile <name>] [--yes] [--dry-run] [--skip-build] [--no-wait] [--json]" },
162534
162792
  { name: "store", summary: "the App Store Connect group behind `testflight`. `preflight <app>` checks readiness offline (icon, entitlements, encryption flag, version, store.config ids, listing + review record); `upload <app>` is what `testflight` runs; `status <app>` reads live build + processing state. `--profile` selects WHICH App Store Connect account: an app built for a Wix customer ships under the CUSTOMER's Apple team (Apple guideline 4.2.6 forbids an app-generation service submitting on a client's behalf), declared per-app as ios.credentialProfile in store.config.yaml and resolved from ~/.himalaya/store-credentials/<name>.json \u2014 the .p8 itself never enters the repo. `iap draft|plan|apply|status` registers an app owner's digital products as in-app purchases (non-consumables, or consumable price tiers): draft reads the Wix catalog and writes the manifest, plan and status only read, apply is the one mutation boundary, and none of them builds, signs or needs a checkout (`himi docs iap-products`)", required: "preflight <app> [--profile <n>] | upload <app> [--track <t>] [--profile <n>] [--yes] [--dry-run] | status <app> [--profile <n>] | iap draft --site <msid> --profile <n> --apple-app <id> --bundle <id> --base-territory <t> --out <f> [--territories <a,b>] [--locale <l>] [--mode per-item|tiers] [--merge <f>] [--report <f>] [--json] | iap plan --manifest <f> --profile <n> --out <f> [--json] | iap apply --plan <f> --profile <n> --out <f> [--yes] [--limit <n>] [--json] | iap status --manifest <f> --profile <n> [--out <f>] [--json]" },
162535
162793
  { name: "guide", summary: "print the Himalaya overview + headless dev loop + available topics (offline)", required: "[--json]" },