@wix/himalaya-cli 0.903.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.
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +39 -21
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +1 -9
- package/dist/cli.mjs +74 -20
- package/guides/sdk-reference.json +5 -5
- package/package.json +2 -2
- package/templates/beauty-salon/dev/index.ts +6 -3
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/branded-lite/dev/index.ts +8 -4
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/community-club/dev/index.ts +4 -2
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/events-meetups/dev/index.ts +4 -2
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/gym-fitness/dev/index.ts +8 -4
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/nonprofit-cause/dev/index.ts +4 -2
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/podcast-creator/dev/index.ts +4 -2
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/portfolio-creative/dev/index.ts +6 -3
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/restaurant/dev/index.ts +4 -2
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/retail-store/dev/index.ts +4 -2
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +45 -3
- package/templates/yoga-studio/dev/index.ts +6 -3
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +45 -3
|
@@ -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
|
|
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 {
|
|
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[] = [];
|
|
@@ -18287,7 +18287,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
18287
18287
|
"open_url",
|
|
18288
18288
|
"calendar",
|
|
18289
18289
|
"share",
|
|
18290
|
-
"wix_cashier"
|
|
18290
|
+
"wix_cashier",
|
|
18291
|
+
"local_database"
|
|
18291
18292
|
],
|
|
18292
18293
|
"minBinaryVersion": "0.1.0",
|
|
18293
18294
|
"bundleVersion": "0.0.1"
|
|
@@ -19829,7 +19830,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
19829
19830
|
"contentType": "application/javascript+ts-bundle",
|
|
19830
19831
|
"declaredCapabilities": [
|
|
19831
19832
|
"wix_api",
|
|
19832
|
-
"share"
|
|
19833
|
+
"share",
|
|
19834
|
+
"local_database"
|
|
19833
19835
|
],
|
|
19834
19836
|
"minBinaryVersion": "0.1.0",
|
|
19835
19837
|
"bundleVersion": "0.0.1"
|
|
@@ -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
|
|
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 {
|
|
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[] = [];
|
|
@@ -21549,7 +21549,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
21549
21549
|
"contentType": "application/javascript+ts-bundle",
|
|
21550
21550
|
"declaredCapabilities": [
|
|
21551
21551
|
"wix_api",
|
|
21552
|
-
"share"
|
|
21552
|
+
"share",
|
|
21553
|
+
"local_database"
|
|
21553
21554
|
],
|
|
21554
21555
|
"minBinaryVersion": "0.1.0",
|
|
21555
21556
|
"bundleVersion": "0.0.1"
|
|
@@ -22479,7 +22480,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
22479
22480
|
"open_url",
|
|
22480
22481
|
"calendar",
|
|
22481
22482
|
"share",
|
|
22482
|
-
"wix_cashier"
|
|
22483
|
+
"wix_cashier",
|
|
22484
|
+
"local_database"
|
|
22483
22485
|
],
|
|
22484
22486
|
"minBinaryVersion": "0.1.0",
|
|
22485
22487
|
"bundleVersion": "0.0.1"
|
|
@@ -24021,7 +24023,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
24021
24023
|
"contentType": "application/javascript+ts-bundle",
|
|
24022
24024
|
"declaredCapabilities": [
|
|
24023
24025
|
"wix_api",
|
|
24024
|
-
"share"
|
|
24026
|
+
"share",
|
|
24027
|
+
"local_database"
|
|
24025
24028
|
],
|
|
24026
24029
|
"minBinaryVersion": "0.1.0",
|
|
24027
24030
|
"bundleVersion": "0.0.1"
|
|
@@ -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
|
|
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 {
|
|
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[] = [];
|
|
@@ -16005,7 +16005,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
16005
16005
|
"open_url",
|
|
16006
16006
|
"calendar",
|
|
16007
16007
|
"share",
|
|
16008
|
-
"wix_cashier"
|
|
16008
|
+
"wix_cashier",
|
|
16009
|
+
"local_database"
|
|
16009
16010
|
],
|
|
16010
16011
|
"minBinaryVersion": "0.1.0",
|
|
16011
16012
|
"bundleVersion": "0.0.1"
|
|
@@ -17547,7 +17548,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
17547
17548
|
"contentType": "application/javascript+ts-bundle",
|
|
17548
17549
|
"declaredCapabilities": [
|
|
17549
17550
|
"wix_api",
|
|
17550
|
-
"share"
|
|
17551
|
+
"share",
|
|
17552
|
+
"local_database"
|
|
17551
17553
|
],
|
|
17552
17554
|
"minBinaryVersion": "0.1.0",
|
|
17553
17555
|
"bundleVersion": "0.0.1"
|
|
@@ -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
|
|
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 {
|
|
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[] = [];
|
|
@@ -15098,7 +15098,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
15098
15098
|
"contentType": "application/javascript+ts-bundle",
|
|
15099
15099
|
"declaredCapabilities": [
|
|
15100
15100
|
"wix_api",
|
|
15101
|
-
"share"
|
|
15101
|
+
"share",
|
|
15102
|
+
"local_database"
|
|
15102
15103
|
],
|
|
15103
15104
|
"minBinaryVersion": "0.1.0",
|
|
15104
15105
|
"bundleVersion": "0.0.1"
|
|
@@ -15988,7 +15989,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
15988
15989
|
"contentType": "application/javascript+ts-bundle",
|
|
15989
15990
|
"declaredCapabilities": [
|
|
15990
15991
|
"wix_api",
|
|
15991
|
-
"share"
|
|
15992
|
+
"share",
|
|
15993
|
+
"local_database"
|
|
15992
15994
|
],
|
|
15993
15995
|
"minBinaryVersion": "0.1.0",
|
|
15994
15996
|
"bundleVersion": "0.0.1"
|
|
@@ -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
|
|
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 {
|
|
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[] = [];
|
|
@@ -22840,7 +22840,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
22840
22840
|
"contentType": "application/javascript+ts-bundle",
|
|
22841
22841
|
"declaredCapabilities": [
|
|
22842
22842
|
"wix_api",
|
|
22843
|
-
"share"
|
|
22843
|
+
"share",
|
|
22844
|
+
"local_database"
|
|
22844
22845
|
],
|
|
22845
22846
|
"minBinaryVersion": "0.1.0",
|
|
22846
22847
|
"bundleVersion": "0.0.1"
|
|
@@ -25234,7 +25235,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
25234
25235
|
"open_url",
|
|
25235
25236
|
"calendar",
|
|
25236
25237
|
"share",
|
|
25237
|
-
"wix_cashier"
|
|
25238
|
+
"wix_cashier",
|
|
25239
|
+
"local_database"
|
|
25238
25240
|
],
|
|
25239
25241
|
"minBinaryVersion": "0.1.0",
|
|
25240
25242
|
"bundleVersion": "0.0.1"
|
|
@@ -26776,7 +26778,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
|
|
|
26776
26778
|
"contentType": "application/javascript+ts-bundle",
|
|
26777
26779
|
"declaredCapabilities": [
|
|
26778
26780
|
"wix_api",
|
|
26779
|
-
"share"
|
|
26781
|
+
"share",
|
|
26782
|
+
"local_database"
|
|
26780
26783
|
],
|
|
26781
26784
|
"minBinaryVersion": "0.1.0",
|
|
26782
26785
|
"bundleVersion": "0.0.1"
|
|
@@ -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
|
|
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 {
|
|
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[] = [];
|