@wix/himalaya-cli 0.1509.0 → 0.1510.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.mjs +192 -26
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -22176,7 +22176,6 @@ function resolveIdiom(tokens2, ctx = {}) {
22176
22176
  var IDIOM_CONTEXTS, UNSAFE_KEYS;
22177
22177
  var init_idiom = __esm({
22178
22178
  "../../core/ts/src/idiom.ts"() {
22179
- "use strict";
22180
22179
  IDIOM_CONTEXTS = {
22181
22180
  ios: { native: ["ios"] },
22182
22181
  android: { native: ["material"] },
@@ -163184,6 +163183,134 @@ var init_member_exchange = __esm({
163184
163183
  }
163185
163184
  });
163186
163185
 
163186
+ // ../../core/serve-runtime/src/member-forward.ts
163187
+ function matchMemberForward(method, raw, rules = MEMBER_FORWARD_ALLOWLIST) {
163188
+ if (typeof method !== "string" || typeof raw !== "string" || raw.length === 0 || raw.length > 8192) return null;
163189
+ const verb = method.toUpperCase();
163190
+ let url;
163191
+ try {
163192
+ url = new URL(raw);
163193
+ } catch {
163194
+ return null;
163195
+ }
163196
+ if (url.protocol !== "https:" || url.port !== "" || url.username || url.password || url.hash) return null;
163197
+ const rawPath = raw.replace(/^https:\/\/[^/?#]*/i, "").split(/[?#]/, 1)[0];
163198
+ if (rawPath !== url.pathname || /[%\\]|\/\/|\.\./.test(rawPath)) return null;
163199
+ const host = url.hostname.toLowerCase();
163200
+ const rule = rules.find((r3) => r3.method === verb && r3.host === host && r3.path.test(url.pathname));
163201
+ if (!rule) return null;
163202
+ const seen = /* @__PURE__ */ new Set();
163203
+ for (const [key3, value] of url.searchParams) {
163204
+ const allowed2 = Object.hasOwn(rule.query, key3) ? rule.query[key3] : void 0;
163205
+ if (!(allowed2 instanceof RegExp) || seen.has(key3) || !allowed2.test(value)) return null;
163206
+ seen.add(key3);
163207
+ }
163208
+ if ((rule.required ?? []).some((key3) => !seen.has(key3))) return null;
163209
+ return { rule, url };
163210
+ }
163211
+ function matchForwardHeaders(rule, raw) {
163212
+ const allowed2 = rule.headers ?? {};
163213
+ const out2 = {};
163214
+ if (raw !== void 0 && raw !== null) {
163215
+ if (typeof raw !== "object" || Array.isArray(raw)) return null;
163216
+ for (const [name, value] of Object.entries(raw)) {
163217
+ const canonical7 = Object.keys(allowed2).find((k2) => k2.toLowerCase() === name.toLowerCase());
163218
+ if (!canonical7 || typeof value !== "string" || !allowed2[canonical7].test(value) || Object.hasOwn(out2, canonical7)) return null;
163219
+ out2[canonical7] = value;
163220
+ }
163221
+ }
163222
+ if ((rule.requiredHeaders ?? []).some((name) => !Object.hasOwn(out2, name))) return null;
163223
+ return out2;
163224
+ }
163225
+ async function readCapped(res, maxChars) {
163226
+ const declared = Number(res.headers.get("content-length"));
163227
+ if (Number.isFinite(declared) && declared > maxChars * 4) {
163228
+ await res.body?.cancel().catch(() => {
163229
+ });
163230
+ return null;
163231
+ }
163232
+ if (!res.body) {
163233
+ const text12 = await res.text();
163234
+ return text12.length > maxChars ? null : text12;
163235
+ }
163236
+ const reader = res.body.getReader();
163237
+ const decoder = new TextDecoder();
163238
+ let text11 = "";
163239
+ for (; ; ) {
163240
+ const { done, value } = await reader.read();
163241
+ if (done) break;
163242
+ text11 += decoder.decode(value, { stream: true });
163243
+ if (text11.length > maxChars) {
163244
+ await reader.cancel().catch(() => {
163245
+ });
163246
+ return null;
163247
+ }
163248
+ }
163249
+ text11 += decoder.decode();
163250
+ return text11.length > maxChars ? null : text11;
163251
+ }
163252
+ async function memberForwardCall(match, body, memberToken, fetchImpl = fetch, extraHeaders = {}) {
163253
+ const headers2 = { Accept: "application/json", ...extraHeaders };
163254
+ if (body !== void 0) headers2["Content-Type"] = "application/json";
163255
+ if (match.rule.auth === "member") headers2.Authorization = memberToken;
163256
+ try {
163257
+ const res = await fetchImpl(match.url.toString(), {
163258
+ method: match.rule.method,
163259
+ headers: headers2,
163260
+ body,
163261
+ redirect: "manual",
163262
+ credentials: "omit",
163263
+ signal: AbortSignal.timeout(FORWARD_TIMEOUT_MS)
163264
+ });
163265
+ const text11 = await readCapped(res, MAX_RESPONSE_CHARS);
163266
+ if (text11 === null) return { ok: false, code: "forward_too_large" };
163267
+ return { ok: true, status: res.status, body: text11 };
163268
+ } catch {
163269
+ return { ok: false, code: "forward_failed" };
163270
+ }
163271
+ }
163272
+ var BOOL, SMALL_INT, TOKENISH, CURSOR, ROOM, LIST_QUERY, MEMBER_FORWARD_ALLOWLIST, FORWARD_TIMEOUT_MS, MAX_RESPONSE_CHARS;
163273
+ var init_member_forward = __esm({
163274
+ "../../core/serve-runtime/src/member-forward.ts"() {
163275
+ BOOL = /^(true|false)$/;
163276
+ SMALL_INT = /^\d{1,6}$/;
163277
+ TOKENISH = /^[A-Za-z0-9._~-]{1,4096}$/;
163278
+ CURSOR = /^[A-Za-z0-9._~:=+/,-]{1,2048}$/;
163279
+ ROOM = "[A-Za-z0-9-]{1,64}";
163280
+ LIST_QUERY = { chatToken: TOKENISH, nextPointer: CURSOR, pageSize: SMALL_INT, archived: BOOL, onlyUnread: BOOL, lastMessageLimit: SMALL_INT, unreadChatroomLimit: SMALL_INT };
163281
+ MEMBER_FORWARD_ALLOWLIST = [
163282
+ // Chat: mint the chat token with the member token alone.
163283
+ { method: "POST", host: "manage.wix.com", path: /^\/_api\/wix-chat-tokens-server\/v1\/chat-tokens$/, auth: "member", query: { isForUnifiedList: BOOL } },
163284
+ // Chat: the member's conversation list (all, or one participant type), authorised by the chat token in the query.
163285
+ { method: "POST", host: "manage.wix.com", path: /^\/_api\/chat-web\/v1\/chatrooms-for-list-view(?:\/(?:business|siteMember))?$/, auth: "none", query: LIST_QUERY, required: ["chatToken"] },
163286
+ { method: "GET", host: "manage.wix.com", path: /^\/_api\/chat-web\/v2\/chatrooms$/, auth: "none", query: { chatToken: TOKENISH, nextPointer: CURSOR, pageSize: SMALL_INT }, required: ["chatToken"] },
163287
+ // Chat: a conversation's history.
163288
+ {
163289
+ method: "GET",
163290
+ host: "manage.wix.com",
163291
+ path: new RegExp(`^/_api/chat-web/v1/chatrooms/${ROOM}/messages$`),
163292
+ auth: "none",
163293
+ query: { chatToken: TOKENISH, pageSize: SMALL_INT, ordering: /^(?:Asc|Desc)$/, nextPointer: CURSOR },
163294
+ required: ["chatToken"]
163295
+ },
163296
+ // Chat: send a message. The chat token rides the `ChatToken` HEADER here (the query is rejected: probed).
163297
+ {
163298
+ method: "POST",
163299
+ host: "manage.wix.com",
163300
+ path: new RegExp(`^/_api/chat-web/v2/chatrooms/${ROOM}/messages$`),
163301
+ auth: "none",
163302
+ query: {},
163303
+ headers: { ChatToken: TOKENISH },
163304
+ requiredHeaders: ["ChatToken"]
163305
+ },
163306
+ // Chat: the live-update (realtime) token. The answer carries a credential for a third party; it is returned to the caller only.
163307
+ { method: "POST", host: "manage.wix.com", path: /^\/_api\/chat-web\/v1\/real-time-tokens$/, auth: "none", query: { chatToken: TOKENISH }, required: ["chatToken"] }
163308
+ ];
163309
+ FORWARD_TIMEOUT_MS = 15e3;
163310
+ MAX_RESPONSE_CHARS = 1e6;
163311
+ }
163312
+ });
163313
+
163187
163314
  // ../../core/serve-runtime/package-share.ts
163188
163315
  function parseStored(bytes2, id) {
163189
163316
  if (!bytes2) return null;
@@ -163740,6 +163867,27 @@ ${releaseId}`;
163740
163867
  }
163741
163868
  return new Response(body, { status: 200, headers: { "content-type": JSON_CT3, etag, "cache-control": cache, ...vary } });
163742
163869
  }
163870
+ async function draftPreviewOnly(request, url, noStore) {
163871
+ const refuse7 = (status, error) => ({ ok: false, response: json5(status, { error }, noStore) });
163872
+ if (opts.webwixBridge === false) return refuse7(503, "webwix_bridge_disabled");
163873
+ if (!opts.previewTokenSecret) return refuse7(503, "preview_token_unavailable");
163874
+ const app = resolveApp(request, url);
163875
+ if (!app) return refuse7(400, "app_required");
163876
+ const pin = resolvePin(request, url);
163877
+ if (!pin || !resolvePreviewToken(request, url)) return refuse7(403, "draft_preview_required");
163878
+ const cfg = await appConfigFor(request, url, app);
163879
+ if (!cfg.ok) return { ok: false, response: cfg.response };
163880
+ let ringPinned = false;
163881
+ if (opts.ringReleases) {
163882
+ try {
163883
+ ringPinned = new Set(await opts.ringReleases(app)).has(pin);
163884
+ } catch {
163885
+ return refuse7(503, "ring_lookup_unavailable");
163886
+ }
163887
+ }
163888
+ if ((await manifest(app))?.releaseId === pin || ringPinned) return refuse7(403, "draft_preview_required");
163889
+ return { ok: true, app, pin, cfgBytes: cfg.bytes };
163890
+ }
163743
163891
  async function authorizePinnedRead(request, url, app, releaseId, screenId) {
163744
163892
  const denied2 = await checkPinnedRead({
163745
163893
  secret: opts.previewTokenSecret,
@@ -164020,26 +164168,9 @@ ${releaseId}`;
164020
164168
  }
164021
164169
  if (request.method === "POST" && path2 === "/v1/__webwix/member-exchange") {
164022
164170
  const noStore = { "cache-control": "no-store" };
164023
- if (opts.webwixBridge === false) return json5(503, { error: "webwix_bridge_disabled" }, noStore);
164024
- if (!opts.previewTokenSecret) return json5(503, { error: "preview_token_unavailable" }, noStore);
164025
- const exchangeApp = resolveApp(request, url);
164026
- if (!exchangeApp) return json5(400, { error: "app_required" }, noStore);
164027
- const exchangePin = resolvePin(request, url);
164028
- if (!exchangePin || !resolvePreviewToken(request, url)) return json5(403, { error: "draft_preview_required" }, noStore);
164029
- const exchangeCfg = await appConfigFor(request, url, exchangeApp);
164030
- if (!exchangeCfg.ok) return exchangeCfg.response;
164031
- let ringPinned = false;
164032
- if (opts.ringReleases) {
164033
- try {
164034
- ringPinned = new Set(await opts.ringReleases(exchangeApp)).has(exchangePin);
164035
- } catch {
164036
- return json5(503, { error: "ring_lookup_unavailable" }, noStore);
164037
- }
164038
- }
164039
- if ((await manifest(exchangeApp))?.releaseId === exchangePin || ringPinned) {
164040
- return json5(403, { error: "draft_preview_required" }, noStore);
164041
- }
164042
- const exchangeClient = wixClientIdOf(utf8Decode(exchangeCfg.bytes));
164171
+ const gate = await draftPreviewOnly(request, url, noStore);
164172
+ if (!gate.ok) return gate.response;
164173
+ const exchangeClient = wixClientIdOf(utf8Decode(gate.cfgBytes));
164043
164174
  if ("error" in exchangeClient) return json5(501, { error: `member_exchange_${exchangeClient.error}` }, noStore);
164044
164175
  const headerToken = request.headers.get("x-himalaya-member-session");
164045
164176
  let sessionToken = headerToken ?? void 0;
@@ -164068,6 +164199,40 @@ ${releaseId}`;
164068
164199
  if (exchanged.code === "bad_request") return json5(400, { error: "invalid_session_token", received }, noStore);
164069
164200
  return json5(502, { error: exchanged.code, ...exchanged.status !== void 0 ? { status: exchanged.status } : {} }, noStore);
164070
164201
  }
164202
+ if (request.method === "POST" && path2 === "/v1/__webwix/member-forward") {
164203
+ const noStore = { "cache-control": "no-store" };
164204
+ const gate = await draftPreviewOnly(request, url, noStore);
164205
+ if (!gate.ok) return gate.response;
164206
+ let raw;
164207
+ try {
164208
+ raw = await request.text();
164209
+ } catch {
164210
+ return json5(400, { error: "body_unreadable" }, noStore);
164211
+ }
164212
+ if (!raw) return json5(400, { error: "empty_body" }, noStore);
164213
+ if (raw.length > 256e3) return json5(413, { error: "body_too_large" }, noStore);
164214
+ let parsed;
164215
+ try {
164216
+ parsed = JSON.parse(raw);
164217
+ } catch {
164218
+ return json5(400, { error: "invalid_json" }, noStore);
164219
+ }
164220
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return json5(400, { error: "invalid_body" }, noStore);
164221
+ if (typeof parsed.memberToken !== "string" || !parsed.memberToken || parsed.memberToken.length > 4096) return json5(400, { error: "member_token_required" }, noStore);
164222
+ const match = matchMemberForward(parsed.method, parsed.url);
164223
+ if (!match) return json5(403, { error: "forward_target_refused" }, noStore);
164224
+ const forwardHeaders = matchForwardHeaders(match.rule, parsed.headers);
164225
+ if (!forwardHeaders) return json5(403, { error: "forward_header_refused" }, noStore);
164226
+ let forwardBody;
164227
+ if (parsed.body !== void 0) {
164228
+ if (match.rule.method === "GET" || match.rule.method === "DELETE") return json5(400, { error: "body_not_allowed" }, noStore);
164229
+ forwardBody = typeof parsed.body === "string" ? parsed.body : JSON.stringify(parsed.body);
164230
+ if (forwardBody.length > 64e3) return json5(413, { error: "body_too_large" }, noStore);
164231
+ }
164232
+ const forwarded = await memberForwardCall(match, forwardBody, parsed.memberToken, webwixFetch, forwardHeaders);
164233
+ if (!forwarded.ok) return json5(502, { error: forwarded.code }, noStore);
164234
+ return json5(200, { status: forwarded.status, body: forwarded.body }, noStore);
164235
+ }
164071
164236
  if (request.method === "POST" && path2 === "/v1/__webwix/call") {
164072
164237
  const served = { rung: "none" };
164073
164238
  let status = 500;
@@ -164693,6 +164858,7 @@ var init_server2 = __esm({
164693
164858
  init_contract();
164694
164859
  init_pinned_read();
164695
164860
  init_member_exchange();
164861
+ init_member_forward();
164696
164862
  init_package_share();
164697
164863
  init_wix_bridge();
164698
164864
  init_account_rung();
@@ -176770,7 +176936,7 @@ function extOf2(url) {
176770
176936
  if (/\.ico$/i.test(path2)) return "ico";
176771
176937
  return "jpg";
176772
176938
  }
176773
- async function readCapped(res, cap) {
176939
+ async function readCapped2(res, cap) {
176774
176940
  const declared = Number(res.headers?.get?.("content-length"));
176775
176941
  if (Number.isFinite(declared) && declared > cap) {
176776
176942
  await discardBody(res);
@@ -176806,7 +176972,7 @@ async function fetchBrandFace(family, url, fetchImpl) {
176806
176972
  await discardBody(res);
176807
176973
  return void 0;
176808
176974
  }
176809
- const capped = await readCapped(res, MAX_FONT_BYTES);
176975
+ const capped = await readCapped2(res, MAX_FONT_BYTES);
176810
176976
  if (!capped) return void 0;
176811
176977
  woff2 = capped;
176812
176978
  } catch {
@@ -177345,7 +177511,7 @@ function pagesFromRegistry(js2) {
177345
177511
  return [...m2[1].matchAll(/["']?([A-Z][A-Za-z0-9_]*)["']?\s*:/g)].map((x2) => x2[1]).slice(0, MAX_ROUTES);
177346
177512
  }
177347
177513
  async function readText3(res, cap) {
177348
- const buf = await readCapped(res, cap);
177514
+ const buf = await readCapped2(res, cap);
177349
177515
  if (!buf) throw new Error(`body over the ${cap}-byte cap`);
177350
177516
  return buf.toString("utf8");
177351
177517
  }
@@ -180788,7 +180954,7 @@ async function createPlaywrightBrowser(load3 = () => import("playwright"), siteF
180788
180954
  await route.fulfill({ status: res.status, headers: { location }, body: Buffer.alloc(0) });
180789
180955
  return;
180790
180956
  }
180791
- const capped = await readCapped(res, MAX_DOCUMENT_BYTES);
180957
+ const capped = await readCapped2(res, MAX_DOCUMENT_BYTES);
180792
180958
  if (!capped) {
180793
180959
  await route.abort();
180794
180960
  return;
@@ -181198,7 +181364,7 @@ function projectDownloader(apiFetch, apiBase, token, extract = systemTar) {
181198
181364
  if (!res.ok) {
181199
181365
  throw new Error(res.status === 401 || res.status === 403 ? `refused (${res.status}) \u2014 the signed-in user cannot download this app's code (editor role needed)` : `project download failed: HTTP ${res.status}`);
181200
181366
  }
181201
- const bytes2 = await readCapped(res, MAX_TARBALL_BYTES);
181367
+ const bytes2 = await readCapped2(res, MAX_TARBALL_BYTES);
181202
181368
  if (!bytes2) throw new Error(`project download is over the ${MAX_TARBALL_BYTES}-byte cap`);
181203
181369
  const root = mkdtempSync10(join112(tmpdir11(), "himi-base44-"));
181204
181370
  const cleanup = () => rmSync30(root, { recursive: true, force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1509.0",
3
+ "version": "0.1510.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": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "e93a43678d27c6109e94db0df1cbc5d530447037d1ba12bc431c73cc"
74
+ "falconPackageHash": "998b3b056eb19f8b72dbf9d3dfcd78c59ad50f7c4390a381ce6780d1"
75
75
  }