@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
|
@@ -12,6 +12,8 @@ export interface CollectionSaving {
|
|
|
12
12
|
type State = Record<string, unknown>;
|
|
13
13
|
interface BackendCollection {
|
|
14
14
|
restorePagesOnLoad?: boolean;
|
|
15
|
+
/** Opt in only for templates whose generated screens adopt the shared read status. */
|
|
16
|
+
readLifecycle?: boolean;
|
|
15
17
|
/** Sees the load action's argument (the screen's onAppear params) BEFORE the query runs. A returned
|
|
16
18
|
* `{ filter, sort }` resets the criteria for that load (a screen that always opens on one view). */
|
|
17
19
|
onLoad?(arg: unknown): CollectionCriteriaReset | void;
|
|
@@ -83,6 +85,8 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
83
85
|
})
|
|
84
86
|
: [];
|
|
85
87
|
let pendingFilter = '';
|
|
88
|
+
const requestedReadLifecycle = (def as { readLifecycle?: { refreshAction: string; statusPath: string } }).readLifecycle;
|
|
89
|
+
const hasReadLifecycle = !!requestedReadLifecycle || backend?.readLifecycle === true;
|
|
86
90
|
const applyNavigationCriteria = (arg: unknown, state: State): boolean => {
|
|
87
91
|
const input = object(arg);
|
|
88
92
|
let changed = false;
|
|
@@ -144,16 +148,18 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
144
148
|
if (previewId && !rows.some(row => collectionId(row) === previewId)) previewId = '';
|
|
145
149
|
const filterChoices = [{ id: 'all', label: copyParam('collection.all', 'All') }, ...(config.rowStyle === 'flat' ? [] : [{ id: 'saved', label: copyParam('collection.saved', 'Saved') }]),
|
|
146
150
|
...projected.filters.map(label => ({ id: `value:${label}`, label: copyParam(`collection.filter.${label}`, label) }))];
|
|
147
|
-
const
|
|
151
|
+
const readStatus = hasReadLifecycle ? s.readStatus as { hasSnapshot?: boolean; refreshing?: boolean } | undefined : undefined;
|
|
152
|
+
const loading = readStatus ? readStatus.refreshing === true && readStatus.hasSnapshot !== true : s.status === 'loading' || s.isLoading === true;
|
|
148
153
|
// Older domain reducers expose `errored` rather than `isErrored`; backend
|
|
149
154
|
// adapters can also intentionally keep their display status "ready" while
|
|
150
155
|
// the query controller carries a recoverable failure. Treat each shape as
|
|
151
156
|
// one collection state so the shared UI never renders a blank catalog.
|
|
152
157
|
const backendState = backend?.controller.getState();
|
|
158
|
+
const read = backend?.readLifecycle ? s.readStatus as { hasSnapshot?: boolean; offline?: boolean; error?: string } | undefined : undefined;
|
|
153
159
|
const provisional = backendState?.provisional === true;
|
|
154
|
-
const errored = s.status === 'error' || s.isErrored === true || s.errored === true || backendState?.status === 'error';
|
|
160
|
+
const errored = !!read?.error || (backend?.readLifecycle === true && !!backendState?.error) || s.status === 'error' || s.isErrored === true || s.errored === true || backendState?.status === 'error';
|
|
155
161
|
const domainError = typeof s.error === 'string' ? s.error.trim() : '';
|
|
156
|
-
const queryError = typeof backendState?.error === 'string' ? backendState.error.trim() : '';
|
|
162
|
+
const queryError = read?.offline && !read.hasSnapshot ? 'This content isn’t available offline. Connect to load it.' : read?.error || (typeof backendState?.error === 'string' ? backendState.error.trim() : '');
|
|
157
163
|
// Some domains carry their failure sentence in `emptyText` (portfolio, podcasts);
|
|
158
164
|
// the screen folds that node into this block, so keep the domain's own words.
|
|
159
165
|
const domainEmptyError = errored && typeof s.emptyText === 'string' ? s.emptyText.trim() : '';
|
|
@@ -178,7 +184,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
178
184
|
// "All" and the collection's own default (e.g. Upcoming) are both un-narrowed: Clear
|
|
179
185
|
// resets to "All", so showing it on either would offer a reset that changes nothing.
|
|
180
186
|
const narrowed = !!prefs.query.trim() || (prefs.filter !== 'all' && prefs.filter !== initialCollection(config).filter) || domainNarrowed;
|
|
181
|
-
const sourceEmpty = !loading && !errored && rows.length === 0 &&
|
|
187
|
+
const sourceEmpty = (backend?.readLifecycle !== true || backendState?.read.hasSnapshot === true) && !loading && !errored && rows.length === 0 &&
|
|
182
188
|
(!(backend && config.backendQuery) || !narrowed) && !domainNarrowed;
|
|
183
189
|
const emptySourceText = (typeof s.emptyText === 'string' && s.emptyText.trim()) || copyParam('collection.emptySource', 'Nothing here yet');
|
|
184
190
|
// Chrome is noise over a small un-narrowed list (fewer than COLLECTION_CHROME_MIN_ITEMS
|
|
@@ -196,14 +202,15 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
196
202
|
noPageError: s.pageError !== true,
|
|
197
203
|
// Rows read from the device ahead of the live read are shown as they are: "saved offline" is what stays on
|
|
198
204
|
// screen when the live read failed, not a first visit that is still loading.
|
|
199
|
-
noOffline: s.fromCache
|
|
205
|
+
noOffline: !(backend?.readLifecycle === true && backendState?.read.stale || s.fromCache === true) || provisional,
|
|
200
206
|
noError: !errored,
|
|
201
207
|
noRetry: !canRetry || signInRequired,
|
|
202
208
|
noSignIn: !signInRequired,
|
|
203
209
|
errorTitle: signInRequired ? copyParam('collection.signInTitle', 'Sign in to see this') : copyParam('collection.loadErrorTitle', 'This collection could not load'),
|
|
204
210
|
errorMessage,
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
isLoading: loading, notLoading: !loading,
|
|
212
|
+
// Legacy searches retain visible rows; opted-in reads also retain valid empty snapshots.
|
|
213
|
+
noLoading: !loading || rows.length > 0 || (backend?.readLifecycle === true && backendState?.read.hasSnapshot === true),
|
|
207
214
|
// De-dup against a domain that paints its OWN empty block (screen-kit emptyBlock is
|
|
208
215
|
// gated on the domain's top-level `noEmpty`): only one empty state ever shows.
|
|
209
216
|
noEmptySource: !sourceEmpty || s.noEmpty === false,
|
|
@@ -330,7 +337,11 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
330
337
|
};
|
|
331
338
|
actions.refresh = (_s, arg) => {
|
|
332
339
|
stashCopy(arg); syncSaved();
|
|
333
|
-
|
|
340
|
+
// Reconnect refreshes the currently visible result. Rebuild every page the
|
|
341
|
+
// user had already opened, using fresh cursors, before replacing the snapshot.
|
|
342
|
+
return (backend.readLifecycle
|
|
343
|
+
? backend.controller.query(criteria(), true, true)
|
|
344
|
+
: backend.controller.query(criteria())).then(() => undefined);
|
|
334
345
|
};
|
|
335
346
|
actions.loadMore = () => backend.controller.loadMore().then(() => undefined);
|
|
336
347
|
actions.retry = actions.collectionRetry = () => backend.controller.retry().then(() => undefined);
|
|
@@ -344,7 +355,10 @@ export function decorateCollection(name: string, def: { state: State; actions?:
|
|
|
344
355
|
const retry = actions.refresh ?? actions.load;
|
|
345
356
|
if (retry) actions.collectionRetry = retry;
|
|
346
357
|
}
|
|
347
|
-
return { state: derive(def.state), actions
|
|
358
|
+
return { state: derive(def.state), actions,
|
|
359
|
+
...(requestedReadLifecycle || backend?.readLifecycle ? { readPresentation: derive } : {}),
|
|
360
|
+
...(requestedReadLifecycle ? { readLifecycle: requestedReadLifecycle }
|
|
361
|
+
: backend?.readLifecycle && runtime?.input?.__himiOwnerPreview !== 'sample' ? { readLifecycle: { refreshAction: 'refresh', statusPath: 'readStatus' } } : {}) };
|
|
348
362
|
}
|
|
349
363
|
|
|
350
364
|
/** Domain-owned page source with shared query controls. Publishing uses a normal
|
|
@@ -357,6 +371,8 @@ export function defineBackendCollection<S extends object, Row extends object>(na
|
|
|
357
371
|
onlineWriteMessageFields?: readonly string[];
|
|
358
372
|
/** Revalidate already opened pages on navigation return; explicit refresh resets. */
|
|
359
373
|
restorePagesOnLoad?: boolean;
|
|
374
|
+
/** Opt this shared list into the read status and reconnect lifecycle. */
|
|
375
|
+
readLifecycle?: boolean;
|
|
360
376
|
/** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
|
|
361
377
|
* domain setting that rides the descriptor, e.g. My Bookings' `weeklyGoal`. */
|
|
362
378
|
onLoad?(arg: unknown): CollectionCriteriaReset | void;
|
|
@@ -367,12 +383,16 @@ export function defineBackendCollection<S extends object, Row extends object>(na
|
|
|
367
383
|
actions?: Record<string, (state: S, ...args: unknown[]) => S | undefined | Promise<S | undefined>>;
|
|
368
384
|
}) {
|
|
369
385
|
let publish: (() => unknown) | undefined;
|
|
370
|
-
const controller = createCollectionQuery(options.source,
|
|
386
|
+
const controller = createCollectionQuery({ ...options.source,
|
|
387
|
+
// Access-denial clearing belongs to the opt-in read lifecycle. Keep the legacy
|
|
388
|
+
// collection contract unchanged for templates that have not adopted it.
|
|
389
|
+
...(options.readLifecycle ? { clearOnError: error => /HTTP (401|403)\b|forbidden|principal.changed|identity.changed/i.test(String(error)) || options.source.clearOnError?.(error) === true } : {}) }, () => { publish?.(); });
|
|
371
390
|
const definition = decorateCollection(name, { state: options.state as State, actions: options.actions as Record<string, Reducer> | undefined }, options.saving, {
|
|
372
391
|
restorePagesOnLoad: options.restorePagesOnLoad,
|
|
392
|
+
readLifecycle: options.readLifecycle,
|
|
373
393
|
onLoad: options.onLoad,
|
|
374
394
|
controller: controller as unknown as BackendCollection['controller'],
|
|
375
|
-
apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
|
|
395
|
+
apply: (state, page) => ({ ...options.apply(state as S, page as unknown as CollectionQueryState<Row>), ...(options.readLifecycle ? { readStatus: page.read } : {}) }) as State,
|
|
376
396
|
});
|
|
377
397
|
const registerService = options.onlineWrites ? withOnlineWrites(options.register ?? register, ['confirmCancel', 'confirmOperation', 'retryOperation', 'commitWrite'], { messageFields: options.onlineWriteMessageFields ?? ['actionMessage'] }) : (options.register ?? register);
|
|
378
398
|
const handle = registerService(name, definition);
|
|
@@ -103,6 +103,8 @@ export function withIapPresentation<S extends object>(options: {
|
|
|
103
103
|
// scopeLanded is the completion of the active load, not a stale load result. It
|
|
104
104
|
// must publish even if another overlapping onAppear load advanced contentVersion.
|
|
105
105
|
if (!next || (content && action !== "scopeLanded" && version !== contentVersion)) return undefined;
|
|
106
|
+
// A committed read projection supersedes older load results still carrying a skeleton.
|
|
107
|
+
if (action === "readChanged" && definition.readLifecycle) contentVersion++;
|
|
106
108
|
if (content) {
|
|
107
109
|
options.onContent?.(next);
|
|
108
110
|
const nextItems = options.items(next);
|
|
@@ -130,9 +132,11 @@ export function withIapPresentation<S extends object>(options: {
|
|
|
130
132
|
};
|
|
131
133
|
try {
|
|
132
134
|
const next = reduce(state, ...args);
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
135
|
+
// A cached read projection must commit before another onAppear can consume it.
|
|
136
|
+
// Other content actions retain their scheduling: shop filters and panels can
|
|
137
|
+
// change while a load is pending, and their existing completion order matters.
|
|
138
|
+
const synchronousReadProjection = action === "readChanged" && !!definition.readLifecycle;
|
|
139
|
+
if ((content && !synchronousReadProjection) || (next && typeof (next as Promise<S | undefined>).then === "function")) {
|
|
136
140
|
return (async () => {
|
|
137
141
|
try { return presentResult(await next); }
|
|
138
142
|
finally { finished(); }
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { stashCopy } from './himi-copy.js';
|
|
2
|
+
const offlineReadsEnabled = true;
|
|
3
|
+
import { isNotFound } from './owner-load-error.js';
|
|
4
|
+
import { defineService as register, createReadController, initialReadStatus, type ServiceDefinition, type ReadMetadata, type ReadStatus } from '@himalaya/state';
|
|
5
|
+
|
|
6
|
+
export interface ReadOptions {
|
|
7
|
+
cache: 'only-if-cached' | 'reload';
|
|
8
|
+
onCacheResult(metadata: ReadMetadata): void;
|
|
9
|
+
}
|
|
10
|
+
/** Passed explicitly down the loader's call graph; never a process-global cache mode. */
|
|
11
|
+
export function readOptions(arg: unknown): ReadOptions | undefined {
|
|
12
|
+
return (arg as { __himiRead?: ReadOptions } | undefined)?.__himiRead;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Explicit request context for loaders with their own presentation state (e.g. IAP). */
|
|
16
|
+
export async function policyRead<T>(cache: ReadOptions['cache'], load: (options: ReadOptions) => Promise<T>, localResult?: (data: T) => boolean) {
|
|
17
|
+
let metadata: ReadMetadata | undefined;
|
|
18
|
+
const data = await load({ cache, onCacheResult(value) {
|
|
19
|
+
metadata = metadata ? { ...value, source: metadata.source === 'cache' || value.source === 'cache' ? 'cache' : 'network',
|
|
20
|
+
cacheable: metadata.cacheable && value.cacheable,
|
|
21
|
+
fetchedAt: Math.min(metadata.fetchedAt, value.fetchedAt),
|
|
22
|
+
expiresAt: Math.min(metadata.expiresAt ?? 0, value.expiresAt ?? 0) } : value;
|
|
23
|
+
} });
|
|
24
|
+
if (!metadata && localResult?.(data)) metadata = { source: 'local', cacheable: false, fetchedAt: Date.now() };
|
|
25
|
+
if (!metadata) throw new Error('Read loader did not provide response provenance');
|
|
26
|
+
return { data, metadata };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type State = Record<string, unknown>;
|
|
30
|
+
type Action = (state: State, arg?: unknown) => State | undefined | Promise<State | undefined>;
|
|
31
|
+
type ReadFailure = Error & { userMessage?: string; signInRequired?: boolean };
|
|
32
|
+
// A record Wix answers 404 for is gone (a delete whose reply was lost): its retained snapshot must
|
|
33
|
+
// not outlive it. Unlike `denied`, it leaves drafts and local controls alone.
|
|
34
|
+
const gone = (error: unknown) => isNotFound(error);
|
|
35
|
+
const denied = (error: unknown) => /HTTP (401|403)\b|forbidden|principal.changed|identity.changed|member access changed/i.test(String(error));
|
|
36
|
+
|
|
37
|
+
/** Adapts a read-only load reducer. Callers forward readOptions(arg) to every
|
|
38
|
+
* primary SDK read and let errors reject. Other actions remain ordinary reducers.
|
|
39
|
+
* The retained snapshot is in memory; persistence belongs exclusively to the
|
|
40
|
+
* policy-controlled Wix cache, never to generic worker hydration or local-db rows.
|
|
41
|
+
*/
|
|
42
|
+
export function withReadLifecycle(registerService: typeof register = register, loadAction = 'load', options: {
|
|
43
|
+
errorMessage?(error: unknown): string;
|
|
44
|
+
/** Only an authored create form or guest shell; never a server-derived fallback. */
|
|
45
|
+
localResult?(state: State): boolean;
|
|
46
|
+
/** Additional host-owned scope when a read explicitly addresses another site. */
|
|
47
|
+
scopeKey?(): string;
|
|
48
|
+
/** Clear module-owned read sidecars on identity loss, access denial, or disposal. */
|
|
49
|
+
onInvalidate?(): void;
|
|
50
|
+
/** Editable fields survive recovery once loaded, and edits during an initial read. */
|
|
51
|
+
preserveFields?: readonly string[];
|
|
52
|
+
unavailable?(state: State, status: ReadStatus): State;
|
|
53
|
+
/** Authored source content only; never return a network mirror or demo fallback here. */
|
|
54
|
+
authoredFallback?(state: State, status: ReadStatus): State | undefined;
|
|
55
|
+
} = {}): typeof register {
|
|
56
|
+
if (!offlineReadsEnabled) return registerService;
|
|
57
|
+
return ((name: string, definition: ServiceDefinition<State>) => {
|
|
58
|
+
if ((globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input?.__himiOwnerPreview === 'sample') return registerService(name, definition);
|
|
59
|
+
const load = definition.actions?.[loadAction] as Action | undefined;
|
|
60
|
+
if (!load || definition.readLifecycle) return registerService(name, definition);
|
|
61
|
+
let args: Record<string, unknown> = {}, key = '', page: State | undefined, read = initialReadStatus();
|
|
62
|
+
let replacePage = false;
|
|
63
|
+
let activeLoad: { promise: Promise<void>; revision: number } | undefined;
|
|
64
|
+
let startedState: State | undefined;
|
|
65
|
+
let hadSnapshot = false, accessRevoked = false, accessNeedsSignIn = false;
|
|
66
|
+
const localResult = options.localResult ?? (value => value.notFound === true);
|
|
67
|
+
let projectedState: State | undefined;
|
|
68
|
+
const editedFields = new Set<string>();
|
|
69
|
+
let service: { getState(): State; readChanged(): Promise<unknown> };
|
|
70
|
+
const project = (state: State): State => {
|
|
71
|
+
for (const field of options.preserveFields ?? []) {
|
|
72
|
+
if (projectedState && JSON.stringify(state[field]) !== JSON.stringify(projectedState[field])) editedFields.add(field);
|
|
73
|
+
}
|
|
74
|
+
const value: State = { ...state, ...(replacePage && page ? page : !read.hasSnapshot ? definition.state : {}), readStatus: read };
|
|
75
|
+
replacePage = false;
|
|
76
|
+
if (accessRevoked) for (const field of options.preserveFields ?? []) {
|
|
77
|
+
if (Object.prototype.hasOwnProperty.call(definition.state, field)) value[field] = definition.state[field];
|
|
78
|
+
else delete value[field];
|
|
79
|
+
}
|
|
80
|
+
// Drafts and local controls survive both missing snapshots and pending reads.
|
|
81
|
+
for (const field of accessRevoked ? [] : Object.keys(state)) {
|
|
82
|
+
if (options.preserveFields?.includes(field) && (hadSnapshot || editedFields.has(field))) value[field] = state[field];
|
|
83
|
+
if (/Draft$|^draft$|^quantity$|^selection$|^selected|^expandedIds$|^searchQuery$|^query$|^filter$|^sort$/.test(field) &&
|
|
84
|
+
(!read.hasSnapshot || (startedState && JSON.stringify(state[field]) !== JSON.stringify(startedState[field])))) value[field] = state[field];
|
|
85
|
+
}
|
|
86
|
+
if (!read.hasSnapshot) {
|
|
87
|
+
const authored = options.authoredFallback?.(value, read);
|
|
88
|
+
if (authored) return projectedState = { ...authored, readStatus: { ...read, hasLocalContent: true } };
|
|
89
|
+
const message = read.offline ? 'This content isn’t available offline. Connect to load it.' : read.error;
|
|
90
|
+
const unavailable = { ...value, status: read.refreshing ? 'loading' : 'error', isLoading: read.refreshing,
|
|
91
|
+
notLoading: !read.refreshing, busy: !read.refreshing, hideContent: true, errored: !read.refreshing, isErrored: !read.refreshing,
|
|
92
|
+
notErrored: read.refreshing, found: false, noEntity: true, hideNotFound: true, hideLoading: !read.refreshing, hideLoadError: read.refreshing, error: message, errorText: message, emptyText: message, noEmpty: true };
|
|
93
|
+
if (accessRevoked) Object.assign(unavailable, { readDenied: true, signInRequired: accessNeedsSignIn, noSignIn: !accessNeedsSignIn });
|
|
94
|
+
return projectedState = options.unavailable?.(unavailable, read) ?? unavailable;
|
|
95
|
+
}
|
|
96
|
+
return projectedState = definition.readPresentation?.(value) ?? value;
|
|
97
|
+
};
|
|
98
|
+
const perform = async (cache: ReadOptions['cache'], requestKey: string) => {
|
|
99
|
+
const requestArgs = requestKey ? JSON.parse(requestKey) as Record<string, unknown> : {};
|
|
100
|
+
return policyRead(cache, async readContext => {
|
|
101
|
+
const input = { ...(service?.getState() ?? definition.state), __himiRead: readContext } as State;
|
|
102
|
+
// Unavailable-state flags added by this adapter are presentation, not a failed
|
|
103
|
+
// domain read. A reducer that spreads its input must not inherit those flags
|
|
104
|
+
// and have its successful cached/local result rejected as another failure.
|
|
105
|
+
for (const field of ['errored', 'isErrored']) {
|
|
106
|
+
if (!Object.prototype.hasOwnProperty.call(definition.state, field)) delete input[field];
|
|
107
|
+
}
|
|
108
|
+
// An unsaved create form is authored local content. Its no-op loader must
|
|
109
|
+
// receive its declared status, not the adapter's temporary unavailable state.
|
|
110
|
+
if (!read.hasSnapshot && options.localResult?.(input)) input.status = definition.state.status;
|
|
111
|
+
const data = await load(input, { ...requestArgs, __himiRead: readContext });
|
|
112
|
+
if (!data || data.status === 'error' || data.errored === true || data.isErrored === true) {
|
|
113
|
+
const unauthorized = data?.signInRequired === true || /HTTP 401|unauthori[sz]ed|sign[\s-]?in/i.test(String(data?.error || data?.errorText || ''));
|
|
114
|
+
const failure = new Error(unauthorized ? 'HTTP 401 unauthorized' : data?.readDenied ? 'HTTP 403 forbidden' : 'Read failed') as ReadFailure;
|
|
115
|
+
failure.signInRequired = data?.signInRequired === true;
|
|
116
|
+
const userMessage = data?.error || data?.errorText || data?.emptyText;
|
|
117
|
+
if (typeof userMessage === 'string' && userMessage.trim()) failure.userMessage = userMessage.trim();
|
|
118
|
+
throw failure;
|
|
119
|
+
}
|
|
120
|
+
delete data.__himiRead;
|
|
121
|
+
return data;
|
|
122
|
+
}, localResult);
|
|
123
|
+
};
|
|
124
|
+
const controller = createReadController<State>({
|
|
125
|
+
allowLocal: true, cached: key => perform('only-if-cached', key), fetch: key => perform('reload', key), clearOnError(error) {
|
|
126
|
+
accessRevoked = denied(error);
|
|
127
|
+
accessNeedsSignIn = (error as ReadFailure | null)?.signInRequired === true || /HTTP 401|unauthori[sz]ed|sign[\s-]?in/i.test(String(error));
|
|
128
|
+
const clear = accessRevoked || gone(error);
|
|
129
|
+
if (clear) options.onInvalidate?.();
|
|
130
|
+
return clear;
|
|
131
|
+
}, errorMessage(error) {
|
|
132
|
+
return (error as ReadFailure | null)?.userMessage || options.errorMessage?.(error) || (accessNeedsSignIn ? 'Sign in to view this content.' : '') || '';
|
|
133
|
+
},
|
|
134
|
+
publish(data, status) {
|
|
135
|
+
// A failed revalidation republishes the controller's retained snapshot. The worker
|
|
136
|
+
// state can contain newer local changes (for example, another fetched page or a
|
|
137
|
+
// selected row), so do not replace that state with the older controller page on a
|
|
138
|
+
// stale/error publication. A first cached paint and a successful refresh still
|
|
139
|
+
// replace the visible read data.
|
|
140
|
+
replacePage = status.hasSnapshot && data !== undefined
|
|
141
|
+
&& (!page || (!status.refreshing && !status.stale && !status.error));
|
|
142
|
+
page = data;
|
|
143
|
+
read = status;
|
|
144
|
+
void service?.readChanged();
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
const host = (globalThis as { himalaya?: { __subscribeReadLifecycle?(listener: (signal: string) => void): () => void } }).himalaya;
|
|
148
|
+
let unsubscribeAdapter: (() => void) | undefined;
|
|
149
|
+
unsubscribeAdapter = host?.__subscribeReadLifecycle?.(signal => {
|
|
150
|
+
if (signal === 'dispose') { options.onInvalidate?.(); unsubscribeAdapter?.(); return; }
|
|
151
|
+
if (signal !== 'invalidate') return;
|
|
152
|
+
options.onInvalidate?.();
|
|
153
|
+
// Identity invalidation clears the controller's private snapshot, so the
|
|
154
|
+
// adapter must also forget that its form fields came from a prior user.
|
|
155
|
+
// Otherwise project() can preserve those fields while the host clears the
|
|
156
|
+
// shared read state.
|
|
157
|
+
hadSnapshot = false;
|
|
158
|
+
accessRevoked = true;
|
|
159
|
+
accessNeedsSignIn = false;
|
|
160
|
+
editedFields.clear();
|
|
161
|
+
page = undefined;
|
|
162
|
+
read = initialReadStatus();
|
|
163
|
+
replacePage = true;
|
|
164
|
+
void service?.readChanged();
|
|
165
|
+
});
|
|
166
|
+
const refresh: Action = async (_state, arg) => {
|
|
167
|
+
stashCopy(arg);
|
|
168
|
+
if (arg && typeof arg === 'object') {
|
|
169
|
+
args = { ...arg as Record<string, unknown> };
|
|
170
|
+
// Localized copy is presentation, not a different backend query.
|
|
171
|
+
delete args.copy;
|
|
172
|
+
}
|
|
173
|
+
const next = JSON.stringify({ ...((globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input ?? {}), ...args, ...(options.scopeKey ? { __readScope: options.scopeKey() } : {}) });
|
|
174
|
+
// Coalesce through the final state projection, not only the controller's
|
|
175
|
+
// network task. A module-scope load() and the host's boot/onAppear load()
|
|
176
|
+
// commonly join the same request; returning when the fetch settles lets the
|
|
177
|
+
// host snapshot the worker before the first caller's readChanged() publishes.
|
|
178
|
+
if (key === next && activeLoad && activeLoad.revision === controller.getRevision()) {
|
|
179
|
+
await activeLoad.promise;
|
|
180
|
+
await service?.readChanged();
|
|
181
|
+
return service?.getState();
|
|
182
|
+
}
|
|
183
|
+
hadSnapshot = read.hasSnapshot && key === next;
|
|
184
|
+
key = next;
|
|
185
|
+
startedState = service?.getState() ?? definition.state;
|
|
186
|
+
projectedState = startedState; editedFields.clear(); accessRevoked = false; accessNeedsSignIn = false;
|
|
187
|
+
const load = controller.load(key);
|
|
188
|
+
const revision = controller.getRevision();
|
|
189
|
+
const task = (async () => {
|
|
190
|
+
await load;
|
|
191
|
+
// Controller publications project through this service action. Await its final
|
|
192
|
+
// projection before resolving load/retry so hosts that serialize worker actions
|
|
193
|
+
// cannot render the pre-read state after the request has completed.
|
|
194
|
+
await service?.readChanged();
|
|
195
|
+
})();
|
|
196
|
+
const active = { promise: task, revision };
|
|
197
|
+
activeLoad = active;
|
|
198
|
+
try {
|
|
199
|
+
await task;
|
|
200
|
+
} finally { if (activeLoad === active) activeLoad = undefined; }
|
|
201
|
+
return await service?.getState();
|
|
202
|
+
};
|
|
203
|
+
service = registerService(name, { ...definition, readLifecycle: { refreshAction: 'refreshRead', statusPath: 'readStatus' },
|
|
204
|
+
state: { ...definition.state, readStatus: read },
|
|
205
|
+
actions: { ...definition.actions, ...(loadAction !== "refresh" && definition.actions?.refresh ? { refresh: () => controller.refresh().then(() => undefined) } : {}), [loadAction]: refresh, retry: () => controller.refresh().then(() => undefined),
|
|
206
|
+
refreshRead: refresh, readChanged: project },
|
|
207
|
+
});
|
|
208
|
+
return service;
|
|
209
|
+
}) as typeof register;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Generic services keep their existing behavior. A `load` action is not proof
|
|
213
|
+
// that it is a safe, cache-eligible read: local device state and writes often use
|
|
214
|
+
// that name too. Shared read helpers and opted-in screens register through
|
|
215
|
+
// withReadLifecycle explicitly.
|
|
216
|
+
export const defineService: typeof register = register;
|
|
217
|
+
/** Safe opt-in for a service whose load action is a cache-eligible read. */
|
|
218
|
+
export const readService: typeof register = withReadLifecycle();
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
/** Raw read escape hatch for an endpoint without a generated SDK binding. */
|
|
222
|
+
export async function rawRead<T>(method: string, path: string, body?: unknown, options?: ReadOptions): Promise<T> {
|
|
223
|
+
const api = (globalThis as any).himalaya?.wixApi;
|
|
224
|
+
if (!api) throw new Error('Wix bridge unavailable');
|
|
225
|
+
if (!options) return api.call(method, path, body) as Promise<T>;
|
|
226
|
+
if (!api.read) throw new Error('READ_LIFECYCLE_UNSUPPORTED');
|
|
227
|
+
const result = await api.read(method, path, body, undefined, { cache: options.cache });
|
|
228
|
+
options.onCacheResult(result.cache);
|
|
229
|
+
return result.data as T;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** Editor recovery refreshes its read eligibility without replacing an open draft
|
|
233
|
+
* or its concurrency revision. Field values remain owned by the form until the
|
|
234
|
+
* user explicitly submits or reopens it. Only a new, unsaved form is local data. */
|
|
235
|
+
export function withFormReadLifecycle(registerService: typeof register = register): typeof register {
|
|
236
|
+
if (!offlineReadsEnabled) return registerService;
|
|
237
|
+
return ((name: string, definition: ServiceDefinition<State>) => withReadLifecycle(registerService, 'load', {
|
|
238
|
+
preserveFields: ['title', 'name', 'price', 'sku', 'visible', 'revision', 'category',
|
|
239
|
+
'description', 'originalDescription', 'descriptionMode', 'descriptionJson', 'descriptionDirty', 'descriptionEdits',
|
|
240
|
+
'when', 'location', 'excerpt', 'body', 'richContent', 'bodyJson', 'bodyDirty', 'bodyEdits',
|
|
241
|
+
'detailsOpen', 'coverUrl', 'originalCoverUrl', 'coverPreview', 'isFeatured',
|
|
242
|
+
'question', 'answer', 'answerFormat', 'answerMode', 'answerJson', 'answerDirty', 'answerEdits', 'categoryId', 'statusValue'],
|
|
243
|
+
localResult: value => value.mode === 'create' && !value.id,
|
|
244
|
+
})(name, definition)) as typeof register;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Server-derived state is never a generic worker snapshot; explicit drafts may persist. */
|
|
248
|
+
export function withReadPersistence(registerService: typeof register, draftFields: readonly string[] = []): typeof register {
|
|
249
|
+
if (!offlineReadsEnabled) return registerService;
|
|
250
|
+
return ((name: string, definition: ServiceDefinition<State>) => registerService(name, {
|
|
251
|
+
...definition,
|
|
252
|
+
notPersisted: [...new Set([...(definition.notPersisted ?? []),
|
|
253
|
+
...Object.keys(definition.state).filter(key => !draftFields.includes(key))])],
|
|
254
|
+
})) as typeof register;
|
|
255
|
+
}
|
|
@@ -250,7 +250,7 @@ interface State {
|
|
|
250
250
|
hideFeaturedSkeleton: boolean;
|
|
251
251
|
welcome: string;
|
|
252
252
|
refreshedAt: string;
|
|
253
|
-
/** The resolved home layout preset (
|
|
253
|
+
/** The resolved home layout preset (shared slot discriminator).
|
|
254
254
|
* Plain string, never a proto enum — a `$service:` casePath decodes it as a
|
|
255
255
|
* JSON string and unknown values fall through to the shipped defaultCase. */
|
|
256
256
|
effectiveLayout: string;
|
|
@@ -161,6 +161,9 @@ function derive(state: State): State {
|
|
|
161
161
|
});
|
|
162
162
|
return {
|
|
163
163
|
...state,
|
|
164
|
+
nextCursor: state.nextCursor ?? "",
|
|
165
|
+
filterOptions: state.filterOptions ?? [{ id: "all", label: copyParam("collection.all", "All"), selected: true },
|
|
166
|
+
...Object.keys(FULFILLMENT).map(label => ({ id: `value:${label}`, label: copyParam(`collection.filter.${label}`, label), selected: false }))],
|
|
164
167
|
displayRows: rows.map((row, index, rows) => {
|
|
165
168
|
// Older owned cache rows retain their original date in meta. Group only
|
|
166
169
|
// adjacent days so pagination and an explicit backend sort remain authoritative.
|