@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
@@ -50,6 +50,10 @@ export interface IapCommerce {
50
50
  buy(item: IapOrderItem): Promise<AppleIapCheckoutState>;
51
51
  /** On resume: re-reads Apple's unfinished queue and the payments being tracked. */
52
52
  reconcile(): Promise<AppleIapCheckoutState>;
53
+ /** Restore purchases (App Review 3.1.1): syncs Apple's purchase history to the device, then makes a
54
+ * new attempt at every unfinished purchase (a scheduled retry is not waited out) and re-reads the
55
+ * member's access. Needs a signed-in member; taps while one runs share it. */
56
+ restore(): Promise<IapRestoreResult>;
53
57
  tick(): Promise<AppleIapCheckoutState>;
54
58
  /** The screen's one `_poll` for prices and purchase, or undefined when the cadence it already
55
59
  * armed still holds (or there is nothing to poll). Emit it from every publish. */
@@ -59,6 +63,26 @@ export interface IapCommerce {
59
63
  poll(): Promise<void>;
60
64
  dispose(): void;
61
65
  }
66
+ /** What a restore found. Counts only: never a product, transaction or member id.
67
+ * - `needsSignIn`: no member is signed in; Apple was not asked.
68
+ * - `restored`: `recovered` purchases were taken by Wix during this restore; `refused` stay held,
69
+ * unfinished, because Wix refused them (another member's purchase, say), plus any Wix refused for good
70
+ * during this restore (finished; the member needs support); `pending` are charged but
71
+ * not yet taken (Wix could not be reached, or asked to retry), and stay held for the next attempt.
72
+ * - `failed`: `storeUnavailable` when the App Store sync or Apple's queue could not be read,
73
+ * `unavailable` when this host does not sell through the App Store, `sessionChanged` when the
74
+ * member changed. */
75
+ export type IapRestoreResult = {
76
+ status: "needsSignIn";
77
+ } | {
78
+ status: "restored";
79
+ recovered: number;
80
+ refused: number;
81
+ pending: number;
82
+ } | {
83
+ status: "failed";
84
+ code: "storeUnavailable" | "unavailable" | "sessionChanged";
85
+ };
62
86
  /** Labels from the screen's copy pack; the shared layer only chooses between them. */
63
87
  export interface IapPurchaseCopy {
64
88
  buy: string;
@@ -24,6 +24,9 @@ export interface IapCommerceDependencies {
24
24
  identity: MemberIdentitySource;
25
25
  host: CommerceHost;
26
26
  store: TransactionStore;
27
+ /** StoreKit's `AppStore.sync()`: brings Apple's purchase history to the device and may ask the
28
+ * person to sign in to their Apple Account. Rejects when the App Store cannot be reached. */
29
+ syncAppStore(): Promise<void>;
27
30
  refs: OrderRefStore;
28
31
  clock: Clock;
29
32
  adapters: readonly IapOrderAdapter[];
@@ -5,4 +5,4 @@ export { onlineProgramOrders, pricingPlanOrders, storesProductOrders, classifyCa
5
5
  export { createWorkerOrderRefStore } from "./order-refs.js";
6
6
  export { itemPaymentEnabled, storesAppleSalesEnabled } from "./checkout-source.js";
7
7
  export type { AccessRead, AppleIapCheckout, AppleIapCheckoutDependencies, AppleIapCheckoutPhase, AppleIapCheckoutState, BridgeOutcome, BridgeRefusal, CashierRoute, IapEligibility, IapOrderAdapter, IapOrderItem, OrderAttempt, OrderFailureCode, OrderPartitionKey, OrderRef, OrderRefPartition, OrderRefStage, OrderRefStore, PaymentAccess, PaymentProgress, SessionBoundBridge, VerticalRef, WixBridge, } from "./apple-iap-contract.js";
8
- export type { AppleIapCheckoutRoutes, IapCommerce, IapCommerceOptions, IapPurchaseCopy, IapPurchaseFields, IapPurchaseGap, IapPurchaseReadiness, } from "./commerce-contract.js";
8
+ export type { AppleIapCheckoutRoutes, IapCommerce, IapCommerceOptions, IapPurchaseCopy, IapPurchaseFields, IapPurchaseGap, IapPurchaseReadiness, IapRestoreResult, } from "./commerce-contract.js";
@@ -0,0 +1,66 @@
1
+ import type { WixVideo, WixVideoChannel, WixVideoInChannel } from "./index.js";
2
+ import { type WixImageSize } from "@wix/himalaya/wix-media";
3
+ /** True for a video whose file Wix hosts (it plays through `getVideoStreamUrl`). */
4
+ export declare function isHostedVideo(video: WixVideo): boolean;
5
+ /** True when the video is published AND its media is ready to play. */
6
+ export declare function isVideoReady(video: WixVideo): boolean;
7
+ /** True while an upload is still being processed (try again later). */
8
+ export declare function isVideoProcessing(video: WixVideo): boolean;
9
+ /** The 11-character YouTube id of a linked YouTube video, or "". The embed URL is
10
+ * treated as data: only the id is taken, never the URL itself. */
11
+ export declare function youtubeVideoId(video: WixVideo): string;
12
+ /**
13
+ * The video's card image, sized for display: the owner's cover, then the poster, then
14
+ * the media thumbnail, then (for a YouTube video) YouTube's own thumbnail. "" when there
15
+ * is none, so a card can show its placeholder.
16
+ *
17
+ * Wix media goes through `wixMediaUrl`: a stored cover is the untouched upload.
18
+ */
19
+ export declare function videoCoverUrl(video: WixVideo, size?: WixImageSize | null): string;
20
+ /** The channel's cover image, sized for display, or "". */
21
+ export declare function channelCoverUrl(channel: WixVideoChannel, size?: WixImageSize | null): string;
22
+ /** The video's length in whole seconds, or undefined when the gateway gives none. */
23
+ export declare function videoDurationSec(video: WixVideo): number | undefined;
24
+ /** "0:23", "4:05", "1:02:03". "" for an unknown length. */
25
+ export declare function formatVideoDuration(seconds: number | undefined): string;
26
+ /** True when the video has an enabled SALE or RENT price. */
27
+ export declare function isPaidVideo(video: WixVideo): boolean;
28
+ /**
29
+ * May the current viewer watch this video? FAILS CLOSED: missing pricing on the channel
30
+ * or the video, or an entitlement that cannot be established, is "no".
31
+ *
32
+ * A channel with a required subscription or a pricing plan needs an active (or a
33
+ * cancelled but unexpired) subscription. A paid video needs that subscription or its
34
+ * own active sale or rent purchase. Query the channel with `CHANNEL_ACCESS_FIELDS` and
35
+ * the video with `VIDEO_FIELDS`.
36
+ */
37
+ export declare function canWatchVideo(channel: WixVideoChannel, video: WixVideo, now?: number): boolean;
38
+ /**
39
+ * Up to `limit` ready videos from the channels' preview pages (`listChannels` with
40
+ * `videosPerChannel`, or `listLatestVideos`). Channels take turns — the first video of
41
+ * each channel, then the second of each, … — so one large channel cannot fill the
42
+ * whole rail. Duplicates (one video in two channels) appear once.
43
+ */
44
+ export declare function latestVideos(channels: WixVideoChannel[], limit?: number): WixVideoInChannel[];
45
+ /** The fields a video card binds to (`$item.*`). Shared by every carousel layout so the
46
+ * caption placement is a layout choice, never a data change. */
47
+ export interface WixVideoCard {
48
+ id: string;
49
+ channelId: string;
50
+ title: string;
51
+ /** "4:05 · Channel" — the parts that are known, joined. */
52
+ meta: string;
53
+ /** "4:05", or "". */
54
+ duration: string;
55
+ channelTitle: string;
56
+ coverUrl: string;
57
+ hasCover: boolean;
58
+ noCover: boolean;
59
+ /** True for a paid video (it may ask the viewer to buy or subscribe). */
60
+ isPaid: boolean;
61
+ notPaid: boolean;
62
+ /** One spoken label: title, length and channel. */
63
+ accessibilityLabel: string;
64
+ }
65
+ /** Shape one video for a card. `untitled` replaces an empty title. */
66
+ export declare function videoCard(entry: WixVideoInChannel, untitled?: string, size?: WixImageSize | null): WixVideoCard;
@@ -0,0 +1,185 @@
1
+ export * from "./display.js";
2
+ declare global {
3
+ var himalaya: {
4
+ wixApi?: {
5
+ call: (method: string, path: string, body?: unknown, headers?: Record<string, string>) => Promise<unknown>;
6
+ };
7
+ };
8
+ }
9
+ /** The Wix Video GraphQL gateway (visitor and member reads). */
10
+ export declare const WIX_VIDEO_GRAPHQL_URL = "https://manage.wix.com/_api/vod/public/graphql";
11
+ /** Per-call options. `site` reads a business other than the app's active one. */
12
+ export interface WixCallOptions {
13
+ site?: string;
14
+ }
15
+ /** One price option of a channel (`SUBSCRIPTION` | `PRICING_PLAN`) or a video (`SALE` | `RENT`). */
16
+ export interface WixVideoPricing {
17
+ type?: string;
18
+ enabled?: boolean;
19
+ /** Channel only: true when every video needs the subscription or plan. */
20
+ required?: boolean;
21
+ price?: number;
22
+ currency?: string;
23
+ /** Rent period in hours (video) or subscription period (channel). */
24
+ period?: number;
25
+ }
26
+ /** The viewer's purchase of a channel subscription or of one video. */
27
+ export interface WixVideoPurchase {
28
+ /** `ACTIVE` | `PENDING` | `CANCELED` | `EXPIRED` | … */
29
+ status?: string;
30
+ expiresAt?: string;
31
+ }
32
+ /** The uploaded or linked media of a video. */
33
+ export interface WixVideoMedia {
34
+ /** `READY` once the file can play; `IN_QUEUE` / `IN_PROGRESS` / … while it processes. */
35
+ status?: string;
36
+ /** Length in milliseconds. */
37
+ duration?: number;
38
+ thumbnailUrl?: string;
39
+ /** The embed URL of a linked (YouTube, Vimeo, …) video. */
40
+ externalUrl?: string;
41
+ externalId?: string;
42
+ }
43
+ /** A selectable image (cover or poster). `selected` is the image URL. */
44
+ export interface WixVideoImage {
45
+ selected?: string | null;
46
+ }
47
+ /** A page of a paginated list. */
48
+ export interface WixVideoPage<T> {
49
+ total?: number;
50
+ cursor?: string | null;
51
+ entities?: T[];
52
+ }
53
+ export interface WixVideo {
54
+ id: string;
55
+ title?: string;
56
+ description?: string;
57
+ publisher?: string;
58
+ /** `COMPUTER` | `GOOGLE_DRIVE` | `DROPBOX` (hosted) | `YOUTUBE` | `VIMEO` | `FACEBOOK` | `LIVE`. */
59
+ source?: string;
60
+ /** `VISIBLE` for a published video. */
61
+ status?: string;
62
+ cover?: WixVideoImage | null;
63
+ poster?: WixVideoImage | null;
64
+ content?: WixVideoMedia | null;
65
+ pricing?: WixVideoPricing[];
66
+ /** The viewer's SALE purchase, when the query asks for it. */
67
+ purchase?: WixVideoPurchase | null;
68
+ /** The viewer's RENT purchase, when the query asks for it (GraphQL alias). */
69
+ rent_purchase?: WixVideoPurchase | null;
70
+ }
71
+ export interface WixVideoChannel {
72
+ id: string;
73
+ title?: string;
74
+ description?: string;
75
+ /** `INTERNAL` | `YOUTUBE`. */
76
+ source?: string;
77
+ featuredVideoId?: string | null;
78
+ hasPaidVideos?: boolean;
79
+ cover?: WixVideoImage | null;
80
+ pricing?: WixVideoPricing[];
81
+ /** The viewer's SUBSCRIPTION purchase, when the query asks for it. */
82
+ purchase?: WixVideoPurchase | null;
83
+ /** The channel's videos: only `total` in a list, a page when the query asks for one. */
84
+ videos?: WixVideoPage<WixVideo>;
85
+ }
86
+ /** A video together with the channel it belongs to. */
87
+ export interface WixVideoInChannel {
88
+ channel: WixVideoChannel;
89
+ video: WixVideo;
90
+ }
91
+ /** Flags every error this client throws carries, so a caller can branch without
92
+ * matching message text. */
93
+ export interface WixVideoErrorFlags {
94
+ /** The gateway refused the viewer (membership, purchase or permission). */
95
+ denied: boolean;
96
+ /** The gateway needs a signed-in member. */
97
+ signInRequired: boolean;
98
+ /** The requested channel does not exist or is not visible. */
99
+ notFound: boolean;
100
+ }
101
+ export type WixVideoError = Error & WixVideoErrorFlags;
102
+ /** Build an error carrying the `WixVideoErrorFlags` (each defaults to false). */
103
+ export declare function wixVideoError(message: string, flags?: Partial<WixVideoErrorFlags>): WixVideoError;
104
+ /** True when a web bridge refused the gateway URL. Wix Video is then out of reach on
105
+ * this surface: the same read works in the native apps. */
106
+ export declare function isWixVideoWebRefusal(error: unknown): boolean;
107
+ /**
108
+ * Run one GraphQL read against the Wix Video gateway and return its `data`.
109
+ *
110
+ * The gateway answers HTTP 200 with an `errors` array for a refused or failed query.
111
+ * That is thrown, never returned as empty data: a failed query must not look like a
112
+ * site with no videos.
113
+ *
114
+ * @scope visitor
115
+ */
116
+ export declare function wixVideoQuery<T>(query: string, variables?: Record<string, unknown>, opts?: WixCallOptions): Promise<T>;
117
+ /** Read every page of a cursor-paginated list. Rows are de-duplicated by id. A
118
+ * repeated or malformed cursor throws instead of looping or returning a partial list. */
119
+ export declare function wixVideoPages<T extends {
120
+ id: string;
121
+ }>(fetchPage: (cursor: string | null) => Promise<WixVideoPage<T> | null | undefined>): Promise<T[]>;
122
+ /** The channel fields a list or card needs. */
123
+ export declare const CHANNEL_FIELDS = "id title description source featuredVideoId hasPaidVideos cover { selected } videos { total }";
124
+ /** The channel fields an access decision needs (`canWatchVideo`). */
125
+ export declare const CHANNEL_ACCESS_FIELDS = "id title pricing { type enabled required } purchase(filter: {type: SUBSCRIPTION}) { status expiresAt }";
126
+ /** The video fields a card needs: title, images, length and processing state. */
127
+ export declare const VIDEO_CARD_FIELDS = "id title source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled }";
128
+ /** The video fields playback and an access decision need. */
129
+ export declare 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 }";
130
+ export interface ListChannelsOptions extends WixCallOptions {
131
+ /** Also read the first N videos of each channel (owner's order). 0 (default) reads none. */
132
+ videosPerChannel?: number;
133
+ }
134
+ /**
135
+ * Every channel of the site, in the owner's order (all pages).
136
+ *
137
+ * @scope visitor
138
+ */
139
+ export declare function listChannels(opts?: ListChannelsOptions): Promise<WixVideoChannel[]>;
140
+ /**
141
+ * One channel and all its videos, in the owner's order. Throws a `notFound` error when
142
+ * the channel does not exist.
143
+ *
144
+ * @scope visitor
145
+ */
146
+ export declare function listChannelVideos(channelId: string, opts?: WixCallOptions): Promise<{
147
+ channel: WixVideoChannel;
148
+ videos: WixVideo[];
149
+ }>;
150
+ /**
151
+ * One video with its channel's access fields. `null` when the channel does not exist;
152
+ * `video: null` when the channel has no such video.
153
+ *
154
+ * @scope visitor
155
+ */
156
+ export declare function getVideo(channelId: string, videoId: string, opts?: WixCallOptions): Promise<{
157
+ channel: WixVideoChannel;
158
+ video: WixVideo | null;
159
+ } | null>;
160
+ /**
161
+ * An authorized HLS stream URL for a hosted video (source `COMPUTER`, `GOOGLE_DRIVE`
162
+ * or `DROPBOX`), or "" when the gateway gives none. Check `canWatchVideo` first: the
163
+ * gateway authorizes the stream per viewer. A linked (YouTube, …) video has no stream
164
+ * here; use `youtubeVideoId`.
165
+ *
166
+ * @scope visitor
167
+ */
168
+ export declare function getVideoStreamUrl(channelId: string, videoId: string, opts?: WixCallOptions): Promise<string>;
169
+ export interface ListLatestVideosOptions extends WixCallOptions {
170
+ /** Most videos to return (default 8). */
171
+ limit?: number;
172
+ /** Channels to read (first page, default 10). */
173
+ channels?: number;
174
+ /** Videos to read from each channel (default 4). */
175
+ perChannel?: number;
176
+ }
177
+ /**
178
+ * The newest ready videos across the site's channels, for a rail or carousel: ONE
179
+ * request that reads the first videos of the first channels. Channel order and each
180
+ * channel's own video order are kept, and the channels take turns, so one large
181
+ * channel cannot fill the whole rail.
182
+ *
183
+ * @scope visitor
184
+ */
185
+ export declare function listLatestVideos(opts?: ListLatestVideosOptions): Promise<WixVideoInChannel[]>;
@@ -1345,6 +1345,10 @@ async function getProducts(productIds) {
1345
1345
  });
1346
1346
  return Array.isArray(r?.products) ? r.products : [];
1347
1347
  }
1348
+ async function restore() {
1349
+ const r = await dispatch("capability.iap.restore");
1350
+ return Array.isArray(r?.entitlements) ? r.entitlements : [];
1351
+ }
1348
1352
  async function purchaseForVerification(input) {
1349
1353
  if (!nonempty2(input?.productId) || !isUUID2(input?.appAccountToken)) return failure2("invalidArgument");
1350
1354
  const result = await managedDispatch(() => dispatch("capability.iap.purchaseForVerification", input));
@@ -2526,6 +2530,7 @@ function createIapCommerceWith(deps) {
2526
2530
  let stopWatchingAccess;
2527
2531
  let checkoutStarted = false;
2528
2532
  let recheckAfterOpening = false;
2533
+ let restoring;
2529
2534
  async function loadPrices(items) {
2530
2535
  if (!deps.pricing.enabled) return { scopeComplete: true };
2531
2536
  lastPricedAt = deps.clock.now();
@@ -2557,6 +2562,38 @@ function createIapCommerceWith(deps) {
2557
2562
  function start() {
2558
2563
  return recover((opened) => opened.start());
2559
2564
  }
2565
+ function restore2() {
2566
+ return restoring ?? (restoring = restoreOnce().finally(() => {
2567
+ restoring = void 0;
2568
+ }));
2569
+ }
2570
+ async function restoreOnce() {
2571
+ const session = deps.session.current();
2572
+ if (session.kind !== "member") return { status: "needsSignIn" };
2573
+ const before = state().purchase.items;
2574
+ const opened = await startedCheckout();
2575
+ if (deps.session.current().generation !== session.generation) return { status: "failed", code: "sessionChanged" };
2576
+ if (!opened) return { status: "failed", code: "unavailable" };
2577
+ if (!await syncedAppStore()) return { status: "failed", code: "storeUnavailable" };
2578
+ if (deps.session.current().generation !== session.generation) return { status: "failed", code: "sessionChanged" };
2579
+ const purchase = (await recover((opened2) => opened2.retry())).purchase;
2580
+ if (deps.session.current().generation !== session.generation) return { status: "failed", code: "sessionChanged" };
2581
+ const after = purchase.items;
2582
+ const newly = (matches) => Math.max(0, countOf(after, matches) - countOf(before, matches));
2583
+ const result = {
2584
+ status: "restored",
2585
+ recovered: newly(isRecovered),
2586
+ // Held refusals stay in the queue; a final refusal is finished, so only this restore's count.
2587
+ refused: countOf(after, isHeldRefusal) + newly(isFinalRefusal2),
2588
+ pending: countOf(after, isStillPending)
2589
+ };
2590
+ const settled = result.recovered + newly(isFinalRefusal2) > 0;
2591
+ if (purchase.unfinishedCount === null && !settled) return { status: "failed", code: "storeUnavailable" };
2592
+ return result;
2593
+ }
2594
+ function syncedAppStore() {
2595
+ return deps.syncAppStore().then(() => true, () => false);
2596
+ }
2560
2597
  async function buy(item) {
2561
2598
  if (unanswered2(deps.pricing.view(item.reference))) return state();
2562
2599
  const started = await startedCheckout();
@@ -2687,6 +2724,7 @@ function createIapCommerceWith(deps) {
2687
2724
  start,
2688
2725
  buy,
2689
2726
  reconcile: reconcile2,
2727
+ restore: restore2,
2690
2728
  tick: () => checkout ? checkout.tick() : Promise.resolve(state()),
2691
2729
  pollDirective,
2692
2730
  poll,
@@ -2707,6 +2745,22 @@ var SUSPENDED_AFTER_MS = 2 * PRICE_REFRESH_MS;
2707
2745
  function unanswered2(view) {
2708
2746
  return view.route === "resolving" || view.reason === "mappingUnavailable";
2709
2747
  }
2748
+ function isRecovered(item) {
2749
+ return item.completion === "finished" && item.access !== "rejected";
2750
+ }
2751
+ function isFinalRefusal2(item) {
2752
+ return item.completion === "finished" && item.access === "rejected";
2753
+ }
2754
+ function isHeldRefusal(item) {
2755
+ const unfinished = item.completion === "pending" || item.completion === "retryRequired";
2756
+ return unfinished && (item.access === "ownershipConflict" || item.access === "rejected");
2757
+ }
2758
+ function isStillPending(item) {
2759
+ return item.completion !== "finished" && item.completion !== "notFound" && !isHeldRefusal(item);
2760
+ }
2761
+ function countOf(items, matches) {
2762
+ return items.filter(matches).length;
2763
+ }
2710
2764
  function hostFor(identity, binding) {
2711
2765
  if (binding.himalayaAppId !== identity.appId || binding.bundleId !== identity.bundleId) return null;
2712
2766
  return { appId: identity.appId, bundleId: identity.bundleId, siteId: binding.siteId };
@@ -2725,6 +2779,9 @@ function createIapCommerce(options = {}) {
2725
2779
  session,
2726
2780
  host: hostOfWorker(),
2727
2781
  store: appleManagedStore,
2782
+ syncAppStore: async () => {
2783
+ await restore();
2784
+ },
2728
2785
  refs: createWorkerOrderRefStore(),
2729
2786
  clock: { now: () => Date.now() },
2730
2787
  adapters: options.adapters ?? [],
@@ -1622,6 +1622,10 @@ async function getProducts(productIds) {
1622
1622
  });
1623
1623
  return Array.isArray(r?.products) ? r.products : [];
1624
1624
  }
1625
+ async function restore() {
1626
+ const r = await dispatch2("capability.iap.restore");
1627
+ return Array.isArray(r?.entitlements) ? r.entitlements : [];
1628
+ }
1625
1629
  async function purchaseForVerification(input) {
1626
1630
  if (!nonempty2(input?.productId) || !isUUID2(input?.appAccountToken)) return failure2("invalidArgument");
1627
1631
  const result = await managedDispatch(() => dispatch2("capability.iap.purchaseForVerification", input));
@@ -2797,6 +2801,7 @@ function createIapCommerceWith(deps) {
2797
2801
  let stopWatchingAccess;
2798
2802
  let checkoutStarted = false;
2799
2803
  let recheckAfterOpening = false;
2804
+ let restoring;
2800
2805
  async function loadPrices(items) {
2801
2806
  if (!deps.pricing.enabled) return { scopeComplete: true };
2802
2807
  lastPricedAt = deps.clock.now();
@@ -2828,6 +2833,38 @@ function createIapCommerceWith(deps) {
2828
2833
  function start() {
2829
2834
  return recover((opened) => opened.start());
2830
2835
  }
2836
+ function restore2() {
2837
+ return restoring ?? (restoring = restoreOnce().finally(() => {
2838
+ restoring = void 0;
2839
+ }));
2840
+ }
2841
+ async function restoreOnce() {
2842
+ const session = deps.session.current();
2843
+ if (session.kind !== "member") return { status: "needsSignIn" };
2844
+ const before = state().purchase.items;
2845
+ const opened = await startedCheckout();
2846
+ if (deps.session.current().generation !== session.generation) return { status: "failed", code: "sessionChanged" };
2847
+ if (!opened) return { status: "failed", code: "unavailable" };
2848
+ if (!await syncedAppStore()) return { status: "failed", code: "storeUnavailable" };
2849
+ if (deps.session.current().generation !== session.generation) return { status: "failed", code: "sessionChanged" };
2850
+ const purchase = (await recover((opened2) => opened2.retry())).purchase;
2851
+ if (deps.session.current().generation !== session.generation) return { status: "failed", code: "sessionChanged" };
2852
+ const after = purchase.items;
2853
+ const newly = (matches) => Math.max(0, countOf(after, matches) - countOf(before, matches));
2854
+ const result = {
2855
+ status: "restored",
2856
+ recovered: newly(isRecovered),
2857
+ // Held refusals stay in the queue; a final refusal is finished, so only this restore's count.
2858
+ refused: countOf(after, isHeldRefusal) + newly(isFinalRefusal2),
2859
+ pending: countOf(after, isStillPending)
2860
+ };
2861
+ const settled = result.recovered + newly(isFinalRefusal2) > 0;
2862
+ if (purchase.unfinishedCount === null && !settled) return { status: "failed", code: "storeUnavailable" };
2863
+ return result;
2864
+ }
2865
+ function syncedAppStore() {
2866
+ return deps.syncAppStore().then(() => true, () => false);
2867
+ }
2831
2868
  async function buy(item) {
2832
2869
  if (unanswered2(deps.pricing.view(item.reference))) return state();
2833
2870
  const started = await startedCheckout();
@@ -2958,6 +2995,7 @@ function createIapCommerceWith(deps) {
2958
2995
  start,
2959
2996
  buy,
2960
2997
  reconcile: reconcile2,
2998
+ restore: restore2,
2961
2999
  tick: () => checkout ? checkout.tick() : Promise.resolve(state()),
2962
3000
  pollDirective,
2963
3001
  poll,
@@ -2978,6 +3016,22 @@ var SUSPENDED_AFTER_MS = 2 * PRICE_REFRESH_MS;
2978
3016
  function unanswered2(view) {
2979
3017
  return view.route === "resolving" || view.reason === "mappingUnavailable";
2980
3018
  }
3019
+ function isRecovered(item) {
3020
+ return item.completion === "finished" && item.access !== "rejected";
3021
+ }
3022
+ function isFinalRefusal2(item) {
3023
+ return item.completion === "finished" && item.access === "rejected";
3024
+ }
3025
+ function isHeldRefusal(item) {
3026
+ const unfinished = item.completion === "pending" || item.completion === "retryRequired";
3027
+ return unfinished && (item.access === "ownershipConflict" || item.access === "rejected");
3028
+ }
3029
+ function isStillPending(item) {
3030
+ return item.completion !== "finished" && item.completion !== "notFound" && !isHeldRefusal(item);
3031
+ }
3032
+ function countOf(items, matches) {
3033
+ return items.filter(matches).length;
3034
+ }
2981
3035
  function hostFor(identity, binding) {
2982
3036
  if (binding.himalayaAppId !== identity.appId || binding.bundleId !== identity.bundleId) return null;
2983
3037
  return { appId: identity.appId, bundleId: identity.bundleId, siteId: binding.siteId };
@@ -2996,6 +3050,9 @@ function createIapCommerce(options = {}) {
2996
3050
  session,
2997
3051
  host: hostOfWorker(),
2998
3052
  store: appleManagedStore,
3053
+ syncAppStore: async () => {
3054
+ await restore();
3055
+ },
2999
3056
  refs: createWorkerOrderRefStore(),
3000
3057
  clock: { now: () => Date.now() },
3001
3058
  adapters: options.adapters ?? [],