@wix/himalaya-cli 0.1251.0 → 0.1252.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.
@@ -15,6 +15,12 @@ export declare function webWixCall(method: string, path: string, body: string |
15
15
  status: number;
16
16
  body: string;
17
17
  }>;
18
+ /** Open a Wix response without buffering it, for the authorized SSE bridge.
19
+ * Credentials stay server-side and the host lock is identical to webWixCall. */
20
+ export declare function webWixStream(method: string, path: string, body: string | undefined, token: string, cookie?: string, forwardedHeaders?: Record<string, string>, signal?: AbortSignal): Promise<Response>;
21
+ /** Keep the credential-bearing stream bridge on the same narrow endpoint policy as native
22
+ * Wix consumer auth. General member calls have broader Wix-host routing through webWixCall. */
23
+ export { isAllowedConsumerStreamTarget } from "../../serve-runtime/src/wix-bridge.js";
18
24
  /** The signed-in member's Wix id, or null when the profile read fails (the identity route then
19
25
  * falls back to the login email). The token belongs in `webWixCall`'s TOKEN slot: the identity
20
26
  * route used to pass it third, where it became the GET's ignored body, so the read went out as
@@ -38,6 +38,8 @@ export interface StreamDirective {
38
38
  url: string;
39
39
  headers?: Record<string, string>;
40
40
  body?: unknown;
41
+ /** Apply the host's Wix authorization to this request before opening the stream. */
42
+ authorize?: boolean;
41
43
  };
42
44
  /** Reducer action name dispatched once per parsed `data:` event. */
43
45
  onEvent: string;
@@ -0,0 +1,144 @@
1
+ /** The narrow Wix consumer endpoints allowed to receive credentials for SSE. */
2
+ export declare function isAllowedConsumerStreamTarget(raw: string): boolean;
3
+ /** Classify an absolute-URL rejection: an off-gateway Wix account host is a
4
+ * native-only endpoint (`owner-only`), anything else is an open-proxy attempt. */
5
+ export declare function absoluteUrlRejection(path: string): "owner-only" | "open-proxy";
6
+ /** The bridge envelope the web host's `wixCallOverBridge` expects on a 2xx. */
7
+ export interface WixBridgeEnvelope {
8
+ status: number;
9
+ body: string;
10
+ }
11
+ /** One anonymous visitor token for `clientId`. #3123 Stage B: the proxy no longer serves visitor
12
+ * calls (each browser mints its own, core/web-host/src/wix/directWixCall.ts), so nothing caches
13
+ * one per pod any more — the one server-side use left is the member-login redirect, which needs a
14
+ * visitor token to build Wix's hosted-login URL once per login. */
15
+ export declare function mintAnonymousVisitor(clientId: string, fetchImpl?: typeof fetch): Promise<string>;
16
+ /**
17
+ * Proxy one REST call to wixapis.com with the given token. `path` must be
18
+ * origin-relative — an absolute URL is the caller trying to use the bridge as an
19
+ * open proxy, which the read-only origin must never be (throws).
20
+ */
21
+ export declare function wixVisitorCall(method: string, path: string, body: string | undefined, token: string, fetchImpl?: typeof fetch, forwardedHeaders?: Record<string, string>): Promise<WixBridgeEnvelope>;
22
+ /**
23
+ * The caller-supplied headers a proxied call may carry to Wix (#3190): the Wix ASPECT headers
24
+ * (locale, currency, consistency, BI, brand, consent, revisions) and the per-call site selector
25
+ * `wix-site-id` — native sends it on every call and the dev-server's per-business net-mock rules key
26
+ * on it. (`ChatToken` is the one destination-bound exception: see `normalizeWixForwardedHeaders`.)
27
+ * An ALLOWLIST, lower-case: anything else is dropped — above all a second credential or an
28
+ * identity header (`x-wix-authorization`, `x-wix-clientid`, `x-xsrf-token`, `wix-account-id`,
29
+ * signed instances), which would otherwise ride along with the rung's own `Authorization` and leave
30
+ * which identity the call runs as to the gateway. Also hop-by-hop and framing headers.
31
+ *
32
+ * The aspect headers are exactly the ones the web host's browser-direct transports forward
33
+ * (`DIRECT_FORWARDABLE_HEADERS`, core/web-host/src/wix/directWixCall.ts — the gateway's CORS answer
34
+ * minus credentials); `BRIDGE_ONLY_FORWARDABLE_HEADERS` is the one addition, and
35
+ * core/web-host/tests/direct-wix-call.test.ts holds the lists together, so a call cannot behave
36
+ * differently depending on which path served it.
37
+ */
38
+ export declare const WIX_FORWARDABLE_HEADERS: ReadonlySet<string>;
39
+ /** What the bridge forwards beyond the browser-direct transports' aspect headers: the site selector
40
+ * (direct owner calls pick their instance by it instead). */
41
+ export declare const BRIDGE_ONLY_FORWARDABLE_HEADERS: ReadonlySet<string>;
42
+ /** The headers of an untrusted web bridge envelope that may reach Wix (`WIX_FORWARDABLE_HEADERS`, plus
43
+ * `ChatToken` when `path` is chat-web itself), in the caller's spelling. The bridge always owns
44
+ * Authorization, cookies, Host and content framing. */
45
+ export declare function normalizeWixForwardedHeaders(input: unknown, path?: string): Record<string, string>;
46
+ /** Guard the msid before it is interpolated into the mint URL (uuid-ish only). */
47
+ export declare function isSafeMsid(msid: string): boolean;
48
+ /** A `renew` never re-mints an instance younger than this, whichever mint made it (another tab's
49
+ * renewal, a first ask, the envelope's own 401 retry). An instance that new is the fresh one: a 401
50
+ * on it is Wix's answer about the endpoint, not a stale credential, and re-minting would only buy
51
+ * the same answer. It also means the route can't be driven to mint on every request. Once an entry
52
+ * is dropped (`invalidate`, a failed mint) there is nothing to hand back, and a renewal mints once,
53
+ * like any ask.
54
+ * No longer than the web host's `OWNER_RENEW_COOLDOWN_MS` (core/web-host/src/wix/directWixCall.ts,
55
+ * #3140): the host never sends `renew=1` sooner than that after its own re-mint, so each renewal it
56
+ * makes is honoured. A longer window here would hand it back the instance Wix just refused.
57
+ * core/serve/tests/owner-instance.test.ts pins the two together. */
58
+ export declare const OWNER_RENEW_MIN_AGE_MS = 5000;
59
+ type OwnerInstanceEntry = {
60
+ instance: string;
61
+ expiresAt: number;
62
+ };
63
+ export declare class OwnerInstanceCache {
64
+ private fetchImpl;
65
+ /** Signed instances are long-lived, but the SESSION behind them may not be —
66
+ * keep the window short so a logout is honored quickly. */
67
+ private ttlMs;
68
+ private mintTimeoutMs;
69
+ /** A monotonic clock for how long a mint TOOK (injectable for tests). */
70
+ private elapsedClock;
71
+ private cache;
72
+ /** The one mint on its way per (business, session). Every caller, plain or renewing, joins it:
73
+ * it started after whatever is cached was minted, so it is the fresh instance. */
74
+ private inflight;
75
+ constructor(fetchImpl?: typeof fetch,
76
+ /** Signed instances are long-lived, but the SESSION behind them may not be —
77
+ * keep the window short so a logout is honored quickly. */
78
+ ttlMs?: number, mintTimeoutMs?: number,
79
+ /** A monotonic clock for how long a mint TOOK (injectable for tests). */
80
+ elapsedClock?: () => number);
81
+ /** Keyed on the SESSION (`wixSession2`), not the whole cookie header: manage.wix.com's jar churns
82
+ * (analytics, experiments), and keying on all of it re-minted on nearly every call (#3044). A
83
+ * header with no session cookie falls back to itself — callers refuse that case before here. */
84
+ private key;
85
+ instance(msid: string, cookieHeader: string, now?: number): Promise<string>;
86
+ /** The instance and the time this cache stops trusting it (#3044: the web host caches until then).
87
+ * `renew`: Wix refused the instance this cache handed out (the route's `renew=1`, #3140) — mint a
88
+ * new one, unless the cached one is younger than OWNER_RENEW_MIN_AGE_MS or a mint is already on
89
+ * its way, in which case that IS the new one. */
90
+ entry(msid: string, cookieHeader: string, now?: number, opts?: {
91
+ renew?: boolean;
92
+ }): Promise<OwnerInstanceEntry>;
93
+ private mint;
94
+ /** Drop a cached instance so the next call re-mints. The envelope owner branch's own 401 retry;
95
+ * the route's `renew=1` goes through `entry(…, { renew: true })` instead. */
96
+ invalidate(msid: string, cookieHeader: string): void;
97
+ }
98
+ export declare const OWNER_SITES_QUERY_PATH = "/site-list/v2/sites/query";
99
+ export declare const OWNER_SITES_COUNT_PATH = "/site-list/v2/sites/count";
100
+ /** True for the ONLY two paths the account-level owner-sites rung may reach. */
101
+ export declare function isOwnerSitesPath(path: string): boolean;
102
+ /**
103
+ * POST one site-list call to wixapis.com, authorized by the FORWARDED caller
104
+ * cookies (never an Authorization header, never stored). Host-locked AND
105
+ * path-locked: anything but the two owner-sites paths throws.
106
+ */
107
+ export declare function wixOwnerSitesCall(path: string, cookieHeader: string, body: string | undefined, fetchImpl?: typeof fetch): Promise<WixBridgeEnvelope>;
108
+ /** One trimmed row of `GET /v1/owner/sites` — `id` IS the site's metaSiteId. */
109
+ export interface OwnerSiteRow {
110
+ id: string;
111
+ displayName: string;
112
+ namespace?: string;
113
+ }
114
+ /**
115
+ * RFC 0050 extension — the trimmed "my sites" listing per session, cached
116
+ * briefly. The cache key is a DIGEST of the cookie header (never the cookies
117
+ * themselves — same posture as OwnerInstanceCache); the limit rides the digest
118
+ * so differently-sized listings don't cross-serve. TTL mirrors OwnerInstanceCache
119
+ * (and wix-cache-policy's ttl:300 for this API): sites are stable, but the
120
+ * SESSION behind the listing may not be — keep the window short so a logout
121
+ * stops listing quickly.
122
+ */
123
+ export declare class OwnerSitesCache {
124
+ private fetchImpl;
125
+ private ttlMs;
126
+ private cache;
127
+ constructor(fetchImpl?: typeof fetch, ttlMs?: number);
128
+ private key;
129
+ sites(cookieHeader: string, limit: number, now?: number): Promise<OwnerSiteRow[]>;
130
+ /** Drop a cached listing (e.g. after an upstream 401) so the next call re-fetches. */
131
+ invalidate(cookieHeader: string, limit: number): void;
132
+ }
133
+ /**
134
+ * Resolve the Wix OAuth clientId for a published app-config, or a typed refusal.
135
+ * Only `wix-*` auth providers carry a REAL Wix clientId — a generic-oauth app's
136
+ * clientId is some other issuer's, and minting against it would just burn a
137
+ * round-trip to wixapis for a guaranteed failure.
138
+ */
139
+ export declare function wixClientIdOf(appConfigJson: string): {
140
+ clientId: string;
141
+ } | {
142
+ error: "unsupported_auth" | "no_client_id";
143
+ };
144
+ export {};
@@ -0,0 +1,13 @@
1
+ /** SHA-256 of `input`, as 32 bytes. Identical to `createHash("sha256").update(input).digest()`. */
2
+ export declare function sha256(input: string | Uint8Array): Uint8Array;
3
+ /** Lowercase hex of `sha256(input)` — what `createHash("sha256").digest("hex")` produces. */
4
+ export declare function sha256Hex(input: string | Uint8Array): string;
5
+ /**
6
+ * The same hex digest as `sha256Hex`, via the platform's native implementation.
7
+ *
8
+ * Identical output — `sha256-parity.test.ts` proves the two agree with each other and with node —
9
+ * but ~40x faster, because it is not JavaScript. Use it wherever the caller is already async AND
10
+ * the input can be large: a package archive, an asset. It is not a drop-in for the sync one, which
11
+ * is what lets `strongEtag` stay a plain function.
12
+ */
13
+ export declare function sha256HexAsync(input: string | Uint8Array): Promise<string>;
package/dist/cli.mjs CHANGED
@@ -6911,7 +6911,6 @@ function resolveTranslation(value, catalog, sourceCatalog) {
6911
6911
  var BINDING_SIGILS, BINDING_SIGIL_RE, TRANSLATION_PREFIX, TRANSLATION_KEY_RE;
6912
6912
  var init_bindings = __esm({
6913
6913
  "../../core/ts/src/bindings.ts"() {
6914
- "use strict";
6915
6914
  BINDING_SIGILS = ["service", "flag", "item", "t"];
6916
6915
  BINDING_SIGIL_RE = new RegExp(
6917
6916
  `^\\$(?:${BINDING_SIGILS.filter((s2) => s2 !== "t").join("|")})\\b|^\\$t:`
@@ -13465,6 +13464,7 @@ function resolveIdiom(tokens2, ctx = {}) {
13465
13464
  var IDIOM_CONTEXTS, UNSAFE_KEYS;
13466
13465
  var init_idiom = __esm({
13467
13466
  "../../core/ts/src/idiom.ts"() {
13467
+ "use strict";
13468
13468
  IDIOM_CONTEXTS = {
13469
13469
  ios: { native: ["ios"] },
13470
13470
  android: { native: ["material"] },
@@ -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-BASyrUOE.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1199331, sha256: "61bbacb882f499d8de973edf6cd283e953dad24fbe362d909e1d66b97aaea7af" },
144079
+ { key: "assets/pdf.worker.min-BmVo14Nb.mjs", contentType: "application/octet-stream", offset: 1199339, length: 1317034, sha256: "a33cfe728c584fdba4fcc1fd54bcdc2f9f2f13889ddbb5b2bd1d0f8cbe49b84e" },
144080
+ { key: "assets/pdfDocument-93Ito3np.js", contentType: "application/javascript; charset=utf-8", offset: 2516373, length: 489166, sha256: "1241febb4b04acb0b3b5341d29b16488d606d7a070e96b45872392309579817e" },
144081
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 3005539, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
144082
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 3010149, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
144083
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 3498733, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
144084
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 3589657, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
144085
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 3680641, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
144086
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 3771621, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
144087
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 3862493, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
144088
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 3953473, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
144089
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 4062109, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
144090
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 4171193, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
144091
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 4280241, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
144092
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 4387941, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
144093
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 4476049, length: 4947, sha256: "14ea64c15439ccc29ea7030d38ef2212a342b8e443445a4e4e3179c0ee3e9c66" },
144094
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 4480996, length: 17344, sha256: "d057298d75e7373e5e2c4d11b7d1bcbea88e03af555ba63d97641c78509c58e4" }
143837
144095
  ];
143838
144096
  }
143839
144097
  });
Binary file
@@ -20,7 +20,7 @@
20
20
  "name": "StreamDirective",
21
21
  "kind": "interface",
22
22
  "jsdoc": "/**\n * Streaming directive (RFC 0040). An action may return its new state with a\n * transient `_stream` field; the runtime strips it from the stored state and\n * asks the native host to open the request as a Server-Sent-Events stream. For\n * every parsed `data:` event the host dispatches `onEvent(state, event)` back\n * through the normal reducer→render loop (the worker has no timers), then\n * `onDone(state)` / `onError(state, {message})` when the stream closes. Requires\n * the bundle to declare the `\"stream\"` capability; `request.url` is checked\n * against the same per-capability host allowlist as `wix_api`.\n */",
23
- "decl": "export interface StreamDirective {\n /** Stable id; a new directive with the same id cancels the prior stream. */\n id: string;\n request: {\n method: \"POST\" | \"GET\";\n url: string;\n headers?: Record<string, string>;\n body?: unknown;\n };\n /** Reducer action name dispatched once per parsed `data:` event. */\n onEvent: string;\n /** Reducer action name dispatched when the stream closes cleanly. */\n onDone?: string;\n /** Reducer action name dispatched on transport/parse error. */\n onError?: string;\n}"
23
+ "decl": "export interface StreamDirective {\n /** Stable id; a new directive with the same id cancels the prior stream. */\n id: string;\n request: {\n method: \"POST\" | \"GET\";\n url: string;\n headers?: Record<string, string>;\n body?: unknown;\n /** Apply the host's Wix authorization to this request before opening the stream. */\n authorize?: boolean;\n };\n /** Reducer action name dispatched once per parsed `data:` event. */\n onEvent: string;\n /** Reducer action name dispatched when the stream closes cleanly. */\n onDone?: string;\n /** Reducer action name dispatched on transport/parse error. */\n onError?: string;\n}"
24
24
  },
25
25
  {
26
26
  "name": "StreamEvent",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1251.0",
3
+ "version": "0.1252.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": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "2fb267af6593debfaa9fac6bdde663a5a54cbd0c533c577df322b5ca"
70
+ "falconPackageHash": "87f23d8f06f88ed92ac988ab724637b5633bcdaccfc2c23e791f1a71"
71
71
  }