@wix/himalaya-cli 0.1544.0 → 0.1546.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/core/sdk/agent/src/index.d.ts +5 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/state/src/index.d.ts +14 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/state/src/read.d.ts +60 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/activity-counters-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/ai-site-chat/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/analytics/src/index.d.ts +42 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/async-jobs/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/automations-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/badges-v2/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/billing-tax-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/extensions.d.ts +5 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/bookings-v2/src/extensions.d.ts +4 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/bookings-v2/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/business-feed/src/index.d.ts +11 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/business-tools-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/chat-web/src/index.d.ts +22 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/contacts/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/coupons/src/index.d.ts +24 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/crm-tasks/src/index.d.ts +11 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/dashboard-setup/src/index.d.ts +11 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/data-collections-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/donations/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/ecom/src/extensions.d.ts +6 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/ecom/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/email-marketing-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +24 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/faq-v2/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/forms/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/forum/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/genie-chat/src/index.d.ts +11 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/gift-cards/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/inbox/src/extensions.d.ts +2 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/inbox/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/invoices/src/index.d.ts +24 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/index.d.ts +28 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/marketing-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-feed-viewer/src/index.d.ts +23 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +25 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/new-releases/src/index.d.ts +23 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/notification-preferences/src/index.d.ts +23 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/notifications-v3/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/online-programs/src/index.d.ts +25 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/portfolio/src/index.d.ts +25 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/pricing-plans/src/index.d.ts +28 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/redirects-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/restaurants/src/extensions.d.ts +2 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/restaurants/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/reviews/src/index.d.ts +24 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/seo-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/site-list/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/site-media/src/index.d.ts +11 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/social-feed/src/index.d.ts +23 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-collections/src/display.d.ts +3 -2
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-collections/src/index.d.ts +27 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/catalog.d.ts +7 -7
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/display.d.ts +4 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/extensions.d.ts +2 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/table-reservations/src/index.d.ts +25 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/users/src/index.d.ts +23 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/wix-data/src/index.d.ts +27 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/workflows-v1/src/index.d.ts +19 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk-auto/_shared/runtime.generated.d.ts +8 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/checkout.mjs +12 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +36 -11
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/state.mjs +206 -12
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-activity-counters.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-ai-site-chat.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-analytics.mjs +21 -7
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-async-jobs.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-automations.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-badges.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-billing-tax.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-blog.mjs +31 -14
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-bookings.mjs +26 -12
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-business-tools.mjs +20 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-chat-web.mjs +29 -7
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +28 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-contacts.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-coupons.mjs +14 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-data-collections.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-data.mjs +15 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-donations.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-ecom.mjs +33 -16
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-email-marketing.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +14 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-faq.mjs +20 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-forms.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-forum.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-gift-cards.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-inbox.mjs +21 -7
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-invoices.mjs +14 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +16 -8
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-marketing.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members-feed-viewer.mjs +21 -10
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +17 -8
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-new-releases.mjs +17 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-notification-preferences.mjs +20 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-notifications.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-online-programs.mjs +16 -8
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-portfolio.mjs +16 -8
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-pricing-plans.mjs +22 -14
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-redirects.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-restaurants.mjs +22 -7
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-reviews.mjs +14 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-rich-content-editor.mjs +3 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-seo.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-site-list.mjs +19 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-social-feed.mjs +15 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores-collections.mjs +20 -10
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores.mjs +116 -83
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-table-reservations.mjs +16 -8
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-users.mjs +15 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +9 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-workflows.mjs +19 -5
- package/dist/cli.mjs +1002 -806
- package/dist/preview-assets.generated.bin +0 -0
- package/fixtures/wix-canned.json +8 -8
- package/guides/dynamic-list.service.ts +38 -29
- package/guides/form-submit.service.ts +2 -1
- package/guides/himi-json-output.md +29 -0
- package/guides/iap-products.md +49 -4
- package/guides/index.json +6 -0
- package/guides/offline-reading.md +63 -0
- package/guides/recipe-dynamic-list.md +2 -0
- package/guides/recipe-four-state.md +1 -0
- package/guides/sdk-reference.json +225 -99
- package/guides/testing-himi-apps.md +2 -0
- package/guides/tier-5-add-a-screen.md +2 -0
- package/package.json +2 -2
- package/templates/beauty-salon/dev/index.ts +4 -3
- package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/beauty-salon/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/beauty-salon/tier5-src/himi-branded-lite-orders/index.ts +3 -0
- package/templates/beauty-salon-owner/dev/index.ts +19 -19
- package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/beauty-salon-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/branded-lite/dev/index.ts +38 -20
- package/templates/branded-lite/tier5-src/_shared/collection-query-legacy.ts +194 -0
- package/templates/branded-lite/tier5-src/_shared/collection-query.ts +158 -99
- package/templates/branded-lite/tier5-src/_shared/collection-service.ts +31 -11
- package/templates/branded-lite/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/branded-lite/tier5-src/_shared/read-service.ts +255 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/branded-lite/tier5-src/himi-branded-lite-orders/index.ts +3 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-shop/index.ts +202 -159
- package/templates/branded-lite/tier5-src/himi-store-product/index.ts +130 -201
- package/templates/community-club/dev/index.ts +4 -4
- package/templates/community-club/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/community-club/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/community-club-owner/dev/index.ts +19 -19
- package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/community-club-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/events-meetups/dev/index.ts +3 -3
- package/templates/events-meetups/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/events-meetups/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/events-meetups-owner/dev/index.ts +17 -17
- package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/events-meetups-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/gym-fitness/dev/index.ts +5 -4
- package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/gym-fitness/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/gym-fitness/tier5-src/himi-branded-lite-orders/index.ts +3 -0
- package/templates/gym-fitness-owner/dev/index.ts +20 -20
- package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/gym-fitness-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/nonprofit-cause/dev/index.ts +4 -4
- package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/nonprofit-cause/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/nonprofit-cause-owner/dev/index.ts +18 -18
- package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/owner-lite/dev/index.ts +20 -20
- package/templates/owner-lite/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/owner-lite/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/podcast-creator/dev/index.ts +4 -4
- package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/podcast-creator/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/podcast-creator-owner/dev/index.ts +19 -19
- package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/podcast-creator-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/portfolio-creative/dev/index.ts +5 -4
- package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/portfolio-creative/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/portfolio-creative/tier5-src/himi-branded-lite-orders/index.ts +3 -0
- package/templates/portfolio-creative-owner/dev/index.ts +19 -19
- package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/portfolio-creative-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/restaurant/dev/index.ts +3 -3
- package/templates/restaurant/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/restaurant/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/restaurant-owner/dev/index.ts +20 -20
- package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/restaurant-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/retail-store/dev/index.ts +19 -1
- package/templates/retail-store/tier5-src/_shared/collection-query-legacy.ts +194 -0
- package/templates/retail-store/tier5-src/_shared/collection-query.ts +158 -99
- package/templates/retail-store/tier5-src/_shared/collection-service.ts +31 -11
- package/templates/retail-store/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/retail-store/tier5-src/_shared/read-service.ts +255 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/retail-store/tier5-src/himi-branded-lite-orders/index.ts +3 -0
- package/templates/retail-store/tier5-src/himi-branded-lite-shop/index.ts +202 -159
- package/templates/retail-store/tier5-src/himi-store-product/index.ts +130 -201
- package/templates/retail-store-owner/dev/index.ts +18 -18
- package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/retail-store-owner/tier5-src/_shared/owner-module-data.ts +1 -1
- package/templates/yoga-studio/dev/index.ts +4 -4
- package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/yoga-studio/tier5-src/_shared/iap-presentation.ts +7 -3
- package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +1 -1
- package/templates/yoga-studio-owner/dev/index.ts +18 -18
- package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +6 -9
- package/templates/yoga-studio-owner/tier5-src/_shared/owner-module-data.ts +1 -1
|
@@ -10,28 +10,33 @@
|
|
|
10
10
|
//
|
|
11
11
|
// Opened with `category` = a category id or name; absent ⇒ All.
|
|
12
12
|
import { getCurrentCart } from "@himalaya/wix-ecom";
|
|
13
|
-
import {
|
|
13
|
+
import { createReadController, initialReadStatus, connectivity, type ReadMetadata, type ReadStatus } from "@himalaya/state";
|
|
14
|
+
import type { WixCallOptions } from "@himalaya/wix-stores";
|
|
14
15
|
import { searchStoreProducts, type StoreProduct } from "../_shared/himi-store.js";
|
|
15
16
|
import { withIapPresentation } from "../_shared/iap-presentation.js";
|
|
16
17
|
import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
|
|
17
18
|
import { loadStoreCategories, type StoreCategoryCard } from "../_shared/himi-store-categories.js";
|
|
18
19
|
import {
|
|
19
20
|
FACETS_BOOT, PAGE_SIZE, applyFacets, countText, currencyOf, facetActions, idOf, option, readViewPrefs, selected, toRows, toggleSaved,
|
|
20
|
-
type FacetState, type Option, type
|
|
21
|
+
type FacetState, type Option, type Row,
|
|
21
22
|
} from "../_shared/himi-store-facets.js";
|
|
22
23
|
|
|
23
24
|
const ALL = "all";
|
|
24
25
|
const PAGE = 100;
|
|
25
26
|
const MAX_PRODUCTS = 500;
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
|
|
28
29
|
interface S extends FacetState {
|
|
30
|
+
readStatus: ReadStatus;
|
|
31
|
+
readMessage: string;
|
|
32
|
+
noReadMessage: boolean;
|
|
33
|
+
hasReadMessage: boolean;
|
|
29
34
|
status: "loading" | "ready" | "error";
|
|
30
35
|
isLoading: boolean;
|
|
31
36
|
notLoading: boolean;
|
|
32
37
|
isErrored: boolean;
|
|
33
38
|
notErrored: boolean;
|
|
34
|
-
/**
|
|
39
|
+
/** Response provenance comes from the shared policy-controlled read adapter. */
|
|
35
40
|
fromCache: boolean;
|
|
36
41
|
noOffline: boolean;
|
|
37
42
|
categorySelected: string;
|
|
@@ -58,14 +63,20 @@ let pendingCategoriesRead: Promise<StoreCategoryCard[]> | undefined;
|
|
|
58
63
|
/** Cart count can land while the first product scope is still pending. */
|
|
59
64
|
let loadedCartCount: number | undefined;
|
|
60
65
|
/** Each category's fetched rows, so switching back to a chip is instant. */
|
|
61
|
-
|
|
66
|
+
interface ScopeSnapshot {
|
|
67
|
+
rows: Row[]; complete: boolean; loading: boolean; fromCache?: boolean;
|
|
68
|
+
currency?: string; total?: number; cursor?: string; metadata: ReadMetadata;
|
|
69
|
+
}
|
|
70
|
+
const scopes = new Map<string, ScopeSnapshot>();
|
|
71
|
+
const readers = new Map<string, ReturnType<typeof createReadController<ScopeSnapshot>>>();
|
|
72
|
+
const publications = new Map<string, Promise<void>>();
|
|
73
|
+
const publicationVersions = new Map<string, number>();
|
|
74
|
+
const restFlights = new WeakSet<ScopeSnapshot>();
|
|
62
75
|
const pendingScopes = new Map<string, number>();
|
|
63
76
|
// A pull-to-refresh that loses the foreground generation still represents fresh data the
|
|
64
77
|
// shopper asked for. Keep that intent until a current request for the scope lands, so a
|
|
65
78
|
// quick trip away and back cannot present the old cached scope as the refresh result.
|
|
66
79
|
const pendingRefreshScopes = new Map<string, number>();
|
|
67
|
-
let cacheWriteGeneration = 0;
|
|
68
|
-
let cacheWriteQueue: Promise<void> = Promise.resolve();
|
|
69
80
|
/** Bumped on every scope change; a stale first page must never write over a newer scope. */
|
|
70
81
|
let generation = 0;
|
|
71
82
|
/** The category the screen shows. Module state, not action state: the boot load and the
|
|
@@ -79,20 +90,6 @@ let pendingAuxiliary: { cartCount: number; version: number } | undefined;
|
|
|
79
90
|
let auxiliaryVersion = 0;
|
|
80
91
|
let lastRouteActivation: string | undefined;
|
|
81
92
|
let routeCategoryConsumed = false;
|
|
82
|
-
let legacyCacheFacetResetDone = false;
|
|
83
|
-
const LEGACY_CACHE_FACETS_KEY = "himi-store:legacy-cache-facets-reset:v1";
|
|
84
|
-
|
|
85
|
-
function shouldResetLegacyCacheFacets(): boolean {
|
|
86
|
-
if (legacyCacheFacetResetDone) return false;
|
|
87
|
-
legacyCacheFacetResetDone = true;
|
|
88
|
-
try {
|
|
89
|
-
const store = (globalThis as { himalaya?: { storage?: { getItem(key: string): string | null; setItem(key: string, value: string): void } } }).himalaya?.storage;
|
|
90
|
-
if (store?.getItem(LEGACY_CACHE_FACETS_KEY) === "1") return false;
|
|
91
|
-
store?.setItem(LEGACY_CACHE_FACETS_KEY, "1");
|
|
92
|
-
} catch { /* one reset this boot even when storage is unavailable */ }
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
93
|
function input(arg?: unknown): { category?: string; categoryId?: string; categoryActivation?: string } {
|
|
97
94
|
const fromArg = arg as { category?: unknown; categoryId?: unknown; categoryActivation?: unknown } | undefined;
|
|
98
95
|
const route = (fromArg && typeof fromArg === "object" && (
|
|
@@ -113,6 +110,7 @@ function matchCategory(want: string): string | undefined {
|
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
async function cartItemCount(): Promise<number | undefined> {
|
|
113
|
+
if (!connectivity().online) return undefined;
|
|
116
114
|
try {
|
|
117
115
|
const cart = await getCurrentCart();
|
|
118
116
|
return (cart.lineItems ?? []).reduce((n, li) => n + (li.quantity ?? 1), 0);
|
|
@@ -125,6 +123,7 @@ async function cartItemCount(): Promise<number | undefined> {
|
|
|
125
123
|
* supersede that refresh's category discovery. Cache reads share an in-flight catalog read;
|
|
126
124
|
* only the newest network read can replace the worker's current categories. */
|
|
127
125
|
function readCategories(fresh: boolean): Promise<StoreCategoryCard[]> {
|
|
126
|
+
if (!connectivity().online) { categoriesAuthoritative = false; return Promise.resolve(categories); }
|
|
128
127
|
// A network read already in flight is fresh enough for a refresh that starts before it
|
|
129
128
|
// lands. Sharing it avoids discarding a good older response when a redundant newer read
|
|
130
129
|
// fails, and lets a route load consume the same authoritative list.
|
|
@@ -169,52 +168,6 @@ function reconcileCategories(): { invalidatedSelection: boolean; intentVersion:
|
|
|
169
168
|
return { invalidatedSelection, intentVersion: categoryIntentVersion };
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
type CachedCard = ProductCard & {
|
|
173
|
-
cachePrice?: number; cacheCreated?: string; cacheName?: string;
|
|
174
|
-
cacheOptions?: Record<string, string[]>; cacheCurrency?: string; legacyCache?: boolean;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
/** Migrate the former himi-store-shop row shape in memory: the entity name stayed the same. */
|
|
178
|
-
function cachedCard(value: unknown): CachedCard | undefined {
|
|
179
|
-
const raw = value as (ProductCard & { subtitle?: string; initial?: string }) | undefined;
|
|
180
|
-
if (!raw?.id || typeof raw.name !== "string") return undefined;
|
|
181
|
-
if (typeof raw.noTagRow === "boolean" && typeof raw.notOnSale === "boolean") return raw as CachedCard;
|
|
182
|
-
const image = typeof raw.image === "string" ? raw.image : "";
|
|
183
|
-
const price = typeof raw.commercePrice === "string" && raw.commercePrice ? raw.commercePrice : raw.subtitle ?? "";
|
|
184
|
-
return {
|
|
185
|
-
id: raw.id, name: raw.name, image, hasImage: typeof raw.hasImage === "boolean" ? raw.hasImage : !!image,
|
|
186
|
-
noImage: typeof raw.noImage === "boolean" ? raw.noImage : !image, initial: raw.initial ?? raw.name.slice(0, 1).toUpperCase(),
|
|
187
|
-
price, wasPrice: "", onSale: false, notOnSale: true, saved: false, notSaved: true,
|
|
188
|
-
swatch1: "", swatch2: "", swatch3: "", swatch4: "", swatch5: "",
|
|
189
|
-
swatch1Hidden: true, swatch2Hidden: true, swatch3Hidden: true, swatch4Hidden: true, swatch5Hidden: true,
|
|
190
|
-
noSwatches: true, noTagRow: true, commercePrice: raw.commercePrice ?? price,
|
|
191
|
-
detailScreenId: raw.detailScreenId || "himi_product", legacyCache: true,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async function readCachedCards(): Promise<CachedCard[]> {
|
|
196
|
-
try {
|
|
197
|
-
return ((await getAll(ENTITY)) as unknown[]).map(cachedCard).filter((c): c is CachedCard => !!c);
|
|
198
|
-
} catch { return []; }
|
|
199
|
-
}
|
|
200
|
-
function writeCachedCards(list: Row[], snapshotCurrency?: string): void {
|
|
201
|
-
const writeGeneration = ++cacheWriteGeneration;
|
|
202
|
-
const snapshot = list.map(r => ({
|
|
203
|
-
...r.card, subtitle: r.card.price,
|
|
204
|
-
cachePrice: r.price, cacheCreated: r.created, cacheName: r.name, cacheOptions: r.options, cacheCurrency: snapshotCurrency,
|
|
205
|
-
} as unknown as DbEntity));
|
|
206
|
-
cacheWriteQueue = cacheWriteQueue.then(async () => {
|
|
207
|
-
if (writeGeneration !== cacheWriteGeneration) return;
|
|
208
|
-
const cached = await getAll(ENTITY);
|
|
209
|
-
if (writeGeneration !== cacheWriteGeneration) return;
|
|
210
|
-
// Once a snapshot starts writing, finish it. A later complete snapshot queues behind
|
|
211
|
-
// it; switching or refreshing categories cannot strand a half-replaced catalog.
|
|
212
|
-
const keep = new Set(snapshot.map(row => row.id));
|
|
213
|
-
for (const row of snapshot) await upsert(ENTITY, row);
|
|
214
|
-
for (const item of cached) if (!keep.has(item.id)) await remove(ENTITY, item.id);
|
|
215
|
-
}).catch(() => { /* capability not granted → online-only */ });
|
|
216
|
-
}
|
|
217
|
-
|
|
218
171
|
function derive(s: S): S {
|
|
219
172
|
// The field keeps exactly what the shopper typed (a trailing space included); the match trims.
|
|
220
173
|
const typed = s.query.trim();
|
|
@@ -222,6 +175,9 @@ function derive(s: S): S {
|
|
|
222
175
|
const next = applyFacets(s, q ? rows.filter((r) => r.name.includes(q)) : rows, currency);
|
|
223
176
|
const n = next.matched;
|
|
224
177
|
const ready = s.status === "ready";
|
|
178
|
+
const readMessage = !s.readStatus.hasSnapshot && s.readStatus.offline
|
|
179
|
+
? "This content isn’t available offline. Connect to load it."
|
|
180
|
+
: s.readStatus.error || "";
|
|
225
181
|
const choices = [{ id: ALL, label: copyParam("shop.allChip", "All") }, ...categories.map((c) => ({ id: c.id, label: c.name }))];
|
|
226
182
|
return {
|
|
227
183
|
...next,
|
|
@@ -229,7 +185,9 @@ function derive(s: S): S {
|
|
|
229
185
|
notLoading: s.status !== "loading",
|
|
230
186
|
isErrored: s.status === "error",
|
|
231
187
|
notErrored: s.status !== "error",
|
|
232
|
-
|
|
188
|
+
// The shared shell owns offline/stale messages; this former local-DB note stays hidden.
|
|
189
|
+
noOffline: true,
|
|
190
|
+
readMessage, noReadMessage: !readMessage, hasReadMessage: !!readMessage,
|
|
233
191
|
categoryOptions: choices.map((c) => option(c, c.id === s.categorySelected)),
|
|
234
192
|
hideCategories: categories.length === 0,
|
|
235
193
|
countLabel: ready ? countText(n) : "",
|
|
@@ -244,8 +202,94 @@ function derive(s: S): S {
|
|
|
244
202
|
};
|
|
245
203
|
}
|
|
246
204
|
|
|
247
|
-
|
|
248
|
-
|
|
205
|
+
/** Every content/price projection reads the active controller, not an older load's skeleton. */
|
|
206
|
+
function currentReadState(state: S): S {
|
|
207
|
+
const controller = readers.get(selectedCategory);
|
|
208
|
+
if (!controller) return derive(state);
|
|
209
|
+
const readStatus = controller.getStatus();
|
|
210
|
+
return derive({ ...state, readStatus, categorySelected: selectedCategory, scopeGen: generation,
|
|
211
|
+
fromCache: readStatus.fromCache || readStatus.offline && readStatus.hasSnapshot,
|
|
212
|
+
status: readStatus.hasSnapshot ? "ready" : readStatus.error || readStatus.offline ? "error" : "loading" });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function fetchPage(category: string, cursor?: string, options?: WixCallOptions) {
|
|
216
|
+
return searchStoreProducts({ limit: PAGE, ...(category !== ALL ? { categoryId: category } : {}), ...(cursor ? { cursor } : {}) }, options);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function combinedMetadata(a: ReadMetadata | undefined, b: ReadMetadata): ReadMetadata {
|
|
220
|
+
return !a ? b : { source: b.source, fetchedAt: Math.min(a.fetchedAt, b.fetchedAt),
|
|
221
|
+
cacheable: a.cacheable && b.cacheable,
|
|
222
|
+
expiresAt: a.expiresAt === undefined || b.expiresAt === undefined ? undefined : Math.min(a.expiresAt, b.expiresAt) };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function readPolicyScope(category: string, cache: 'only-if-cached' | 'reload', depth: number): Promise<{ data: ScopeSnapshot; metadata: ReadMetadata }> {
|
|
226
|
+
const list: Row[] = [], seen = new Set<string>(), cursors = new Set<string>();
|
|
227
|
+
let metadata: ReadMetadata | undefined, cursor: string | undefined, scopeCurrency: string | undefined, total: number | undefined;
|
|
228
|
+
let hasPage = false;
|
|
229
|
+
do {
|
|
230
|
+
let pageMetadata: ReadMetadata | undefined;
|
|
231
|
+
let res: Awaited<ReturnType<typeof fetchPage>>;
|
|
232
|
+
try { res = await fetchPage(category, cursor, { cache, onCacheResult: value => { pageMetadata = value; } }); }
|
|
233
|
+
catch (error) {
|
|
234
|
+
// A later cache miss is an honest partial snapshot; it never sends a request.
|
|
235
|
+
if (cache === 'only-if-cached' && hasPage && /OFFLINE_CACHE_MISS/.test(String(error))) break;
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
if (!pageMetadata) throw new Error('READ_LIFECYCLE_UNSUPPORTED: update the Himalaya runtime');
|
|
239
|
+
hasPage = true; metadata = combinedMetadata(metadata, pageMetadata);
|
|
240
|
+
const products = res.products ?? [];
|
|
241
|
+
scopeCurrency ??= products.map(currencyOf).find(Boolean);
|
|
242
|
+
total = Math.max(total ?? 0, res.pagingMetadata?.total ?? 0);
|
|
243
|
+
const added = toRows(products).filter(row => { if (seen.has(row.card.id)) return false; seen.add(row.card.id); return true; });
|
|
244
|
+
list.push(...added.slice(0, MAX_PRODUCTS - list.length));
|
|
245
|
+
cursor = res.pagingMetadata?.cursors?.next || undefined;
|
|
246
|
+
if (cursor && (!added.length || cursors.has(cursor) || cursors.size >= 100)) throw new Error('Product pagination did not advance');
|
|
247
|
+
if (cursor) cursors.add(cursor);
|
|
248
|
+
// A refresh stages fresh cursors to the depth the shopper has already loaded.
|
|
249
|
+
depth = Math.max(depth, cache === 'reload' ? scopes.get(category)?.rows.length ?? 0 : 0);
|
|
250
|
+
} while (cursor && list.length < Math.min(depth, MAX_PRODUCTS));
|
|
251
|
+
const data: ScopeSnapshot = { rows: list, complete: !cursor && list.length >= Math.min(total ?? 0, MAX_PRODUCTS),
|
|
252
|
+
loading: !!cursor, cursor, currency: scopeCurrency, total, fromCache: cache === 'only-if-cached', metadata: metadata! };
|
|
253
|
+
return { data, metadata: metadata! };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function publishRead(category: string, data: ScopeSnapshot | undefined, status: ReadStatus): void {
|
|
257
|
+
if (data) { data.fromCache = status.fromCache || status.offline; scopes.set(category, data); }
|
|
258
|
+
else scopes.delete(category);
|
|
259
|
+
if (category !== selectedCategory) return;
|
|
260
|
+
rows = data?.rows ?? []; currency = data?.currency;
|
|
261
|
+
const gen = generation, version = (publicationVersions.get(category) ?? 0) + 1;
|
|
262
|
+
publicationVersions.set(category, version);
|
|
263
|
+
const publication = (async () => {
|
|
264
|
+
await committed(gen);
|
|
265
|
+
if (gen !== generation || category !== selectedCategory || publicationVersions.get(category) !== version) return;
|
|
266
|
+
await svc.readChanged({ gen, category, readStatus: status });
|
|
267
|
+
})();
|
|
268
|
+
publications.set(category, publication);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function clearOnReadDenial(error: unknown, category: string): boolean {
|
|
272
|
+
if (!/HTTP (401|403)|PRINCIPAL_CHANGED/.test(String(error))) return false;
|
|
273
|
+
auxiliaryVersion++; categoryReadVersion++;
|
|
274
|
+
categories = []; categoriesAuthoritative = false; pendingCategoriesRead = undefined;
|
|
275
|
+
loadedCartCount = 0; pendingAuxiliary = undefined;
|
|
276
|
+
pendingScopes.clear(); pendingRefreshScopes.clear();
|
|
277
|
+
for (const [key, controller] of readers) if (key !== category) controller.invalidate();
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function reader(category: string) {
|
|
282
|
+
let value = readers.get(category);
|
|
283
|
+
if (!value) {
|
|
284
|
+
value = createReadController<ScopeSnapshot>({
|
|
285
|
+
cached: key => readPolicyScope(key, 'only-if-cached', MAX_PRODUCTS),
|
|
286
|
+
fetch: key => readPolicyScope(key, 'reload', scopes.get(key)?.rows.length ?? 0),
|
|
287
|
+
publish: (data, status) => publishRead(category, data, status),
|
|
288
|
+
clearOnError: error => clearOnReadDenial(error, category),
|
|
289
|
+
});
|
|
290
|
+
readers.set(category, value);
|
|
291
|
+
}
|
|
292
|
+
return value;
|
|
249
293
|
}
|
|
250
294
|
|
|
251
295
|
/** Fetch the rest of a scope in the background, a page at a time. A page that lands while
|
|
@@ -254,16 +298,22 @@ async function fetchRest(category: string, cursor: string): Promise<void> {
|
|
|
254
298
|
let next: string | undefined = cursor;
|
|
255
299
|
let failed = false;
|
|
256
300
|
const scope = scopes.get(category);
|
|
257
|
-
if (!scope) return;
|
|
301
|
+
if (!scope || restFlights.has(scope)) return;
|
|
302
|
+
restFlights.add(scope);
|
|
303
|
+
const controller = reader(category);
|
|
304
|
+
let revision = controller.getRevision();
|
|
305
|
+
const current = () => scopes.get(category) === scope && controller.getRevision() === revision;
|
|
258
306
|
const seenCursors = new Set<string>();
|
|
259
307
|
const seenProducts = new Set(scope.rows.map(row => row.card.id));
|
|
260
308
|
try {
|
|
261
309
|
while (next && scope.rows.length < MAX_PRODUCTS) {
|
|
262
|
-
if (
|
|
310
|
+
if (!current()) return;
|
|
263
311
|
if (seenCursors.has(next) || seenCursors.size >= 100) throw new Error("Product pagination did not advance");
|
|
264
312
|
seenCursors.add(next);
|
|
265
|
-
|
|
266
|
-
|
|
313
|
+
let metadata: ReadMetadata | undefined;
|
|
314
|
+
const res = await fetchPage(category, next, { cache: 'reload', onCacheResult: value => { metadata = value; } });
|
|
315
|
+
if (!metadata) throw new Error('READ_LIFECYCLE_UNSUPPORTED');
|
|
316
|
+
if (!current()) return;
|
|
267
317
|
const added = toRows(res.products ?? []).filter(row => {
|
|
268
318
|
if (seenProducts.has(row.card.id)) return false;
|
|
269
319
|
seenProducts.add(row.card.id);
|
|
@@ -273,14 +323,28 @@ async function fetchRest(category: string, cursor: string): Promise<void> {
|
|
|
273
323
|
next = res.pagingMetadata?.cursors?.next ?? undefined;
|
|
274
324
|
if (!added.length && next) throw new Error("Product pagination made no progress");
|
|
275
325
|
scope.rows.push(...added.slice(0, MAX_PRODUCTS - scope.rows.length));
|
|
276
|
-
|
|
326
|
+
scope.metadata = combinedMetadata(scope.metadata, metadata);
|
|
327
|
+
scope.cursor = next;
|
|
328
|
+
controller.replace({ data: scope, metadata: scope.metadata }, category);
|
|
329
|
+
revision = controller.getRevision();
|
|
330
|
+
if (category === selectedCategory) await publications.get(category);
|
|
277
331
|
if (!next && seenProducts.size < Math.min(scope.total, MAX_PRODUCTS)) throw new Error("Product pagination is incomplete");
|
|
278
332
|
}
|
|
279
|
-
} catch {
|
|
280
|
-
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (!current()) return;
|
|
335
|
+
failed = true;
|
|
336
|
+
if (clearOnReadDenial(error, category)) reader(category).invalidate();
|
|
337
|
+
else if (scopes.get(category) === scope) publishRead(category, scope, {
|
|
338
|
+
...reader(category).getStatus(), stale: true, refreshing: false, error: "Couldn’t refresh. Content may be outdated.",
|
|
339
|
+
});
|
|
340
|
+
} finally {
|
|
341
|
+
restFlights.delete(scope);
|
|
342
|
+
if (!current() && scopes.get(category) === scope) scope.loading = false;
|
|
343
|
+
}
|
|
344
|
+
if (!current()) return;
|
|
281
345
|
scope.complete = !failed;
|
|
282
346
|
scope.loading = false;
|
|
283
|
-
|
|
347
|
+
|
|
284
348
|
}
|
|
285
349
|
|
|
286
350
|
/** Wait, a bounded number of turns, until the action that started fetch `gen` has committed
|
|
@@ -311,77 +375,17 @@ async function publishAuxiliaryIfReady(): Promise<void> {
|
|
|
311
375
|
/** Fetch `category`'s first page and hand it to `scopeLanded`. The action that started the
|
|
312
376
|
* fetch has already returned: a search, a sort or another chip the shopper picks meanwhile
|
|
313
377
|
* is current state, and a state captured before this await must never overwrite it. */
|
|
314
|
-
async function fetchScope(category: string, gen: number,
|
|
315
|
-
// A pull to refresh keeps what is on screen until the new page is in hand.
|
|
316
|
-
const shown = refresh ? scopes.get(category) : undefined;
|
|
378
|
+
async function fetchScope(category: string, gen: number, _refresh = false): Promise<void> {
|
|
317
379
|
try {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const total = res.pagingMetadata?.total;
|
|
325
|
-
// Keep partial live rows visible, but neither cache them nor suppress a later retry.
|
|
326
|
-
const complete = !cursor && new Set(loaded.map(row => row.card.id)).size >= Math.min(total ?? 0, MAX_PRODUCTS);
|
|
327
|
-
const scope = { rows: loaded, complete, loading: !!cursor, fromCache: false, currency: products.map(currencyOf).find(Boolean), total };
|
|
328
|
-
// Fresh data for this scope: every other scope refetches when the shopper next opens it.
|
|
329
|
-
if (refresh) scopes.clear();
|
|
330
|
-
scopes.set(category, scope);
|
|
331
|
-
rows = scope.rows;
|
|
332
|
-
currency = scope.currency ?? currency;
|
|
333
|
-
if (cursor) void fetchRest(category, cursor);
|
|
334
|
-
else if (scope.complete && category === ALL) writeCachedCards(scope.rows, scope.currency);
|
|
335
|
-
await committed(gen);
|
|
336
|
-
if (gen === generation) {
|
|
337
|
-
await svc.scopeLanded({ gen, category, status: "ready", fromCache: false });
|
|
338
|
-
await publishAuxiliaryIfReady();
|
|
339
|
-
}
|
|
340
|
-
} catch (error) {
|
|
341
|
-
if (gen !== generation) return;
|
|
342
|
-
if (pendingRefreshScopes.get(category) === gen) pendingRefreshScopes.delete(category);
|
|
343
|
-
if (shown) {
|
|
344
|
-
// A failed refresh is not a failed screen: the cards the shopper was looking at stay.
|
|
345
|
-
await committed(gen);
|
|
346
|
-
if (gen === generation) {
|
|
347
|
-
await svc.scopeLanded({ gen, category, status: "ready", fromCache: !!shown?.fromCache });
|
|
348
|
-
await publishAuxiliaryIfReady();
|
|
349
|
-
}
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
// Offline: paint the last synced catalog — only for All, and never over an auth refusal.
|
|
353
|
-
const status = /HTTP (\d{3})/.exec(String(error))?.[1];
|
|
354
|
-
if (category === ALL && status !== "401" && status !== "403") {
|
|
355
|
-
const cached = await readCachedCards();
|
|
356
|
-
if (gen !== generation) return;
|
|
357
|
-
if (cached.length) {
|
|
358
|
-
const legacyCache = cached.some((card) => card.legacyCache || typeof card.cachePrice !== "number" || !card.cacheOptions);
|
|
359
|
-
rows = cached.map((card) => {
|
|
360
|
-
const stored = card;
|
|
361
|
-
currency ??= stored.cacheCurrency;
|
|
362
|
-
return {
|
|
363
|
-
card, price: Number.isFinite(stored.cachePrice) ? stored.cachePrice! : 0,
|
|
364
|
-
created: stored.cacheCreated ?? "", name: stored.cacheName ?? card.name.toLowerCase(), options: stored.cacheOptions ?? {},
|
|
365
|
-
};
|
|
366
|
-
});
|
|
367
|
-
scopes.set(category, { rows, complete: true, loading: false, fromCache: true, currency });
|
|
368
|
-
await committed(gen);
|
|
369
|
-
if (gen === generation) {
|
|
370
|
-
await svc.scopeLanded({ gen, category, status: "ready", fromCache: true, resetFacets: legacyCache && shouldResetLegacyCacheFacets() });
|
|
371
|
-
await publishAuxiliaryIfReady();
|
|
372
|
-
}
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
rows = [];
|
|
377
|
-
await committed(gen);
|
|
378
|
-
if (gen === generation) {
|
|
379
|
-
await svc.scopeLanded({ gen, category, status: "error", fromCache: false });
|
|
380
|
-
await publishAuxiliaryIfReady();
|
|
381
|
-
}
|
|
380
|
+
const controller = reader(category);
|
|
381
|
+
await controller.load(category);
|
|
382
|
+
await publications.get(category);
|
|
383
|
+
const scope = scopes.get(category), status = controller.getStatus();
|
|
384
|
+
if (scope?.cursor && !status.fromCache && !status.error && !status.offline) void fetchRest(category, scope.cursor);
|
|
385
|
+
if (gen === generation) await publishAuxiliaryIfReady();
|
|
382
386
|
} finally {
|
|
383
387
|
if (pendingScopes.get(category) === gen) pendingScopes.delete(category);
|
|
384
|
-
if (
|
|
388
|
+
if (pendingRefreshScopes.get(category) === gen) pendingRefreshScopes.delete(category);
|
|
385
389
|
}
|
|
386
390
|
}
|
|
387
391
|
|
|
@@ -390,11 +394,19 @@ async function fetchScope(category: string, gen: number, refresh = false): Promi
|
|
|
390
394
|
* refresh) fetches again but leaves the current cards up until the new page is in hand. */
|
|
391
395
|
function showScope(s: S, category: string, refresh = false): S {
|
|
392
396
|
const pending = pendingScopes.get(category);
|
|
393
|
-
|
|
394
|
-
|
|
397
|
+
const reactivating = category !== selectedCategory;
|
|
398
|
+
if (!refresh && !reactivating && pending === generation) {
|
|
399
|
+
const readStatus = reader(category).getStatus();
|
|
400
|
+
return derive({ ...s, readStatus, status: readStatus.hasSnapshot ? "ready" : readStatus.error || readStatus.offline ? "error" : "loading",
|
|
401
|
+
fromCache: readStatus.fromCache, categorySelected: category, scopeGen: generation });
|
|
395
402
|
}
|
|
396
403
|
const known = scopes.get(category);
|
|
397
|
-
const
|
|
404
|
+
const status = known ? reader(category).getStatus() : undefined;
|
|
405
|
+
const needsRefresh = refresh || pendingRefreshScopes.has(category) || (!!known && (
|
|
406
|
+
known.fromCache || status?.stale || !!status?.error ||
|
|
407
|
+
(known.metadata.expiresAt !== undefined && known.metadata.expiresAt <= Date.now()) ||
|
|
408
|
+
(!known.complete && !known.loading)
|
|
409
|
+
));
|
|
398
410
|
selectedCategory = category;
|
|
399
411
|
const gen = ++generation;
|
|
400
412
|
if (known) {
|
|
@@ -402,15 +414,22 @@ function showScope(s: S, category: string, refresh = false): S {
|
|
|
402
414
|
currency = known.currency ?? currency;
|
|
403
415
|
}
|
|
404
416
|
if (known && !needsRefresh) {
|
|
405
|
-
return derive({ ...s, status: "ready", fromCache: !!known.fromCache, categorySelected: category, scopeGen: gen,
|
|
417
|
+
return derive({ ...s, readStatus: reader(category).getStatus(), status: "ready", fromCache: !!known.fromCache, categorySelected: category, scopeGen: gen,
|
|
406
418
|
visible: PAGE_SIZE, cursorEpoch: s.cursorEpoch + 1 });
|
|
407
419
|
}
|
|
408
420
|
pendingScopes.set(category, gen);
|
|
409
421
|
if (needsRefresh) pendingRefreshScopes.set(category, gen);
|
|
422
|
+
if (reactivating && pending !== undefined) {
|
|
423
|
+
// An earlier visit's hung refresh must not own this visit. Keep its snapshot while
|
|
424
|
+
// fencing that flight, then start a current request with fresh pagination cursors.
|
|
425
|
+
const controller = reader(category);
|
|
426
|
+
if (known) controller.replace({ data: known, metadata: known.metadata }, category);
|
|
427
|
+
else controller.invalidate();
|
|
428
|
+
}
|
|
410
429
|
void fetchScope(category, gen, needsRefresh);
|
|
411
430
|
const keep = needsRefresh && !!known && s.status === "ready";
|
|
412
|
-
return derive({ ...s, status: keep ? "ready" : "loading", categorySelected: category, scopeGen: gen,
|
|
413
|
-
...(keep ? {} : { visible: PAGE_SIZE, cursorEpoch: s.cursorEpoch + 1 }) });
|
|
431
|
+
return derive({ ...s, readStatus: reader(category).getStatus(), status: keep ? "ready" : "loading", categorySelected: category, scopeGen: gen,
|
|
432
|
+
...(keep && category === s.categorySelected ? {} : { visible: PAGE_SIZE, cursorEpoch: s.cursorEpoch + 1 }) });
|
|
414
433
|
}
|
|
415
434
|
|
|
416
435
|
/** The categories and the cart count, then `loaded` — which reads the state as it is when
|
|
@@ -584,27 +603,30 @@ async function load(s: S, arg?: unknown, fresh = false): Promise<S | undefined>
|
|
|
584
603
|
const view = readViewPrefs();
|
|
585
604
|
const BOOT: S = derive({
|
|
586
605
|
...FACETS_BOOT, ...view,
|
|
606
|
+
readStatus: initialReadStatus(), readMessage: "", noReadMessage: true, hasReadMessage: false,
|
|
587
607
|
status: "loading", isLoading: true, notLoading: false, isErrored: false, notErrored: true, fromCache: false, noOffline: true,
|
|
588
608
|
categorySelected: ALL, scopeGen: 0, query: "", categoryOptions: [], hideCategories: true,
|
|
589
609
|
countLabel: "", noEmpty: true, emptyText: "", cartCount: 0,
|
|
590
610
|
});
|
|
591
611
|
|
|
592
612
|
const facets = facetActions<S>(derive);
|
|
593
|
-
const CONTENT = ["load", "refresh", "retry", "loaded", "scopeLanded", "scopeGrew", "selectCategory", "search", "toggleSave", "loadMore", ...Object.keys(facets)];
|
|
613
|
+
const CONTENT = ["readChanged", "load", "refresh", "retry", "loaded", "scopeLanded", "scopeGrew", "selectCategory", "search", "toggleSave", "loadMore", ...Object.keys(facets)];
|
|
594
614
|
const register = withIapPresentation<S>({
|
|
595
615
|
// Restored from the last run, these painted a state this boot has not reached: a count over
|
|
596
616
|
// no cards, an open sheet, a stale scope generation that defeats `committed`, a search
|
|
597
617
|
// narrowing the list under an empty field. The shopper's view, sort and filters do persist.
|
|
598
618
|
prefix: "shop",
|
|
599
|
-
transientKeys: ["results", "query", "scopeGen", "status", "isLoading", "notLoading", "isErrored", "notErrored", "fromCache", "noOffline",
|
|
619
|
+
transientKeys: ["readStatus", "readMessage", "noReadMessage", "hasReadMessage", "results", "query", "scopeGen", "status", "isLoading", "notLoading", "isErrored", "notErrored", "fromCache", "noOffline",
|
|
600
620
|
"countLabel", "noEmpty", "emptyText", "cartCount", "panel", "filtersOpen", "sortOpen", "categorySelected", "categoryOptions", "hideCategories",
|
|
601
621
|
"sortOptions", "sortLabel", "filterLabel", "activeFilters", "noActiveFilters", "priceChips", "priceSelected", "hidePrice",
|
|
602
622
|
"opt1Name", "opt1Chips", "opt1Selected", "hideOpt1", "opt2Name", "opt2Chips", "opt2Selected", "hideOpt2", "opt3Name", "opt3Chips", "opt3Selected", "hideOpt3",
|
|
603
623
|
"visible", "cursorEpoch", "matched", "nextCursor", "noMore", "showLabel", "hideGrid", "hideList", "hideComfortable", "hideCompact"],
|
|
604
624
|
contentActions: CONTENT,
|
|
605
625
|
forcePriceRefreshActions: ["load", "refresh", "retry"],
|
|
606
|
-
items: (state) => state.results.map((row) => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.commercePrice })),
|
|
607
|
-
present: (state, price, _allowed, appleIapOrResolving) =>
|
|
626
|
+
items: (state) => currentReadState(state).results.map((row) => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.commercePrice })),
|
|
627
|
+
present: (state, price, _allowed, appleIapOrResolving) => {
|
|
628
|
+
const current = currentReadState(state);
|
|
629
|
+
return { ...current, results: current.results.map((row) => {
|
|
608
630
|
const stored = row as typeof row & { commerceWasPrice?: string; commerceOnSale?: boolean };
|
|
609
631
|
const wasPrice = stored.commerceWasPrice ?? row.wasPrice;
|
|
610
632
|
const onSale = stored.commerceOnSale ?? row.onSale;
|
|
@@ -626,17 +648,30 @@ const register = withIapPresentation<S>({
|
|
|
626
648
|
notOnSale: !onSale,
|
|
627
649
|
noTagRow: !onSale && row.noSwatches,
|
|
628
650
|
};
|
|
629
|
-
|
|
651
|
+
}) };
|
|
652
|
+
},
|
|
630
653
|
});
|
|
631
654
|
|
|
632
655
|
type A = (s: S, arg?: unknown) => S | undefined | Promise<S | undefined>;
|
|
633
656
|
const svc = register<S, Record<string, A>>("himiBrandedShop", {
|
|
634
657
|
state: BOOT,
|
|
658
|
+
readLifecycle: { refreshAction: "recover", statusPath: "readStatus" },
|
|
635
659
|
actions: {
|
|
636
660
|
load: (s, arg) => load(s, arg),
|
|
637
661
|
// Pull to refresh: the categories and the scope again, from Wix.
|
|
638
662
|
refresh: (s, arg) => load(s, arg, true),
|
|
639
|
-
retry: (s) => showScope(s, s.categorySelected),
|
|
663
|
+
retry: (s) => showScope(s, s.categorySelected, true),
|
|
664
|
+
recover: async () => { await fetchScope(selectedCategory, generation, true); return undefined; },
|
|
665
|
+
readChanged: (s, arg) => {
|
|
666
|
+
if (!arg || typeof arg !== 'object') return undefined;
|
|
667
|
+
const value = arg as { gen: number; category: string; readStatus: ReadStatus };
|
|
668
|
+
if (value.gen !== generation || value.category !== selectedCategory) return undefined;
|
|
669
|
+
const status = value.readStatus;
|
|
670
|
+
return derive({ ...s, ...(loadedCartCount === undefined ? {} : { cartCount: loadedCartCount }), readStatus: status, fromCache: status.fromCache || status.offline && status.hasSnapshot,
|
|
671
|
+
status: status.hasSnapshot ? "ready" : status.error || status.offline ? "error" : "loading",
|
|
672
|
+
categorySelected: value.category, scopeGen: value.gen });
|
|
673
|
+
},
|
|
674
|
+
identityChanged: (s) => derive({ ...s, ...FACETS_BOOT, readStatus: initialReadStatus(), scopeGen: generation, cartCount: 0, status: "loading" }),
|
|
640
675
|
// The categories and the cart count landed (load / refresh).
|
|
641
676
|
loaded: (s, arg) => loadedState(s, arg),
|
|
642
677
|
// The first page of the scope on screen landed, or failed.
|
|
@@ -657,7 +692,7 @@ const svc = register<S, Record<string, A>>("himiBrandedShop", {
|
|
|
657
692
|
categoryIntentVersion++;
|
|
658
693
|
pendingLink = undefined;
|
|
659
694
|
if (id !== ALL && !categories.some((c) => c.id === id)) return s;
|
|
660
|
-
|
|
695
|
+
// showScope compares the prior selection before committing the requested category.
|
|
661
696
|
// categorySelected is committed with the tap (showScope), so a tap back to the chip
|
|
662
697
|
// on screen while another category loads is a no-op on the right scope.
|
|
663
698
|
if (id === s.categorySelected) return cancelsPendingRoute || refresh ? showScope(s, id, refresh) : s;
|
|
@@ -687,5 +722,13 @@ const svc = register<S, Record<string, A>>("himiBrandedShop", {
|
|
|
687
722
|
},
|
|
688
723
|
});
|
|
689
724
|
|
|
725
|
+
(globalThis as { himalaya?: { __subscribeReadLifecycle?(listener: (signal: string) => void): () => void } }).himalaya?.__subscribeReadLifecycle?.(signal => {
|
|
726
|
+
if (signal !== 'invalidate') return;
|
|
727
|
+
generation++; auxiliaryVersion++; categoryReadVersion++;
|
|
728
|
+
rows = []; currency = undefined; categories = []; categoriesAuthoritative = false;
|
|
729
|
+
scopes.clear(); pendingScopes.clear(); pendingRefreshScopes.clear();
|
|
730
|
+
loadedCartCount = 0; pendingAuxiliary = undefined; pendingCategoriesRead = undefined;
|
|
731
|
+
void svc.identityChanged();
|
|
732
|
+
});
|
|
690
733
|
svc.load();
|
|
691
734
|
export { svc };
|