@wix/himalaya-cli 0.1387.0 → 0.1389.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/commerce-contract.d.ts +24 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/commerce-core.d.ts +3 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/index.d.ts +1 -1
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/display.d.ts +66 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/index.d.ts +185 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +57 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +57 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +324 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
  10. package/dist/cli.mjs +10 -5
  11. package/guides/iap-screen-adoption.md +53 -1
  12. package/guides/importable-sdks.md +1 -0
  13. package/guides/sdk-reference.json +261 -2
  14. package/package.json +2 -2
  15. package/templates/beauty-salon/dev/index.ts +193 -1
  16. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +1 -1
  17. package/templates/beauty-salon/tier5-src/_shared/iap-worker.ts +84 -3
  18. package/templates/beauty-salon/tier5-src/_shared/video-caption.ts +21 -0
  19. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +31 -1
  20. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +36 -1
  21. package/templates/beauty-salon-owner/config.ts +11 -0
  22. package/templates/beauty-salon-owner/dev/index.ts +540 -0
  23. package/templates/beauty-salon-owner/himalaya.content.json +6 -0
  24. package/templates/beauty-salon-owner/platforms.json +2 -1
  25. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +1 -1
  26. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  27. package/templates/branded-lite/dev/index.ts +1212 -54
  28. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +1 -1
  29. package/templates/branded-lite/tier5-src/_shared/iap-worker.ts +84 -3
  30. package/templates/branded-lite/tier5-src/_shared/video-caption.ts +21 -0
  31. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +31 -1
  32. package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +23 -32
  33. package/templates/branded-lite/tier5-src/himi-branded-lite-videos/index.ts +44 -38
  34. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +36 -1
  35. package/templates/community-club/dev/index.ts +193 -1
  36. package/templates/community-club/tier5-src/_shared/collection-contract.ts +1 -1
  37. package/templates/community-club/tier5-src/_shared/iap-worker.ts +84 -3
  38. package/templates/community-club/tier5-src/_shared/video-caption.ts +21 -0
  39. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +31 -1
  40. package/templates/community-club/tier5-src/himi-store-account/index.ts +36 -1
  41. package/templates/community-club-owner/config.ts +11 -0
  42. package/templates/community-club-owner/dev/index.ts +540 -0
  43. package/templates/community-club-owner/himalaya.content.json +6 -0
  44. package/templates/community-club-owner/platforms.json +2 -1
  45. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +1 -1
  46. package/templates/community-club-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  47. package/templates/events-meetups/dev/index.ts +193 -1
  48. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +1 -1
  49. package/templates/events-meetups/tier5-src/_shared/iap-worker.ts +84 -3
  50. package/templates/events-meetups/tier5-src/_shared/video-caption.ts +21 -0
  51. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +31 -1
  52. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +36 -1
  53. package/templates/events-meetups-owner/config.ts +11 -0
  54. package/templates/events-meetups-owner/dev/index.ts +540 -0
  55. package/templates/events-meetups-owner/himalaya.content.json +6 -0
  56. package/templates/events-meetups-owner/platforms.json +2 -1
  57. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +1 -1
  58. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  59. package/templates/gym-fitness/dev/index.ts +193 -1
  60. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +1 -1
  61. package/templates/gym-fitness/tier5-src/_shared/iap-worker.ts +84 -3
  62. package/templates/gym-fitness/tier5-src/_shared/video-caption.ts +21 -0
  63. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +31 -1
  64. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +36 -1
  65. package/templates/gym-fitness-owner/config.ts +11 -0
  66. package/templates/gym-fitness-owner/dev/index.ts +540 -0
  67. package/templates/gym-fitness-owner/himalaya.content.json +6 -0
  68. package/templates/gym-fitness-owner/platforms.json +2 -1
  69. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +1 -1
  70. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  71. package/templates/nonprofit-cause/dev/index.ts +193 -1
  72. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +1 -1
  73. package/templates/nonprofit-cause/tier5-src/_shared/iap-worker.ts +84 -3
  74. package/templates/nonprofit-cause/tier5-src/_shared/video-caption.ts +21 -0
  75. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +31 -1
  76. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +36 -1
  77. package/templates/nonprofit-cause-owner/config.ts +11 -0
  78. package/templates/nonprofit-cause-owner/dev/index.ts +540 -0
  79. package/templates/nonprofit-cause-owner/himalaya.content.json +6 -0
  80. package/templates/nonprofit-cause-owner/platforms.json +2 -1
  81. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +1 -1
  82. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  83. package/templates/owner-lite/config.ts +11 -0
  84. package/templates/owner-lite/dev/index.ts +540 -0
  85. package/templates/owner-lite/himalaya.content.json +6 -0
  86. package/templates/owner-lite/platforms.json +2 -1
  87. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +1 -1
  88. package/templates/owner-lite/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  89. package/templates/podcast-creator/dev/index.ts +193 -1
  90. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +1 -1
  91. package/templates/podcast-creator/tier5-src/_shared/iap-worker.ts +84 -3
  92. package/templates/podcast-creator/tier5-src/_shared/video-caption.ts +21 -0
  93. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +31 -1
  94. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +36 -1
  95. package/templates/podcast-creator-owner/config.ts +11 -0
  96. package/templates/podcast-creator-owner/dev/index.ts +540 -0
  97. package/templates/podcast-creator-owner/himalaya.content.json +6 -0
  98. package/templates/podcast-creator-owner/platforms.json +2 -1
  99. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +1 -1
  100. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  101. package/templates/portfolio-creative/dev/index.ts +193 -1
  102. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +1 -1
  103. package/templates/portfolio-creative/tier5-src/_shared/iap-worker.ts +84 -3
  104. package/templates/portfolio-creative/tier5-src/_shared/video-caption.ts +21 -0
  105. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +31 -1
  106. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +36 -1
  107. package/templates/portfolio-creative-owner/config.ts +11 -0
  108. package/templates/portfolio-creative-owner/dev/index.ts +540 -0
  109. package/templates/portfolio-creative-owner/himalaya.content.json +6 -0
  110. package/templates/portfolio-creative-owner/platforms.json +2 -1
  111. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +1 -1
  112. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  113. package/templates/restaurant/dev/index.ts +193 -1
  114. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +1 -1
  115. package/templates/restaurant/tier5-src/_shared/iap-worker.ts +84 -3
  116. package/templates/restaurant/tier5-src/_shared/video-caption.ts +21 -0
  117. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +31 -1
  118. package/templates/restaurant/tier5-src/himi-store-account/index.ts +36 -1
  119. package/templates/restaurant-owner/config.ts +11 -0
  120. package/templates/restaurant-owner/dev/index.ts +540 -0
  121. package/templates/restaurant-owner/himalaya.content.json +6 -0
  122. package/templates/restaurant-owner/platforms.json +2 -1
  123. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +1 -1
  124. package/templates/restaurant-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  125. package/templates/retail-store/dev/index.ts +193 -1
  126. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +1 -1
  127. package/templates/retail-store/tier5-src/_shared/iap-worker.ts +84 -3
  128. package/templates/retail-store/tier5-src/_shared/video-caption.ts +21 -0
  129. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +31 -1
  130. package/templates/retail-store/tier5-src/himi-store-account/index.ts +36 -1
  131. package/templates/retail-store-owner/config.ts +11 -0
  132. package/templates/retail-store-owner/dev/index.ts +540 -0
  133. package/templates/retail-store-owner/himalaya.content.json +6 -0
  134. package/templates/retail-store-owner/platforms.json +2 -1
  135. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +1 -1
  136. package/templates/retail-store-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  137. package/templates/yoga-studio/dev/index.ts +193 -1
  138. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +1 -1
  139. package/templates/yoga-studio/tier5-src/_shared/iap-worker.ts +84 -3
  140. package/templates/yoga-studio/tier5-src/_shared/video-caption.ts +21 -0
  141. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +31 -1
  142. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +36 -1
  143. package/templates/yoga-studio-owner/config.ts +11 -0
  144. package/templates/yoga-studio-owner/dev/index.ts +540 -0
  145. package/templates/yoga-studio-owner/himalaya.content.json +6 -0
  146. package/templates/yoga-studio-owner/platforms.json +2 -1
  147. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +1 -1
  148. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  149. package/templates/branded-lite/tier5-src/_shared/vod.ts +0 -71
@@ -2398,7 +2398,7 @@
2398
2398
  "name": "\"./commerce-contract.js\"",
2399
2399
  "kind": "reexport",
2400
2400
  "jsdoc": "",
2401
- "decl": "export type {\n AppleIapCheckoutRoutes, IapCommerce, IapCommerceOptions, IapPurchaseCopy, IapPurchaseFields, IapPurchaseGap, IapPurchaseReadiness,\n} from \"./commerce-contract.js\";"
2401
+ "decl": "export type {\n AppleIapCheckoutRoutes, IapCommerce, IapCommerceOptions, IapPurchaseCopy, IapPurchaseFields, IapPurchaseGap, IapPurchaseReadiness,\n IapRestoreResult,\n} from \"./commerce-contract.js\";"
2402
2402
  },
2403
2403
  {
2404
2404
  "name": "createIapCommerce",
@@ -2602,7 +2602,13 @@
2602
2602
  "name": "IapCommerce",
2603
2603
  "kind": "interface",
2604
2604
  "jsdoc": "",
2605
- "decl": "export interface IapCommerce {\n /** Routes, display prices, regular-checkout guards and the visible-refresh poll. */\n readonly pricing: IapCatalogPricing;\n /** Prices the items the screen shows, scoped to this app by the host. `scopeComplete` is false\n * when the host's own identity could not be read, so the screen offers Retry itself. */\n loadPrices(items: IapCatalogItem[]): Promise<{ scopeComplete: boolean }>;\n readiness(): IapPurchaseReadiness;\n /** Whether the latest price load (the screen's or the poll's) had the host's own identity; when\n * false the screen offers Retry itself. */\n scopeComplete(): boolean;\n state(): AppleIapCheckoutState;\n /** The item's App Store action, from its price route (the settled one while a refresh resolves),\n * purchase readiness and the checkout state. */\n purchaseFields(reference: CatalogReference, copy: IapPurchaseCopy): IapPurchaseFields;\n /** Checks readiness, then recovers held purchases and resumes payments already handed over. */\n start(): Promise<AppleIapCheckoutState>;\n buy(item: IapOrderItem): Promise<AppleIapCheckoutState>;\n /** On resume: re-reads Apple's unfinished queue and the payments being tracked. */\n reconcile(): Promise<AppleIapCheckoutState>;\n tick(): Promise<AppleIapCheckoutState>;\n /** The screen's one `_poll` for prices and purchase, or undefined when the cadence it already\n * armed still holds (or there is nothing to poll). Emit it from every publish. */\n pollDirective(action: string): IapPollDirective | undefined;\n /** The polled action's body: due purchase work — a full reconcile w\n/* … */"
2605
+ "decl": "export interface IapCommerce {\n /** Routes, display prices, regular-checkout guards and the visible-refresh poll. */\n readonly pricing: IapCatalogPricing;\n /** Prices the items the screen shows, scoped to this app by the host. `scopeComplete` is false\n * when the host's own identity could not be read, so the screen offers Retry itself. */\n loadPrices(items: IapCatalogItem[]): Promise<{ scopeComplete: boolean }>;\n readiness(): IapPurchaseReadiness;\n /** Whether the latest price load (the screen's or the poll's) had the host's own identity; when\n * false the screen offers Retry itself. */\n scopeComplete(): boolean;\n state(): AppleIapCheckoutState;\n /** The item's App Store action, from its price route (the settled one while a refresh resolves),\n * purchase readiness and the checkout state. */\n purchaseFields(reference: CatalogReference, copy: IapPurchaseCopy): IapPurchaseFields;\n /** Checks readiness, then recovers held purchases and resumes payments already handed over. */\n start(): Promise<AppleIapCheckoutState>;\n buy(item: IapOrderItem): Promise<AppleIapCheckoutState>;\n /** On resume: re-reads Apple's unfinished queue and the payments being tracked. */\n reconcile(): Promise<AppleIapCheckoutState>;\n /** Restore purchases (App Review 3.1.1): syncs Apple's purchase history to the device, then makes a\n * new attempt at every unfinished purchase (a scheduled retry is not waited out) and re-reads the\n * member's access. Needs a signed-in member; taps while one runs share it. */\n restore(): Promise<IapRestoreResult>;\n tick(): Promise<AppleIapCheck\n/* … */"
2606
+ },
2607
+ {
2608
+ "name": "IapRestoreResult",
2609
+ "kind": "type",
2610
+ "jsdoc": "/** What a restore found. Counts only: never a product, transaction or member id.\n * - `needsSignIn`: no member is signed in; Apple was not asked.\n * - `restored`: `recovered` purchases were taken by Wix during this restore; `refused` stay held,\n * unfinished, because Wix refused them (another member's purchase, say), plus any Wix refused for good\n * during this restore (finished; the member needs support); `pending` are charged but\n * not yet taken (Wix could not be reached, or asked to retry), and stay held for the next attempt.\n * - `failed`: `storeUnavailable` when the App Store sync or Apple's queue could not be read,\n * `unavailable` when this host does not sell through the App Store, `sessionChanged` when the\n * member changed. */",
2611
+ "decl": "export type IapRestoreResult =\n | { status: \"needsSignIn\" }\n | { status: \"restored\"; recovered: number; refused: number; pending: number }\n | { status: \"failed\"; code: \"storeUnavailable\" | \"unavailable\" | \"sessionChanged\" };"
2606
2612
  },
2607
2613
  {
2608
2614
  "name": "IapPurchaseCopy",
@@ -24613,6 +24619,259 @@
24613
24619
  }
24614
24620
  ]
24615
24621
  },
24622
+ {
24623
+ "name": "@himalaya/wix-video",
24624
+ "source": "stdlib/wix-sdk/video/src/index.ts",
24625
+ "description": "Wix Video — channels, videos, covers, durations, playback (native apps; the web bridges refuse its gateway)",
24626
+ "api": [
24627
+ {
24628
+ "name": "isHostedVideo",
24629
+ "kind": "function",
24630
+ "jsdoc": "/** True for a video whose file Wix hosts (it plays through `getVideoStreamUrl`). */",
24631
+ "decl": "export function isHostedVideo(video: WixVideo): boolean;"
24632
+ },
24633
+ {
24634
+ "name": "isVideoReady",
24635
+ "kind": "function",
24636
+ "jsdoc": "/** True when the video is published AND its media is ready to play. */",
24637
+ "decl": "export function isVideoReady(video: WixVideo): boolean;"
24638
+ },
24639
+ {
24640
+ "name": "isVideoProcessing",
24641
+ "kind": "function",
24642
+ "jsdoc": "/** True while an upload is still being processed (try again later). */",
24643
+ "decl": "export function isVideoProcessing(video: WixVideo): boolean;"
24644
+ },
24645
+ {
24646
+ "name": "youtubeVideoId",
24647
+ "kind": "function",
24648
+ "jsdoc": "/** The 11-character YouTube id of a linked YouTube video, or \"\". The embed URL is\n * treated as data: only the id is taken, never the URL itself. */",
24649
+ "decl": "export function youtubeVideoId(video: WixVideo): string;"
24650
+ },
24651
+ {
24652
+ "name": "videoCoverUrl",
24653
+ "kind": "function",
24654
+ "jsdoc": "/**\n * The video's card image, sized for display: the owner's cover, then the poster, then\n * the media thumbnail, then (for a YouTube video) YouTube's own thumbnail. \"\" when there\n * is none, so a card can show its placeholder.\n *\n * Wix media goes through `wixMediaUrl`: a stored cover is the untouched upload.\n */",
24655
+ "decl": "export function videoCoverUrl(video: WixVideo, size: WixImageSize | null = WIX_LIST_IMAGE): string;"
24656
+ },
24657
+ {
24658
+ "name": "channelCoverUrl",
24659
+ "kind": "function",
24660
+ "jsdoc": "/** The channel's cover image, sized for display, or \"\". */",
24661
+ "decl": "export function channelCoverUrl(channel: WixVideoChannel, size: WixImageSize | null = WIX_LIST_IMAGE): string;"
24662
+ },
24663
+ {
24664
+ "name": "videoDurationSec",
24665
+ "kind": "function",
24666
+ "jsdoc": "/** The video's length in whole seconds, or undefined when the gateway gives none. */",
24667
+ "decl": "export function videoDurationSec(video: WixVideo): number | undefined;"
24668
+ },
24669
+ {
24670
+ "name": "formatVideoDuration",
24671
+ "kind": "function",
24672
+ "jsdoc": "/** \"0:23\", \"4:05\", \"1:02:03\". \"\" for an unknown length. */",
24673
+ "decl": "export function formatVideoDuration(seconds: number | undefined): string;"
24674
+ },
24675
+ {
24676
+ "name": "isPaidVideo",
24677
+ "kind": "function",
24678
+ "jsdoc": "/** True when the video has an enabled SALE or RENT price. */",
24679
+ "decl": "export function isPaidVideo(video: WixVideo): boolean;"
24680
+ },
24681
+ {
24682
+ "name": "canWatchVideo",
24683
+ "kind": "function",
24684
+ "jsdoc": "/**\n * May the current viewer watch this video? FAILS CLOSED: missing pricing on the channel\n * or the video, or an entitlement that cannot be established, is \"no\".\n *\n * A channel with a required subscription or a pricing plan needs an active (or a\n * cancelled but unexpired) subscription. A paid video needs that subscription or its\n * own active sale or rent purchase. Query the channel with `CHANNEL_ACCESS_FIELDS` and\n * the video with `VIDEO_FIELDS`.\n */",
24685
+ "decl": "export function canWatchVideo(channel: WixVideoChannel, video: WixVideo, now = Date.now()): boolean;"
24686
+ },
24687
+ {
24688
+ "name": "latestVideos",
24689
+ "kind": "function",
24690
+ "jsdoc": "/**\n * Up to `limit` ready videos from the channels' preview pages (`listChannels` with\n * `videosPerChannel`, or `listLatestVideos`). Channels take turns — the first video of\n * each channel, then the second of each, … — so one large channel cannot fill the\n * whole rail. Duplicates (one video in two channels) appear once.\n */",
24691
+ "decl": "export function latestVideos(channels: WixVideoChannel[], limit = 8): WixVideoInChannel[];"
24692
+ },
24693
+ {
24694
+ "name": "WixVideoCard",
24695
+ "kind": "interface",
24696
+ "jsdoc": "/** The fields a video card binds to (`$item.*`). Shared by every carousel layout so the\n * caption placement is a layout choice, never a data change. */",
24697
+ "decl": "export interface WixVideoCard {\n id: string;\n channelId: string;\n title: string;\n /** \"4:05 · Channel\" — the parts that are known, joined. */\n meta: string;\n /** \"4:05\", or \"\". */\n duration: string;\n channelTitle: string;\n coverUrl: string;\n hasCover: boolean;\n noCover: boolean;\n /** True for a paid video (it may ask the viewer to buy or subscribe). */\n isPaid: boolean;\n notPaid: boolean;\n /** One spoken label: title, length and channel. */\n accessibilityLabel: string;\n}"
24698
+ },
24699
+ {
24700
+ "name": "videoCard",
24701
+ "kind": "function",
24702
+ "jsdoc": "/** Shape one video for a card. `untitled` replaces an empty title. */",
24703
+ "decl": "export function videoCard(entry: WixVideoInChannel, untitled = \"Video\", size: WixImageSize | null = WIX_LIST_IMAGE): WixVideoCard;"
24704
+ },
24705
+ {
24706
+ "name": "WIX_VIDEO_GRAPHQL_URL",
24707
+ "kind": "const",
24708
+ "jsdoc": "/** The Wix Video GraphQL gateway (visitor and member reads). */",
24709
+ "decl": "export const WIX_VIDEO_GRAPHQL_URL = \"https://manage.wix.com/_api/vod/public/graphql\";"
24710
+ },
24711
+ {
24712
+ "name": "WixCallOptions",
24713
+ "kind": "interface",
24714
+ "jsdoc": "/** Per-call options. `site` reads a business other than the app's active one. */",
24715
+ "decl": "export interface WixCallOptions {\n site?: string;\n}"
24716
+ },
24717
+ {
24718
+ "name": "WixVideoPricing",
24719
+ "kind": "interface",
24720
+ "jsdoc": "/** One price option of a channel (`SUBSCRIPTION` | `PRICING_PLAN`) or a video (`SALE` | `RENT`). */",
24721
+ "decl": "export interface WixVideoPricing {\n type?: string;\n enabled?: boolean;\n /** Channel only: true when every video needs the subscription or plan. */\n required?: boolean;\n price?: number;\n currency?: string;\n /** Rent period in hours (video) or subscription period (channel). */\n period?: number;\n}"
24722
+ },
24723
+ {
24724
+ "name": "WixVideoPurchase",
24725
+ "kind": "interface",
24726
+ "jsdoc": "/** The viewer's purchase of a channel subscription or of one video. */",
24727
+ "decl": "export interface WixVideoPurchase {\n /** `ACTIVE` | `PENDING` | `CANCELED` | `EXPIRED` | … */\n status?: string;\n expiresAt?: string;\n}"
24728
+ },
24729
+ {
24730
+ "name": "WixVideoMedia",
24731
+ "kind": "interface",
24732
+ "jsdoc": "/** The uploaded or linked media of a video. */",
24733
+ "decl": "export interface WixVideoMedia {\n /** `READY` once the file can play; `IN_QUEUE` / `IN_PROGRESS` / … while it processes. */\n status?: string;\n /** Length in milliseconds. */\n duration?: number;\n thumbnailUrl?: string;\n /** The embed URL of a linked (YouTube, Vimeo, …) video. */\n externalUrl?: string;\n externalId?: string;\n}"
24734
+ },
24735
+ {
24736
+ "name": "WixVideoImage",
24737
+ "kind": "interface",
24738
+ "jsdoc": "/** A selectable image (cover or poster). `selected` is the image URL. */",
24739
+ "decl": "export interface WixVideoImage {\n selected?: string | null;\n}"
24740
+ },
24741
+ {
24742
+ "name": "WixVideoPage",
24743
+ "kind": "interface",
24744
+ "jsdoc": "/** A page of a paginated list. */",
24745
+ "decl": "export interface WixVideoPage<T> {\n total?: number;\n cursor?: string | null;\n entities?: T[];\n}"
24746
+ },
24747
+ {
24748
+ "name": "WixVideo",
24749
+ "kind": "interface",
24750
+ "jsdoc": "",
24751
+ "decl": "export interface WixVideo {\n id: string;\n title?: string;\n description?: string;\n publisher?: string;\n /** `COMPUTER` | `GOOGLE_DRIVE` | `DROPBOX` (hosted) | `YOUTUBE` | `VIMEO` | `FACEBOOK` | `LIVE`. */\n source?: string;\n /** `VISIBLE` for a published video. */\n status?: string;\n cover?: WixVideoImage | null;\n poster?: WixVideoImage | null;\n content?: WixVideoMedia | null;\n pricing?: WixVideoPricing[];\n /** The viewer's SALE purchase, when the query asks for it. */\n purchase?: WixVideoPurchase | null;\n /** The viewer's RENT purchase, when the query asks for it (GraphQL alias). */\n rent_purchase?: WixVideoPurchase | null;\n}"
24752
+ },
24753
+ {
24754
+ "name": "WixVideoChannel",
24755
+ "kind": "interface",
24756
+ "jsdoc": "",
24757
+ "decl": "export interface WixVideoChannel {\n id: string;\n title?: string;\n description?: string;\n /** `INTERNAL` | `YOUTUBE`. */\n source?: string;\n featuredVideoId?: string | null;\n hasPaidVideos?: boolean;\n cover?: WixVideoImage | null;\n pricing?: WixVideoPricing[];\n /** The viewer's SUBSCRIPTION purchase, when the query asks for it. */\n purchase?: WixVideoPurchase | null;\n /** The channel's videos: only `total` in a list, a page when the query asks for one. */\n videos?: WixVideoPage<WixVideo>;\n}"
24758
+ },
24759
+ {
24760
+ "name": "WixVideoInChannel",
24761
+ "kind": "interface",
24762
+ "jsdoc": "/** A video together with the channel it belongs to. */",
24763
+ "decl": "export interface WixVideoInChannel {\n channel: WixVideoChannel;\n video: WixVideo;\n}"
24764
+ },
24765
+ {
24766
+ "name": "WixVideoErrorFlags",
24767
+ "kind": "interface",
24768
+ "jsdoc": "/** Flags every error this client throws carries, so a caller can branch without\n * matching message text. */",
24769
+ "decl": "export interface WixVideoErrorFlags {\n /** The gateway refused the viewer (membership, purchase or permission). */\n denied: boolean;\n /** The gateway needs a signed-in member. */\n signInRequired: boolean;\n /** The requested channel does not exist or is not visible. */\n notFound: boolean;\n}"
24770
+ },
24771
+ {
24772
+ "name": "WixVideoError",
24773
+ "kind": "type",
24774
+ "jsdoc": "",
24775
+ "decl": "export type WixVideoError = Error & WixVideoErrorFlags;"
24776
+ },
24777
+ {
24778
+ "name": "wixVideoError",
24779
+ "kind": "function",
24780
+ "jsdoc": "/** Build an error carrying the `WixVideoErrorFlags` (each defaults to false). */",
24781
+ "decl": "export function wixVideoError(message: string, flags: Partial<WixVideoErrorFlags> = {}): WixVideoError;"
24782
+ },
24783
+ {
24784
+ "name": "isWixVideoWebRefusal",
24785
+ "kind": "function",
24786
+ "jsdoc": "/** True when a web bridge refused the gateway URL. Wix Video is then out of reach on\n * this surface: the same read works in the native apps. */",
24787
+ "decl": "export function isWixVideoWebRefusal(error: unknown): boolean;"
24788
+ },
24789
+ {
24790
+ "name": "wixVideoQuery",
24791
+ "kind": "function",
24792
+ "jsdoc": "/**\n * Run one GraphQL read against the Wix Video gateway and return its `data`.\n *\n * The gateway answers HTTP 200 with an `errors` array for a refused or failed query.\n * That is thrown, never returned as empty data: a failed query must not look like a\n * site with no videos.\n *\n * @scope visitor\n */",
24793
+ "decl": "export async function wixVideoQuery<T>(query: string, variables: Record<string, unknown> = {}, opts?: WixCallOptions): Promise<T>;",
24794
+ "scope": "visitor"
24795
+ },
24796
+ {
24797
+ "name": "wixVideoPages",
24798
+ "kind": "function",
24799
+ "jsdoc": "/** Read every page of a cursor-paginated list. Rows are de-duplicated by id. A\n * repeated or malformed cursor throws instead of looping or returning a partial list. */",
24800
+ "decl": "export async function wixVideoPages<T extends { id: string }>(\n fetchPage: (cursor: string | null) => Promise<WixVideoPage<T> | null | undefined>,\n): Promise<T[]>;"
24801
+ },
24802
+ {
24803
+ "name": "CHANNEL_FIELDS",
24804
+ "kind": "const",
24805
+ "jsdoc": "/** The channel fields a list or card needs. */",
24806
+ "decl": "export const CHANNEL_FIELDS = \"id title description source featuredVideoId hasPaidVideos cover { selected } videos { total }\";"
24807
+ },
24808
+ {
24809
+ "name": "CHANNEL_ACCESS_FIELDS",
24810
+ "kind": "const",
24811
+ "jsdoc": "/** The channel fields an access decision needs (`canWatchVideo`). */",
24812
+ "decl": "export const CHANNEL_ACCESS_FIELDS = \"id title pricing { type enabled required } purchase(filter: {type: SUBSCRIPTION}) { status expiresAt }\";"
24813
+ },
24814
+ {
24815
+ "name": "VIDEO_CARD_FIELDS",
24816
+ "kind": "const",
24817
+ "jsdoc": "/** The video fields a card needs: title, images, length and processing state. */",
24818
+ "decl": "export const VIDEO_CARD_FIELDS = \"id title source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled }\";"
24819
+ },
24820
+ {
24821
+ "name": "VIDEO_FIELDS",
24822
+ "kind": "const",
24823
+ "jsdoc": "/** The video fields playback and an access decision need. */",
24824
+ "decl": "export const VIDEO_FIELDS = \"id title description source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled } purchase(filter: {type: SALE}) { status } rent_purchase: purchase(filter: {type: RENT}) { status }\";"
24825
+ },
24826
+ {
24827
+ "name": "ListChannelsOptions",
24828
+ "kind": "interface",
24829
+ "jsdoc": "",
24830
+ "decl": "export interface ListChannelsOptions extends WixCallOptions {\n /** Also read the first N videos of each channel (owner's order). 0 (default) reads none. */\n videosPerChannel?: number;\n}"
24831
+ },
24832
+ {
24833
+ "name": "listChannels",
24834
+ "kind": "function",
24835
+ "jsdoc": "/**\n * Every channel of the site, in the owner's order (all pages).\n *\n * @scope visitor\n */",
24836
+ "decl": "export async function listChannels(opts: ListChannelsOptions = {}): Promise<WixVideoChannel[]>;",
24837
+ "scope": "visitor"
24838
+ },
24839
+ {
24840
+ "name": "listChannelVideos",
24841
+ "kind": "function",
24842
+ "jsdoc": "/**\n * One channel and all its videos, in the owner's order. Throws a `notFound` error when\n * the channel does not exist.\n *\n * @scope visitor\n */",
24843
+ "decl": "export async function listChannelVideos(channelId: string, opts?: WixCallOptions): Promise<{ channel: WixVideoChannel; videos: WixVideo[] }>;",
24844
+ "scope": "visitor"
24845
+ },
24846
+ {
24847
+ "name": "getVideo",
24848
+ "kind": "function",
24849
+ "jsdoc": "/**\n * One video with its channel's access fields. `null` when the channel does not exist;\n * `video: null` when the channel has no such video.\n *\n * @scope visitor\n */",
24850
+ "decl": "export async function getVideo(channelId: string, videoId: string, opts?: WixCallOptions): Promise<{ channel: WixVideoChannel; video: WixVideo | null } | null>;",
24851
+ "scope": "visitor"
24852
+ },
24853
+ {
24854
+ "name": "getVideoStreamUrl",
24855
+ "kind": "function",
24856
+ "jsdoc": "/**\n * An authorized HLS stream URL for a hosted video (source `COMPUTER`, `GOOGLE_DRIVE`\n * or `DROPBOX`), or \"\" when the gateway gives none. Check `canWatchVideo` first: the\n * gateway authorizes the stream per viewer. A linked (YouTube, …) video has no stream\n * here; use `youtubeVideoId`.\n *\n * @scope visitor\n */",
24857
+ "decl": "export async function getVideoStreamUrl(channelId: string, videoId: string, opts?: WixCallOptions): Promise<string>;",
24858
+ "scope": "visitor"
24859
+ },
24860
+ {
24861
+ "name": "ListLatestVideosOptions",
24862
+ "kind": "interface",
24863
+ "jsdoc": "",
24864
+ "decl": "export interface ListLatestVideosOptions extends WixCallOptions {\n /** Most videos to return (default 8). */\n limit?: number;\n /** Channels to read (first page, default 10). */\n channels?: number;\n /** Videos to read from each channel (default 4). */\n perChannel?: number;\n}"
24865
+ },
24866
+ {
24867
+ "name": "listLatestVideos",
24868
+ "kind": "function",
24869
+ "jsdoc": "/**\n * The newest ready videos across the site's channels, for a rail or carousel: ONE\n * request that reads the first videos of the first channels. Channel order and each\n * channel's own video order are kept, and the channels take turns, so one large\n * channel cannot fill the whole rail.\n *\n * @scope visitor\n */",
24870
+ "decl": "export async function listLatestVideos(opts: ListLatestVideosOptions = {}): Promise<WixVideoInChannel[]>;",
24871
+ "scope": "visitor"
24872
+ }
24873
+ ]
24874
+ },
24616
24875
  {
24617
24876
  "name": "@himalaya/wix-workflows",
24618
24877
  "source": "stdlib/wix-sdk/workflows-v1/src/index.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1387.0",
3
+ "version": "0.1389.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "3775f6901bf78a5a41956aac4c29f84e7abbb7149aa95c4bf7552269"
74
+ "falconPackageHash": "7819e77c37b272b77ba95abb8564fbd979ea16abda28b831b8ce4bc5"
75
75
  }
@@ -3300,6 +3300,93 @@ const SCREENS = runtimeSetting({
3300
3300
  "accessibilityLabel": "My subscriptions"
3301
3301
  }
3302
3302
  },
3303
+ {
3304
+ "id": "himi_account.restore",
3305
+ "body": {
3306
+ "package": "himalaya",
3307
+ "type": "HStack",
3308
+ "props": {
3309
+ "spacing": 12,
3310
+ "children": [
3311
+ {
3312
+ "id": "",
3313
+ "body": {
3314
+ "package": "himalaya",
3315
+ "type": "Icon",
3316
+ "props": {
3317
+ "name": "refresh",
3318
+ "size": 18,
3319
+ "tint": "accentPrimary"
3320
+ }
3321
+ },
3322
+ "modifiers": {
3323
+ "accessibilityRole": "decorative"
3324
+ }
3325
+ },
3326
+ {
3327
+ "id": "",
3328
+ "body": {
3329
+ "package": "himalaya",
3330
+ "type": "Text",
3331
+ "props": {
3332
+ "text": "$service:himiStoreAccount.restoreLabel",
3333
+ "style": "bodyEmphasis"
3334
+ }
3335
+ }
3336
+ },
3337
+ {
3338
+ "id": "",
3339
+ "body": {
3340
+ "package": "himalaya",
3341
+ "type": "Spacer",
3342
+ "props": {}
3343
+ }
3344
+ }
3345
+ ]
3346
+ }
3347
+ },
3348
+ "modifiers": {
3349
+ "background": {
3350
+ "token": "surfaceElevated"
3351
+ },
3352
+ "border": {
3353
+ "width": 1,
3354
+ "color": {
3355
+ "token": "border"
3356
+ }
3357
+ },
3358
+ "cornerRadius": 8,
3359
+ "padding": {
3360
+ "leading": 14,
3361
+ "trailing": 14,
3362
+ "top": 13,
3363
+ "bottom": 13
3364
+ },
3365
+ "onTap": {
3366
+ "actionId": "tier5.himiStoreAccount.iapRestore"
3367
+ },
3368
+ "pressFeedback": "scale",
3369
+ "haptic": "light",
3370
+ "accessibilityLabel": "$service:himiStoreAccount.restoreLabel",
3371
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3372
+ }
3373
+ },
3374
+ {
3375
+ "id": "himi_account.restore_message",
3376
+ "body": {
3377
+ "package": "himalaya",
3378
+ "type": "Text",
3379
+ "props": {
3380
+ "text": "$service:himiStoreAccount.restoreMessage",
3381
+ "style": "footnote",
3382
+ "color": "textSecondary",
3383
+ "collapseWhenEmpty": true
3384
+ }
3385
+ },
3386
+ "modifiers": {
3387
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3388
+ }
3389
+ },
3303
3390
  {
3304
3391
  "id": "himi_account.addresses",
3305
3392
  "body": {
@@ -3729,6 +3816,109 @@ const SCREENS = runtimeSetting({
3729
3816
  "hiddenPath": "$service:himiStoreAccount.isMember"
3730
3817
  }
3731
3818
  },
3819
+ {
3820
+ "id": "",
3821
+ "body": {
3822
+ "package": "himalaya",
3823
+ "type": "VStack",
3824
+ "props": {
3825
+ "spacing": 8,
3826
+ "alignment": "leading",
3827
+ "children": [
3828
+ {
3829
+ "id": "himi_account.restore_guest",
3830
+ "body": {
3831
+ "package": "himalaya",
3832
+ "type": "HStack",
3833
+ "props": {
3834
+ "spacing": 12,
3835
+ "children": [
3836
+ {
3837
+ "id": "",
3838
+ "body": {
3839
+ "package": "himalaya",
3840
+ "type": "Icon",
3841
+ "props": {
3842
+ "name": "refresh",
3843
+ "size": 18,
3844
+ "tint": "accentPrimary"
3845
+ }
3846
+ },
3847
+ "modifiers": {
3848
+ "accessibilityRole": "decorative"
3849
+ }
3850
+ },
3851
+ {
3852
+ "id": "",
3853
+ "body": {
3854
+ "package": "himalaya",
3855
+ "type": "Text",
3856
+ "props": {
3857
+ "text": "$service:himiStoreAccount.restoreLabel",
3858
+ "style": "bodyEmphasis"
3859
+ }
3860
+ }
3861
+ },
3862
+ {
3863
+ "id": "",
3864
+ "body": {
3865
+ "package": "himalaya",
3866
+ "type": "Spacer",
3867
+ "props": {}
3868
+ }
3869
+ }
3870
+ ]
3871
+ }
3872
+ },
3873
+ "modifiers": {
3874
+ "background": {
3875
+ "token": "surfaceElevated"
3876
+ },
3877
+ "border": {
3878
+ "width": 1,
3879
+ "color": {
3880
+ "token": "border"
3881
+ }
3882
+ },
3883
+ "cornerRadius": 8,
3884
+ "padding": {
3885
+ "leading": 14,
3886
+ "trailing": 14,
3887
+ "top": 13,
3888
+ "bottom": 13
3889
+ },
3890
+ "onTap": {
3891
+ "actionId": "tier5.himiStoreAccount.iapRestore"
3892
+ },
3893
+ "pressFeedback": "scale",
3894
+ "haptic": "light",
3895
+ "accessibilityLabel": "$service:himiStoreAccount.restoreLabel",
3896
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3897
+ }
3898
+ },
3899
+ {
3900
+ "id": "himi_account.restore_guest_message",
3901
+ "body": {
3902
+ "package": "himalaya",
3903
+ "type": "Text",
3904
+ "props": {
3905
+ "text": "$service:himiStoreAccount.restoreMessage",
3906
+ "style": "footnote",
3907
+ "color": "textSecondary",
3908
+ "collapseWhenEmpty": true
3909
+ }
3910
+ },
3911
+ "modifiers": {
3912
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3913
+ }
3914
+ }
3915
+ ]
3916
+ }
3917
+ },
3918
+ "modifiers": {
3919
+ "hiddenPath": "$service:himiStoreAccount.isMember"
3920
+ }
3921
+ },
3732
3922
  {
3733
3923
  "id": "himi_account.language",
3734
3924
  "body": {
@@ -4098,6 +4288,7 @@ const SCREENS = runtimeSetting({
4098
4288
  "actionId": "tier5.himiStoreAccount.refresh",
4099
4289
  "params": {
4100
4290
  "memberAccountLinks": "1",
4291
+ "restorePurchases": "1",
4101
4292
  "copy": "{\"account.subtitle\":\"Orders, bookings, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart, bookings and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart, bookings and memberships stay with you.\"}"
4102
4293
  }
4103
4294
  }
@@ -4109,7 +4300,8 @@ const SCREENS = runtimeSetting({
4109
4300
  "contentType": "application/javascript+ts-bundle",
4110
4301
  "declaredCapabilities": [
4111
4302
  "wix_api",
4112
- "app_info"
4303
+ "app_info",
4304
+ "iap"
4113
4305
  ],
4114
4306
  "minBinaryVersion": "0.2.0",
4115
4307
  "bundleVersion": "0.0.1"
@@ -98,7 +98,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
98
98
  himiBrandedBadges: { rows: 'badges', title: 'name', subtitle: 'description', view: 'grid' },
99
99
  himiBrandedDonations: { rows: 'campaigns', title: 'name', subtitle: 'description', view: 'grid', primaryVerb: { label: 'Donate' } },
100
100
  himiBrandedGiftCards: { rows: 'cards', title: 'name', subtitle: 'description', view: 'grid' },
101
- himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'description', view: 'grid' },
101
+ himiBrandedVideos: { rows: 'channels', title: 'title', subtitle: 'subtitle', image: 'coverUrl', view: 'grid', searchPlaceholder: 'Search channels', countNoun: ['channel', 'channels'] },
102
102
  himiBrandedPodcasts: { rows: 'episodes', title: 'title', subtitle: 'subtitle', image: 'image', metadata: ['progressLabel'], view: 'list' },
103
103
  himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
104
104
  himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
@@ -2,9 +2,9 @@
2
2
  // `@himalaya/iap-checkout` → createIapCommerce), shared by every screen that shows or sells an
3
3
  // IAP-backed item. Mapping, Apple prices, payment guards, the host scope and the sale live in the
4
4
  // SDK; this only supplies the copy and the hydration reset.
5
- import type { IapPriceCopy } from "@himalaya/iap";
6
- import type { IapPurchaseCopy } from "@himalaya/iap-checkout";
7
- import { copyParam } from "./himi-copy.js";
5
+ import { hostSessionFromGlobal, type IapPriceCopy } from "@himalaya/iap";
6
+ import type { IapCommerce, IapPurchaseCopy, IapRestoreResult } from "@himalaya/iap-checkout";
7
+ import { copyParam, copyTemplate } from "./himi-copy.js";
8
8
 
9
9
  /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
10
10
  export function iapPriceCopy(prefix: string): IapPriceCopy {
@@ -31,6 +31,87 @@ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
31
31
  };
32
32
  }
33
33
 
34
+ /** The Restore purchases control (App Review 3.1.1) a screen binds. */
35
+ export interface IapRestoreFields {
36
+ restoreHidden: boolean;
37
+ restoreLabel: string;
38
+ restoreDisabled: boolean;
39
+ restoreMessage: string;
40
+ }
41
+
42
+ /** Keep these out of hydration: a restore's answer belongs to the run that got it. */
43
+ export const IAP_RESTORE_KEYS = ["restoreHidden", "restoreLabel", "restoreDisabled", "restoreMessage"] as const;
44
+
45
+ /** The control before the screen knows it sells through Apple. */
46
+ export const NO_IAP_RESTORE: IapRestoreFields = { restoreHidden: true, restoreLabel: "", restoreDisabled: false, restoreMessage: "" };
47
+
48
+ export interface IapRestoreControl {
49
+ /** `sellsThroughApple` is the screen's own signal: its item's App Store route, or Account's
50
+ * purchase readiness. */
51
+ fields(sellsThroughApple: boolean): IapRestoreFields;
52
+ /** Runs the shared `commerce.restore()`, repainting as it starts and when it answers. A tap while
53
+ * one runs is ignored. */
54
+ run(): Promise<void>;
55
+ }
56
+
57
+ /** Copy from the screen's pack (`<prefix>.restorePurchasesCta`, …). The restore itself, and the access
58
+ * re-read after it, are the shared commerce's. */
59
+ export function createIapRestore(
60
+ commerce: Pick<IapCommerce, "restore">,
61
+ options: { prefix: string; repaint(): void; /** The session generation; defaults to the host's. */ generation?: () => number },
62
+ ): IapRestoreControl {
63
+ const generation = options.generation ?? (() => hostSessionFromGlobal().current().generation);
64
+ let running = false;
65
+ let message = "";
66
+ let messageGeneration = -1;
67
+
68
+ function say(text: string, forGeneration: number): void {
69
+ message = text;
70
+ messageGeneration = forGeneration;
71
+ }
72
+
73
+ async function run(): Promise<void> {
74
+ if (running) return;
75
+ running = true;
76
+ const startedFor = generation();
77
+ say(copyParam(`${options.prefix}.restoringText`, "Restoring…"), startedFor);
78
+ options.repaint();
79
+ // Stamped with the session the tap was for: an answer landing after a sign-out is never shown.
80
+ say(restoreMessage(await restored(commerce), options.prefix), startedFor);
81
+ running = false;
82
+ options.repaint();
83
+ }
84
+
85
+ return {
86
+ fields: (sellsThroughApple) => ({
87
+ restoreHidden: !sellsThroughApple,
88
+ restoreLabel: copyParam(`${options.prefix}.restorePurchasesCta`, "Restore purchases"),
89
+ restoreDisabled: running,
90
+ // The answer was for the person signed in when it ran; another session never inherits it.
91
+ restoreMessage: sellsThroughApple && messageGeneration === generation() ? message : "",
92
+ }),
93
+ run,
94
+ };
95
+ }
96
+
97
+ function restored(commerce: Pick<IapCommerce, "restore">): Promise<IapRestoreResult> {
98
+ return commerce.restore().catch(() => ({ status: "failed", code: "storeUnavailable" }) as const);
99
+ }
100
+
101
+ function restoreMessage(result: IapRestoreResult, prefix: string): string {
102
+ if (result.status === "needsSignIn") return copyParam(`${prefix}.restoreSignInText`, "Sign in to restore your purchases.");
103
+ if (result.status === "failed") return result.code === "sessionChanged" ? "" : copyParam(`${prefix}.restoreFailedText`, "Couldn't reach the App Store. Try again.");
104
+ // Every part that happened is said: a recovery never hides a refusal or a purchase still pending.
105
+ const parts = [
106
+ result.recovered === 1 ? copyParam(`${prefix}.restoredOneText`, "Restored 1 purchase.") : "",
107
+ result.recovered > 1 ? copyTemplate(`${prefix}.restoredManyText`, "Restored {count} purchases.", { count: result.recovered }) : "",
108
+ // Held unfinished because Wix refused it (another member's purchase, say).
109
+ result.refused > 0 ? copyParam(`${prefix}.restoreRefusedText`, "A purchase on this Apple ID couldn't be added to this account. Contact support.") : "",
110
+ result.pending > 0 ? copyParam(`${prefix}.restorePendingText`, "A purchase is still being confirmed. Try again in a moment.") : "",
111
+ ].filter(Boolean);
112
+ return parts.length ? parts.join(" ") : copyParam(`${prefix}.restoreUpToDateText`, "Your purchases are up to date.");
113
+ }
114
+
34
115
  /** What `notHydrated` does, for a runtime that ignores the option (a binary older than it, still
35
116
  * served new bundles): put the listed keys back to their declared values before any action runs,
36
117
  * so a persisted price or an enabled purchase action never paints as this run's. */