@wix/himalaya-cli 0.844.0 → 0.845.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 (47) hide show
  1. package/dist/cli.mjs +9 -2
  2. package/guides/release-notes.md +19 -0
  3. package/package.json +2 -2
  4. package/templates/beauty-salon-owner/dev/index.ts +858 -0
  5. package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +59 -9
  6. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  7. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  8. package/templates/community-club-owner/dev/index.ts +858 -0
  9. package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +59 -9
  10. package/templates/community-club-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  11. package/templates/community-club-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  12. package/templates/events-meetups-owner/dev/index.ts +858 -0
  13. package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +59 -9
  14. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  15. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  16. package/templates/gym-fitness-owner/dev/index.ts +858 -0
  17. package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +59 -9
  18. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  19. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  20. package/templates/nonprofit-cause-owner/dev/index.ts +858 -0
  21. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +59 -9
  22. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  23. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  24. package/templates/owner-lite/dev/index.ts +858 -0
  25. package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +59 -9
  26. package/templates/owner-lite/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  27. package/templates/owner-lite/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  28. package/templates/podcast-creator-owner/dev/index.ts +858 -0
  29. package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +59 -9
  30. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  31. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  32. package/templates/portfolio-creative-owner/dev/index.ts +858 -0
  33. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +59 -9
  34. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  35. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  36. package/templates/restaurant-owner/dev/index.ts +858 -0
  37. package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +59 -9
  38. package/templates/restaurant-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  39. package/templates/restaurant-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  40. package/templates/retail-store-owner/dev/index.ts +858 -0
  41. package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +59 -9
  42. package/templates/retail-store-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  43. package/templates/retail-store-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
  44. package/templates/yoga-studio-owner/dev/index.ts +858 -0
  45. package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +59 -9
  46. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-analytics/index.ts +7 -1
  47. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-analytics/report.ts +690 -0
@@ -233,25 +233,69 @@ function linkFieldsOf(raw: string, role: string) {
233
233
  };
234
234
  }
235
235
 
236
- /** The host of an `https://host/…` url, lowercased, or "" if it has none.
236
+ /** A syntactically plain hostname: labels of letters, digits and dashes joined
237
+ * by dots, or a bracketed IPv6 literal. Anything else — a backslash, a space,
238
+ * a stray `@`, the empty string — means the parse below did not yield a host,
239
+ * and a non-host must never be matched against the Wix suffixes: `endsWith` is
240
+ * a plain string test, and `evil.example\.wix.com` ends with `.wix.com` while
241
+ * the browser opens `evil.example`. */
242
+ const PLAIN_HOST = /^(?:[a-z0-9-]+(?:\.[a-z0-9-]+)*|\[[0-9a-f:.]+\])$/;
243
+
244
+ /** The host an `https://host/…` url will actually open, lowercased, or "" when
245
+ * that cannot be established.
246
+ *
237
247
  * A regex, not `new URL`: the worker runtimes (JSContext, Hermes, QuickJS) do
238
- * not all ship the URL constructor. Credentials before an `@` are ignored, so
239
- * `https://manage.wix.com@evil.example/` reads as `evil.example` — the whole
240
- * point of showing a host is that it is the one the device will actually open. */
248
+ * not all ship the URL constructor. So this has to imitate the URL standard's
249
+ * authority parsing closely enough that the host it reports is the host the
250
+ * device opens — the entire value of showing a host is that it is the one the
251
+ * tap goes to. Two details earn their keep:
252
+ *
253
+ * - Credentials before an `@` are ignored, so
254
+ * `https://manage.wix.com@evil.example/` reads as `evil.example`.
255
+ * - A BACKSLASH ends the authority, because for http(s) the URL standard
256
+ * treats `\` exactly like `/`. Without that, `https://evil.example\@wix.com/`
257
+ * reads as `wix.com` here while every browser opens `evil.example` with the
258
+ * path `/@wix.com/`. That is exactly the lie this disclosure exists to
259
+ * prevent, so the separator set has to match the URL standard's rather than
260
+ * RFC 3986's.
261
+ *
262
+ * A string that survives parsing but is not a plain hostname returns "", which
263
+ * callers must treat as untrusted rather than as "there is no host here". */
241
264
  export function hostOf(url: string): string {
242
- const m = /^https?:\/\/([^/?#]*)/i.exec(url.trim());
265
+ const m = /^https?:\/\/([^/?#\\]*)/i.exec(url.trim());
243
266
  if (!m) return "";
244
267
  const authority = m[1];
245
268
  const at = authority.lastIndexOf("@");
246
269
  const hostPort = at >= 0 ? authority.slice(at + 1) : authority;
247
- return hostPort.replace(/:\d+$/, "").toLowerCase();
270
+ const host = hostPort.replace(/:\d+$/, "").toLowerCase();
271
+ return PLAIN_HOST.test(host) ? host : "";
248
272
  }
249
273
 
250
- /** Is this url somewhere Wix itself serves? */
274
+ /** Is this url somewhere Wix itself serves?
275
+ *
276
+ * Guarded by `PLAIN_HOST` first. The suffix tests are string operations, so
277
+ * without the guard anything ending in `.wix.com` passes no matter what else
278
+ * it contains. `hostOf` already applies the same guard; this repeats it because
279
+ * a predicate that decides whether to warn somebody should not depend on its
280
+ * caller having sanitized the input. */
251
281
  export function isWixHost(host: string): boolean {
282
+ if (!PLAIN_HOST.test(host)) return false;
252
283
  return host === "wix.com" || host.endsWith(".wix.com") || host === "wixsite.com" || host.endsWith(".wixsite.com");
253
284
  }
254
285
 
286
+ /** Shown in place of a host when an `http(s)` link's host cannot be
287
+ * established. Deliberately not domain-shaped: it has to read as "this could
288
+ * not be checked", never as a site the owner might think they recognize.
289
+ *
290
+ * KNOWN LIMITATION: this is the one user-facing string here that does not come
291
+ * from the copy pack, so it does not localize. It is a literal for the same
292
+ * reason the `(host)` parenthetical beside it is: both are appended by a PURE
293
+ * function, reached from a pure one, with no copy resolver in scope — and
294
+ * threading one through to reach a safety annotation is a larger change than
295
+ * the annotation. Route it through copy when this file next has a reason to
296
+ * take a resolver, rather than half-threading one now. */
297
+ const UNKNOWN_HOST_LABEL = "unknown site";
298
+
255
299
  /** A link row's label, with the destination host appended when the link leaves
256
300
  * Wix.
257
301
  *
@@ -269,8 +313,14 @@ export function isWixHost(host: string): boolean {
269
313
  * is an `/add-sdui-component`-class parity change. */
270
314
  export function disclosedLabel(link: { label: string; url: string }): string {
271
315
  const host = hostOf(link.url);
272
- if (!host || isWixHost(host)) return link.label;
273
- return `${link.label} (${host})`;
316
+ // Silence is reserved for a host positively identified as Wix's. Every link
317
+ // reaching here came from `extractLinks`, which accepts `http(s)://` only —
318
+ // so a url whose host cannot be established is a MALFORMED one, not a scheme
319
+ // with nothing to show, and reading that as "nothing to disclose" is how
320
+ // `https:///evil.example/` rendered with no host while opening
321
+ // `evil.example`. Fail closed: unknown is disclosed, never trusted.
322
+ if (isWixHost(host)) return link.label;
323
+ return `${link.label} (${host || UNKNOWN_HOST_LABEL})`;
274
324
  }
275
325
 
276
326
  /** Aria replies come back as markdown. There is no native markdown renderer
@@ -39,6 +39,12 @@ import {
39
39
  type WixAnalyticsMeasurement,
40
40
  type WixMeasure,
41
41
  } from "@himalaya/wix-analytics";
42
+ // The REPORTS service — the Semantic Model reporting engine — is the
43
+ // second service in this bundle. Imported for its registration side effect:
44
+ // `defineService` registers at call time and the host resolves
45
+ // `$service:ownerLiteAnalyticsReport` out of that registry. See report.ts for
46
+ // why the two share a bundle and why only this one auto-loads.
47
+ import { svc as reportSvc } from "./report.js";
42
48
 
43
49
  interface CardRow { id: string; label: string; value: string; delta: string; }
44
50
  interface StatRow { id: string; label: string; state: string; }
@@ -549,4 +555,4 @@ const svc = defineService<AnalyticsState, {
549
555
  });
550
556
 
551
557
  svc.load();
552
- export { svc };
558
+ export { svc, reportSvc };