@wix/himalaya-cli 0.904.0 → 0.905.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.
@@ -20,6 +20,39 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
20
20
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
21
21
  import { wixMediaUrl } from "@himalaya/wix-media";
22
22
 
23
+ /** Latched once the member feed is refused because of WHERE the app is running rather
24
+ * than because of the request — see the catch in `loadExtras`. Module scope, so it
25
+ * survives every refresh of this worker and is reset only by a fresh boot. */
26
+ let feedSurfaceRefused = false;
27
+
28
+ /** True for the one refusal that can never change while the app keeps running: the web
29
+ * bridge reporting that this host is not reachable from a browser at all. Deliberately
30
+ * narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
31
+ * this typed refusal is permanent. */
32
+ function isFeedSurfaceRefusal(error: unknown): boolean {
33
+ return /webwix_owner_only/.test(String((error as Error)?.message ?? error));
34
+ }
35
+
36
+ /** In-flight feed reads, keyed by the container they are for.
37
+ *
38
+ * The latch alone still let ONE duplicate through: two Home refreshes that overlap both read
39
+ * `feedSurfaceRefused === false` before the first rejection reaches the catch. Sharing the
40
+ * request closes that window — the second refresh awaits the first instead of sending its own,
41
+ * and both see the same rejection, so the latch is set exactly once.
42
+ *
43
+ * Keyed by container, not a single slot: `active` is the most recently active group, recomputed
44
+ * per refresh from a live `queryGroups`, so two overlapping refreshes can legitimately want
45
+ * DIFFERENT containers. A single shared promise would hand the second one the first one's feed. */
46
+ const feedInFlight = new Map<string, Promise<Awaited<ReturnType<typeof listFeedItems>>>>();
47
+
48
+ function feedFor(containerId: string, limit: number): Promise<Awaited<ReturnType<typeof listFeedItems>>> {
49
+ const pending = feedInFlight.get(containerId);
50
+ if (pending) return pending;
51
+ const started = listFeedItems(containerId, limit).finally(() => { feedInFlight.delete(containerId); });
52
+ feedInFlight.set(containerId, started);
53
+ return started;
54
+ }
55
+
23
56
  interface ProductFeature {
24
57
  id: string;
25
58
  name: string;
@@ -714,10 +747,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
714
747
  const response = await queryGroups({ paging: { limit: 12 } });
715
748
  const groups = (response.groups ?? []).filter((group) => group.id);
716
749
  if (verticals.includes("groups")) extras.groupCards = groups.slice(0, 6).map(groupCard);
717
- if (verticals.includes("feed") && groups.length) {
750
+ if (verticals.includes("feed") && groups.length && !feedSurfaceRefused) {
718
751
  const active = groups.slice().sort((a, b) =>
719
752
  new Date(b.lastActivityDate ?? 0).getTime() - new Date(a.lastActivityDate ?? 0).getTime())[0];
720
- const feed = await listFeedItems(active.id, 8);
753
+ const feed = await feedFor(active.id, 8);
721
754
  const profiles = feed.profiles ?? [];
722
755
  const items = (feed.feedItems ?? []).filter((item) => item.feedItemId);
723
756
  extras.feedHighlights = items.slice(0, 5).map((item) => {
@@ -742,7 +775,15 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
742
775
  }))
743
776
  .slice(0, 12);
744
777
  }
745
- } catch { /* collapse silently */ }
778
+ } catch (error) {
779
+ // This particular refusal is PERMANENT, not a blip: in a browser the member feed's
780
+ // host cannot be reached at all, so asking again can only fail again. Retrying on
781
+ // every Home visit spent one doomed round trip each time and filled the error log
782
+ // with something nobody can act on. Latch it once and stop asking; on a phone,
783
+ // where the call does work, this never trips.
784
+ if (isFeedSurfaceRefusal(error)) feedSurfaceRefused = true;
785
+ /* otherwise collapse silently, as before */
786
+ }
746
787
  })());
747
788
  }
748
789
  await Promise.all(jobs);
@@ -893,6 +934,7 @@ function baseState(): State {
893
934
  * let a stale failure clobber a newer success (returning `undefined` keeps the
894
935
  * current service state; see JSRuntimeBootstrap.applyNext). */
895
936
  let refreshGen = 0;
937
+
896
938
  /** Last successful featured list — catch paths read this when the action's
897
939
  * `state` snapshot was captured before a racing refresh painted products. */
898
940
  let lastFeaturedProducts: ProductFeature[] = [];
@@ -20,6 +20,39 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
20
20
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
21
21
  import { wixMediaUrl } from "@himalaya/wix-media";
22
22
 
23
+ /** Latched once the member feed is refused because of WHERE the app is running rather
24
+ * than because of the request — see the catch in `loadExtras`. Module scope, so it
25
+ * survives every refresh of this worker and is reset only by a fresh boot. */
26
+ let feedSurfaceRefused = false;
27
+
28
+ /** True for the one refusal that can never change while the app keeps running: the web
29
+ * bridge reporting that this host is not reachable from a browser at all. Deliberately
30
+ * narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
31
+ * this typed refusal is permanent. */
32
+ function isFeedSurfaceRefusal(error: unknown): boolean {
33
+ return /webwix_owner_only/.test(String((error as Error)?.message ?? error));
34
+ }
35
+
36
+ /** In-flight feed reads, keyed by the container they are for.
37
+ *
38
+ * The latch alone still let ONE duplicate through: two Home refreshes that overlap both read
39
+ * `feedSurfaceRefused === false` before the first rejection reaches the catch. Sharing the
40
+ * request closes that window — the second refresh awaits the first instead of sending its own,
41
+ * and both see the same rejection, so the latch is set exactly once.
42
+ *
43
+ * Keyed by container, not a single slot: `active` is the most recently active group, recomputed
44
+ * per refresh from a live `queryGroups`, so two overlapping refreshes can legitimately want
45
+ * DIFFERENT containers. A single shared promise would hand the second one the first one's feed. */
46
+ const feedInFlight = new Map<string, Promise<Awaited<ReturnType<typeof listFeedItems>>>>();
47
+
48
+ function feedFor(containerId: string, limit: number): Promise<Awaited<ReturnType<typeof listFeedItems>>> {
49
+ const pending = feedInFlight.get(containerId);
50
+ if (pending) return pending;
51
+ const started = listFeedItems(containerId, limit).finally(() => { feedInFlight.delete(containerId); });
52
+ feedInFlight.set(containerId, started);
53
+ return started;
54
+ }
55
+
23
56
  interface ProductFeature {
24
57
  id: string;
25
58
  name: string;
@@ -714,10 +747,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
714
747
  const response = await queryGroups({ paging: { limit: 12 } });
715
748
  const groups = (response.groups ?? []).filter((group) => group.id);
716
749
  if (verticals.includes("groups")) extras.groupCards = groups.slice(0, 6).map(groupCard);
717
- if (verticals.includes("feed") && groups.length) {
750
+ if (verticals.includes("feed") && groups.length && !feedSurfaceRefused) {
718
751
  const active = groups.slice().sort((a, b) =>
719
752
  new Date(b.lastActivityDate ?? 0).getTime() - new Date(a.lastActivityDate ?? 0).getTime())[0];
720
- const feed = await listFeedItems(active.id, 8);
753
+ const feed = await feedFor(active.id, 8);
721
754
  const profiles = feed.profiles ?? [];
722
755
  const items = (feed.feedItems ?? []).filter((item) => item.feedItemId);
723
756
  extras.feedHighlights = items.slice(0, 5).map((item) => {
@@ -742,7 +775,15 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
742
775
  }))
743
776
  .slice(0, 12);
744
777
  }
745
- } catch { /* collapse silently */ }
778
+ } catch (error) {
779
+ // This particular refusal is PERMANENT, not a blip: in a browser the member feed's
780
+ // host cannot be reached at all, so asking again can only fail again. Retrying on
781
+ // every Home visit spent one doomed round trip each time and filled the error log
782
+ // with something nobody can act on. Latch it once and stop asking; on a phone,
783
+ // where the call does work, this never trips.
784
+ if (isFeedSurfaceRefusal(error)) feedSurfaceRefused = true;
785
+ /* otherwise collapse silently, as before */
786
+ }
746
787
  })());
747
788
  }
748
789
  await Promise.all(jobs);
@@ -893,6 +934,7 @@ function baseState(): State {
893
934
  * let a stale failure clobber a newer success (returning `undefined` keeps the
894
935
  * current service state; see JSRuntimeBootstrap.applyNext). */
895
936
  let refreshGen = 0;
937
+
896
938
  /** Last successful featured list — catch paths read this when the action's
897
939
  * `state` snapshot was captured before a racing refresh painted products. */
898
940
  let lastFeaturedProducts: ProductFeature[] = [];
@@ -20,6 +20,39 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
20
20
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
21
21
  import { wixMediaUrl } from "@himalaya/wix-media";
22
22
 
23
+ /** Latched once the member feed is refused because of WHERE the app is running rather
24
+ * than because of the request — see the catch in `loadExtras`. Module scope, so it
25
+ * survives every refresh of this worker and is reset only by a fresh boot. */
26
+ let feedSurfaceRefused = false;
27
+
28
+ /** True for the one refusal that can never change while the app keeps running: the web
29
+ * bridge reporting that this host is not reachable from a browser at all. Deliberately
30
+ * narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
31
+ * this typed refusal is permanent. */
32
+ function isFeedSurfaceRefusal(error: unknown): boolean {
33
+ return /webwix_owner_only/.test(String((error as Error)?.message ?? error));
34
+ }
35
+
36
+ /** In-flight feed reads, keyed by the container they are for.
37
+ *
38
+ * The latch alone still let ONE duplicate through: two Home refreshes that overlap both read
39
+ * `feedSurfaceRefused === false` before the first rejection reaches the catch. Sharing the
40
+ * request closes that window — the second refresh awaits the first instead of sending its own,
41
+ * and both see the same rejection, so the latch is set exactly once.
42
+ *
43
+ * Keyed by container, not a single slot: `active` is the most recently active group, recomputed
44
+ * per refresh from a live `queryGroups`, so two overlapping refreshes can legitimately want
45
+ * DIFFERENT containers. A single shared promise would hand the second one the first one's feed. */
46
+ const feedInFlight = new Map<string, Promise<Awaited<ReturnType<typeof listFeedItems>>>>();
47
+
48
+ function feedFor(containerId: string, limit: number): Promise<Awaited<ReturnType<typeof listFeedItems>>> {
49
+ const pending = feedInFlight.get(containerId);
50
+ if (pending) return pending;
51
+ const started = listFeedItems(containerId, limit).finally(() => { feedInFlight.delete(containerId); });
52
+ feedInFlight.set(containerId, started);
53
+ return started;
54
+ }
55
+
23
56
  interface ProductFeature {
24
57
  id: string;
25
58
  name: string;
@@ -714,10 +747,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
714
747
  const response = await queryGroups({ paging: { limit: 12 } });
715
748
  const groups = (response.groups ?? []).filter((group) => group.id);
716
749
  if (verticals.includes("groups")) extras.groupCards = groups.slice(0, 6).map(groupCard);
717
- if (verticals.includes("feed") && groups.length) {
750
+ if (verticals.includes("feed") && groups.length && !feedSurfaceRefused) {
718
751
  const active = groups.slice().sort((a, b) =>
719
752
  new Date(b.lastActivityDate ?? 0).getTime() - new Date(a.lastActivityDate ?? 0).getTime())[0];
720
- const feed = await listFeedItems(active.id, 8);
753
+ const feed = await feedFor(active.id, 8);
721
754
  const profiles = feed.profiles ?? [];
722
755
  const items = (feed.feedItems ?? []).filter((item) => item.feedItemId);
723
756
  extras.feedHighlights = items.slice(0, 5).map((item) => {
@@ -742,7 +775,15 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
742
775
  }))
743
776
  .slice(0, 12);
744
777
  }
745
- } catch { /* collapse silently */ }
778
+ } catch (error) {
779
+ // This particular refusal is PERMANENT, not a blip: in a browser the member feed's
780
+ // host cannot be reached at all, so asking again can only fail again. Retrying on
781
+ // every Home visit spent one doomed round trip each time and filled the error log
782
+ // with something nobody can act on. Latch it once and stop asking; on a phone,
783
+ // where the call does work, this never trips.
784
+ if (isFeedSurfaceRefusal(error)) feedSurfaceRefused = true;
785
+ /* otherwise collapse silently, as before */
786
+ }
746
787
  })());
747
788
  }
748
789
  await Promise.all(jobs);
@@ -893,6 +934,7 @@ function baseState(): State {
893
934
  * let a stale failure clobber a newer success (returning `undefined` keeps the
894
935
  * current service state; see JSRuntimeBootstrap.applyNext). */
895
936
  let refreshGen = 0;
937
+
896
938
  /** Last successful featured list — catch paths read this when the action's
897
939
  * `state` snapshot was captured before a racing refresh painted products. */
898
940
  let lastFeaturedProducts: ProductFeature[] = [];
@@ -20,6 +20,39 @@ import { readSavedProducts, type SavedProduct } from "../_shared/himi-store-save
20
20
  import { readSavedIds } from "../_shared/himi-blog-saved.js";
21
21
  import { wixMediaUrl } from "@himalaya/wix-media";
22
22
 
23
+ /** Latched once the member feed is refused because of WHERE the app is running rather
24
+ * than because of the request — see the catch in `loadExtras`. Module scope, so it
25
+ * survives every refresh of this worker and is reset only by a fresh boot. */
26
+ let feedSurfaceRefused = false;
27
+
28
+ /** True for the one refusal that can never change while the app keeps running: the web
29
+ * bridge reporting that this host is not reachable from a browser at all. Deliberately
30
+ * narrower than `isBridgeUnavailable` — a 502/503 is an outage worth retrying, and only
31
+ * this typed refusal is permanent. */
32
+ function isFeedSurfaceRefusal(error: unknown): boolean {
33
+ return /webwix_owner_only/.test(String((error as Error)?.message ?? error));
34
+ }
35
+
36
+ /** In-flight feed reads, keyed by the container they are for.
37
+ *
38
+ * The latch alone still let ONE duplicate through: two Home refreshes that overlap both read
39
+ * `feedSurfaceRefused === false` before the first rejection reaches the catch. Sharing the
40
+ * request closes that window — the second refresh awaits the first instead of sending its own,
41
+ * and both see the same rejection, so the latch is set exactly once.
42
+ *
43
+ * Keyed by container, not a single slot: `active` is the most recently active group, recomputed
44
+ * per refresh from a live `queryGroups`, so two overlapping refreshes can legitimately want
45
+ * DIFFERENT containers. A single shared promise would hand the second one the first one's feed. */
46
+ const feedInFlight = new Map<string, Promise<Awaited<ReturnType<typeof listFeedItems>>>>();
47
+
48
+ function feedFor(containerId: string, limit: number): Promise<Awaited<ReturnType<typeof listFeedItems>>> {
49
+ const pending = feedInFlight.get(containerId);
50
+ if (pending) return pending;
51
+ const started = listFeedItems(containerId, limit).finally(() => { feedInFlight.delete(containerId); });
52
+ feedInFlight.set(containerId, started);
53
+ return started;
54
+ }
55
+
23
56
  interface ProductFeature {
24
57
  id: string;
25
58
  name: string;
@@ -714,10 +747,10 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
714
747
  const response = await queryGroups({ paging: { limit: 12 } });
715
748
  const groups = (response.groups ?? []).filter((group) => group.id);
716
749
  if (verticals.includes("groups")) extras.groupCards = groups.slice(0, 6).map(groupCard);
717
- if (verticals.includes("feed") && groups.length) {
750
+ if (verticals.includes("feed") && groups.length && !feedSurfaceRefused) {
718
751
  const active = groups.slice().sort((a, b) =>
719
752
  new Date(b.lastActivityDate ?? 0).getTime() - new Date(a.lastActivityDate ?? 0).getTime())[0];
720
- const feed = await listFeedItems(active.id, 8);
753
+ const feed = await feedFor(active.id, 8);
721
754
  const profiles = feed.profiles ?? [];
722
755
  const items = (feed.feedItems ?? []).filter((item) => item.feedItemId);
723
756
  extras.feedHighlights = items.slice(0, 5).map((item) => {
@@ -742,7 +775,15 @@ async function fetchExtrasSignal(arg: unknown): Promise<ExtrasSignal> {
742
775
  }))
743
776
  .slice(0, 12);
744
777
  }
745
- } catch { /* collapse silently */ }
778
+ } catch (error) {
779
+ // This particular refusal is PERMANENT, not a blip: in a browser the member feed's
780
+ // host cannot be reached at all, so asking again can only fail again. Retrying on
781
+ // every Home visit spent one doomed round trip each time and filled the error log
782
+ // with something nobody can act on. Latch it once and stop asking; on a phone,
783
+ // where the call does work, this never trips.
784
+ if (isFeedSurfaceRefusal(error)) feedSurfaceRefused = true;
785
+ /* otherwise collapse silently, as before */
786
+ }
746
787
  })());
747
788
  }
748
789
  await Promise.all(jobs);
@@ -893,6 +934,7 @@ function baseState(): State {
893
934
  * let a stale failure clobber a newer success (returning `undefined` keeps the
894
935
  * current service state; see JSRuntimeBootstrap.applyNext). */
895
936
  let refreshGen = 0;
937
+
896
938
  /** Last successful featured list — catch paths read this when the action's
897
939
  * `state` snapshot was captured before a racing refresh painted products. */
898
940
  let lastFeaturedProducts: ProductFeature[] = [];