@wix/himalaya-cli 0.1386.0 → 0.1388.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 (71) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/display.d.ts +66 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/index.d.ts +185 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-auth.mjs +9 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +324 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/package.json +4 -0
  6. package/dist/cli.mjs +1249 -491
  7. package/guides/branded-lite-site-binding.md +12 -5
  8. package/guides/himi-json-output.md +12 -2
  9. package/guides/iap-products.md +91 -11
  10. package/guides/importable-sdks.md +1 -0
  11. package/guides/sdk-reference.json +253 -0
  12. package/package.json +2 -2
  13. package/templates/beauty-salon/README.md +8 -3
  14. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +1 -1
  15. package/templates/beauty-salon/tier5-src/_shared/video-caption.ts +21 -0
  16. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +31 -1
  17. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +1 -1
  18. package/templates/branded-lite/README.md +8 -3
  19. package/templates/branded-lite/dev/index.ts +982 -16
  20. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +1 -1
  21. package/templates/branded-lite/tier5-src/_shared/video-caption.ts +21 -0
  22. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +31 -1
  23. package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +23 -32
  24. package/templates/branded-lite/tier5-src/himi-branded-lite-videos/index.ts +44 -38
  25. package/templates/community-club/README.md +8 -3
  26. package/templates/community-club/tier5-src/_shared/collection-contract.ts +1 -1
  27. package/templates/community-club/tier5-src/_shared/video-caption.ts +21 -0
  28. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +31 -1
  29. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +1 -1
  30. package/templates/events-meetups/README.md +8 -3
  31. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +1 -1
  32. package/templates/events-meetups/tier5-src/_shared/video-caption.ts +21 -0
  33. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +31 -1
  34. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +1 -1
  35. package/templates/gym-fitness/README.md +8 -3
  36. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +1 -1
  37. package/templates/gym-fitness/tier5-src/_shared/video-caption.ts +21 -0
  38. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +31 -1
  39. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +1 -1
  40. package/templates/nonprofit-cause/README.md +8 -3
  41. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +1 -1
  42. package/templates/nonprofit-cause/tier5-src/_shared/video-caption.ts +21 -0
  43. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +31 -1
  44. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +1 -1
  45. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +1 -1
  46. package/templates/podcast-creator/README.md +8 -3
  47. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +1 -1
  48. package/templates/podcast-creator/tier5-src/_shared/video-caption.ts +21 -0
  49. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +31 -1
  50. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +1 -1
  51. package/templates/portfolio-creative/README.md +8 -3
  52. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +1 -1
  53. package/templates/portfolio-creative/tier5-src/_shared/video-caption.ts +21 -0
  54. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +31 -1
  55. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +1 -1
  56. package/templates/restaurant/README.md +8 -3
  57. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +1 -1
  58. package/templates/restaurant/tier5-src/_shared/video-caption.ts +21 -0
  59. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +31 -1
  60. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +1 -1
  61. package/templates/retail-store/README.md +8 -3
  62. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +1 -1
  63. package/templates/retail-store/tier5-src/_shared/video-caption.ts +21 -0
  64. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +31 -1
  65. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +1 -1
  66. package/templates/yoga-studio/README.md +8 -3
  67. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +1 -1
  68. package/templates/yoga-studio/tier5-src/_shared/video-caption.ts +21 -0
  69. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +31 -1
  70. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +1 -1
  71. package/templates/branded-lite/tier5-src/_shared/vod.ts +0 -71
@@ -16,13 +16,20 @@ on conflict: [`apps/himi-branded-lite/config.ts`](../apps/himi-branded-lite/conf
16
16
 
17
17
  If you scaffolded from the `branded-lite` template with the standalone CLI
18
18
  (`himi init my-app --template branded-lite`) and have **no monorepo checkout**, the whole
19
- site binding is **two values**, and nothing on this page below this section applies to you:
19
+ site binding is **three values**, and nothing on this page below this section applies to you:
20
20
 
21
21
  ```sh
22
22
  export WIX_CLIENT_ID="<your-headless-oauth-client-id>"
23
23
  export WIX_SITE_URL="https://your-site.wixsite.com/…"
24
+ export WIX_SITE_ID="<metasite-id>"
24
25
  ```
25
26
 
27
+ `WIX_SITE_ID` is the site that client belongs to. Reads need only the client, but a native
28
+ build matches its member sign-up CAPTCHA key, push registration and IAP binding against the
29
+ site: without it, sign-up on a CAPTCHA-protected site is refused. The member login takes it
30
+ from the environment while `WIX_CLIENT_ID` is the client the config uses
31
+ ([member-captcha-native.md](member-captcha-native.md)).
32
+
26
33
  Don't have the client id? You no longer have to go and copy it out of the dashboard —
27
34
  `himi site client ensure --site <msid> --yes` resolves the site's Headless client (reusing
28
35
  one that already carries your redirect, or minting one with it registered) and prints the
@@ -39,9 +46,9 @@ failing at redirect-session with "no auth code". `himi site list` shows the site
39
46
  already have.
40
47
 
41
48
  — or hardcode them directly in your app's [`config.ts`](../apps/himi-branded-lite/config.ts)
42
- (`WixConsumerAuthProvider({ clientId: "…", siteUrl: "…" })`). The Headless **`clientId` is the
43
- binding**; `metaSiteId` follows from the visitor token that client mints. That's it — the app now
44
- reads that site's real catalog. (Leaving the shipped `YOUR_WIX_CLIENT_ID` placeholder in place makes
49
+ (`WixConsumerAuthProvider({ clientId: "…", siteUrl: "…", siteId: "…" })`). The Headless
50
+ **`clientId` binds the data**; reads take the `metaSiteId` from the visitor token that client mints,
51
+ so the app now reads that site's real catalog. The native features above still need `siteId`. (Leaving the shipped `YOUR_WIX_CLIENT_ID` placeholder in place makes
45
52
  the app boot and read nothing; `himi validate` now warns when it's unreplaced.)
46
53
 
47
54
  Binding is **step 1 of integrating the app with the website**. To duplicate the site's
@@ -119,7 +126,7 @@ himalaya-dev app open himi-branded-lite device
119
126
  1. Set the env vars in the environment that runs the **dev-server / gen / Xcode / Gradle** process.
120
127
  2. Rebuild or re-run gen so `auth.json` picks up the new values if you are not relying on a live env override at boot.
121
128
 
122
- Off-repo (`himi init --template branded-lite`): set `WIX_CLIENT_ID` and `WIX_SITE_URL` the same way — the generated `config.ts` reads those names.
129
+ Off-repo (`himi init --template branded-lite`): set `WIX_CLIENT_ID`, `WIX_SITE_URL` and `WIX_SITE_ID` the same way — the generated `config.ts` reads the first two, and the member login pairs `WIX_SITE_ID` with `WIX_CLIENT_ID`.
123
130
 
124
131
  ### Where does MSID fit?
125
132
 
@@ -92,13 +92,23 @@ statuses are preserved: incomplete status is exit 1; a failed live Wix catalog r
92
92
 
93
93
  Item-level unavailable checks take precedence over drift in the top-level code. Detailed
94
94
  `priceSource`, `priceDrift` and `priceCheckIssue` fields live in each item's `provisioning` report.
95
- Manifest parsing failures retain their legacy envelopes; this is not a whole-command migration.
95
+ An invalid manifest, including a `reviewScreenshot` file that is missing or not a PNG or JPEG,
96
+ fails with `manifest_invalid`, and `problems` lists every reason. This is not a whole-command migration.
96
97
 
97
98
  IAP provisioning refusals from `plan`, `apply` and `status` also expose their existing typed
98
99
  `reason` as a top-level `code`: `target` (wrong app, bundle, team or territory), `access`
99
100
  (Apple authentication/authorization), `plan` (invalid reviewed plan), `account` (changed
100
101
  credential profile or key), and `drift` (Apple changed since planning). Existing `reason` and
101
- exit statuses remain intact. Manifest parsing failures still use their legacy envelope.
102
+ exit statuses remain intact. An invalid manifest carries `manifest_invalid` alongside its
103
+ existing `problems` list.
104
+
105
+ ## `himi store iap screenshots` codes
106
+
107
+ | Code | Meaning |
108
+ |---|---|
109
+ | `screenshots_incomplete` | At least one selected item has no screenshot. Each one in `items` carries a `skipped` reason: no purchase screen, no Apple price yet, an existing screenshot kept without `--force`, a screen that did not show the Apple price, or a failed read of the item. The screenshots that were made are saved and named in the manifest. Exit 1. |
110
+
111
+ An invalid manifest fails with `manifest_invalid`, like the other `store iap` commands.
102
112
 
103
113
  ## `himi store iap connect` codes
104
114
 
@@ -112,7 +112,8 @@ and nothing is crawled from a Wix catalog.
112
112
  { "locale": "en-US", "name": "Nutrition Program", "description": "Lifetime access to the nutrition plan." }
113
113
  ],
114
114
  "price": { "baseTerritory": "USA", "currency": "USD", "amount": "0.99" },
115
- "territories": ["USA", "CAN"]
115
+ "territories": ["USA", "CAN"],
116
+ "reviewScreenshot": "screens/nutrition-program.png"
116
117
  }
117
118
  ]
118
119
  }
@@ -138,6 +139,13 @@ Rules, all checked locally before any request:
138
139
  reference name at most 64, product id at most 100, review note at most 4,000.
139
140
  - **`territories`** is an explicit list that includes the base territory. Availability
140
141
  never extends to future territories automatically.
142
+ - **`reviewScreenshot`** (optional) is a PNG or JPEG of the purchase as it appears in your
143
+ app, for Apple's reviewer only; customers never see it. The path is relative to the
144
+ manifest's folder (or absolute). Without a screenshot, a product stays `MISSING_METADATA`
145
+ and can't be submitted. Every named file must exist and be a PNG or JPEG before `plan`
146
+ sends a request. Apple accepts any App Store screenshot size your app supports, so a
147
+ full-size iPhone screenshot is safest. `plan` warns about a size it doesn't recognize,
148
+ and Apple decides.
141
149
  - **Duplicates are refused:** a product id, Wix entity, entitlement or reference name
142
150
  used twice, including a Wix entity mapped both directly and through a tier.
143
151
  - **Unknown keys are refused, at every level.** So is any string that looks like a key or a token.
@@ -149,6 +157,7 @@ himi store iap draft --site <msid> --profile fit-club-owner --apple-app 6450000
149
157
  himi store iap plan --manifest ./iap-products.json --profile fit-club-owner --out ./iap-plan.json
150
158
  himi store iap apply --plan ./iap-plan.json --profile fit-club-owner --out ./iap-result.json
151
159
  himi store iap status --manifest ./iap-products.json --profile fit-club-owner --json
160
+ himi store iap screenshots --manifest ./iap-products.json --content . --profile fit-club-owner
152
161
  himi store iap connect --manifest ./iap-products.json --profile fit-club-owner \
153
162
  --iap-key ~/keys/SubscriptionKey_ABCD123456.p8 --iap-key-id ABCD123456 --content . --dry-run
154
163
  ```
@@ -172,7 +181,7 @@ A **conflict** is any of:
172
181
 
173
182
  - the same product id with another type;
174
183
  - the reference name already used by another product id;
175
- - a live product whose metadata or availability differs, or an in-review product needing changes;
184
+ - a live product whose metadata, availability or review screenshot differs, or an in-review product needing changes;
176
185
  - an existing `exact` price or availability that differs;
177
186
  - a price change with a different base territory, ambiguous price, custom territory prices, or a future/temporary schedule;
178
187
  - no Apple point satisfying the requested price rule, or an unknown product/review state needing changes.
@@ -200,12 +209,78 @@ created. The plan marks this in the item's `notes`.
200
209
  - If anything changed that the plan did not list (**drift**), it stops before the first
201
210
  write. Planned work that already happened is accepted as done.
202
211
  4. For each product, it creates the product, then a draft version, then its
203
- localizations. Then it sets the exact initial price and the explicit territories. Then
204
- it reads everything back. A `changePrice` instead writes an immediate base-territory price
212
+ localizations. Then it sets the exact initial price and the explicit territories, and
213
+ uploads the review screenshot. Then it reads everything back. A `changePrice` instead writes an immediate base-territory price
205
214
  through the same price-schedule API; Apple equalizes the other territories. A changed source
206
215
  price, schedule, review state or resolved target stops apply before the first write.
207
216
  5. Writes the result file, even when the run is partial.
208
217
 
218
+ **The review screenshot** is uploaded the way App Store Connect expects. Apply reserves it,
219
+ PUTs each part to the upload URLs Apple returns, and commits the file's MD5. Apple's storage
220
+ hosts only ever get the file's bytes, never the API key's token.
221
+
222
+ - **Identified by checksum.** Apple keeps the MD5 as `sourceFileChecksum`. A screenshot with
223
+ the same checksum is reused. A different file, an unfinished upload, or an image Apple
224
+ refused is planned as `replaceReviewScreenshot`.
225
+ - **Never deleted while valid.** Apple keeps one screenshot per product, and documents it as
226
+ "can be updated but not removed". A replacement asks Apple for a new upload over the old one;
227
+ if Apple refuses a second one, the step fails and says to replace it in App Store Connect. Only
228
+ what an interrupted or refused upload left behind (no finished image) is cleared first.
229
+ - **Locked once live.** The screenshot of a live or in-review product is never changed. A
230
+ different file there is a conflict.
231
+ - **Bytes fixed at plan time.** The plan records each file's checksum, and apply re-reads
232
+ the file. A file edited after `plan` stops apply as drift before any write.
233
+ - **Read back.** Apple processes the image after the commit, and apply waits briefly for
234
+ the result. A refused image (`FAILED`) fails the step with Apple's reason. An image still
235
+ processing (`UPLOAD_COMPLETE`) counts as uploaded.
236
+
237
+ ### Making the review screenshots: `screenshots`
238
+
239
+ `himi store iap screenshots --manifest <file> --content <app dir> [--profile <name>]` makes each
240
+ product's review screenshot from the app's own purchase screen, so nobody has to take them on a phone:
241
+
242
+ 1. **It renders the screen.** For each item it opens that Wix item's detail screen: `himi_product_<id>`,
243
+ `himi_program_<id>` or `himi_plan_<id>`, or the plain screen given the `id` when the app has no such
244
+ route. For a plan it falls back to the plans list (`himi_plans`), where older apps sell plans. The
245
+ screen loads with live Wix data.
246
+ 2. **It renders as the device would.** The render answers what a bound iPhone answers for a signed-in member of the site:
247
+ - the app's purchases binding;
248
+ - the item's App Store mapping;
249
+ - Apple's price;
250
+ - the member session. The stand-in member is new, so its own plan orders read as none.
251
+
252
+ The screen's own pricing and checkout code then paints the Apple price and an enabled buy button,
253
+ as the member sees them. Nothing is sent to wix-iap or Apple, and no member is signed in anywhere.
254
+ 3. **It checks the result.** A picture is kept only when three things hold:
255
+ - the screen shows Apple's price;
256
+ - its own state offers the purchase, meaning an enabled buy action, or an enabled join on a plan row;
257
+ - its read of the item succeeded.
258
+
259
+ A screen that fell back to "not available", showed no buy action, or failed to load the item is
260
+ reported, never saved.
261
+ 4. **It saves.** It writes `review-screenshots/<productId>.png` next to the manifest, at 1290×2796
262
+ (the largest-iPhone App Store size). It then sets each item's `reviewScreenshot` to that file,
263
+ leaving the rest of the manifest as it was.
264
+
265
+ **Apple's price** comes from App Store Connect when you pass `--profile`, a read-only call. Otherwise it is
266
+ the manifest's `exact` amount. An `atLeast` item that Apple has no price for yet is skipped: run `apply`
267
+ first. A price tier has no single purchase screen and is skipped too.
268
+
269
+ **Other options:**
270
+ - With `--profile`, a product that is live or in review keeps its screenshot: changing it there is a
271
+ conflict anyway.
272
+ - An item that already names a screenshot is kept unless you pass `--force`.
273
+ - `--only <productId,…>` limits the run; an id the manifest doesn't have is reported.
274
+ - `--dry-run` lists what would be rendered.
275
+ - `--out-dir` puts the files elsewhere.
276
+
277
+ The render reads Wix with a visitor token. A screen whose item a visitor cannot read is reported with
278
+ the failing read; take that screenshot on a phone, or fix the screen. A plan detail screen that reads
279
+ `GET /pricing-plans/v2/plans/<id>` gets a 403 as a visitor, while the public plans list works. The
280
+ templates read the public list since #4256.
281
+
282
+ Then run `plan` and `apply` as usual: they upload the screenshots.
283
+
209
284
  **Large catalogs.** App Store Connect allows about 3,600 API requests an hour per key.
210
285
  Registering a new product takes about ten, and each run also lists the app's products once.
211
286
 
@@ -225,8 +300,9 @@ Apple again before doing anything else:
225
300
  - A write is repeated **at most once**, and only after that read proves it didn't land.
226
301
  - An outcome that is still unknown stops that product as `partial`.
227
302
 
228
- So rerunning the same plan never creates a second product, version, locale or price
229
- schedule, even without the local journal. Nothing is ever deleted or rolled back.
303
+ So rerunning the same plan never creates a second product, version, locale, price
304
+ schedule or screenshot, even without the local journal. Nothing is rolled back, and the only
305
+ thing ever deleted is an unfinished or refused screenshot upload, never a finished screenshot.
230
306
 
231
307
  **`status` is read-only.** It reports what Apple has for each product. Add `--out <file>`
232
308
  to save it in the same shape as the result file.
@@ -333,6 +409,8 @@ Each item has two parts:
333
409
  - per-step states and any conflicts
334
410
  - `unmet` — where Apple still differs from the manifest
335
411
  - the requested and observed price, availability and locales
412
+ - `reviewScreenshot` — for an item that names one, the requested file name and the file
413
+ name and processing state Apple holds
336
414
  - the draft version and Apple's review state
337
415
  - `remainingRequirements`
338
416
  - `lastReadAt`
@@ -354,12 +432,14 @@ future importer must authenticate ownership and re-read Apple before activating
354
432
 
355
433
  ## What is still yours to do
356
434
 
357
- - **Finish the product in App Store Connect.** Upload the App Review screenshot and add
358
- anything else App Store Connect lists as missing. `remainingRequirements` names what
359
- Apple reports.
435
+ - **Finish the product in App Store Connect.** Add anything App Store Connect still lists
436
+ as missing. `remainingRequirements` names what Apple reports, including a missing review
437
+ screenshot when the item names none.
360
438
  - **Handle the business side.** Paid Apps agreement, banking and tax information go
361
439
  through the owner's App Store Connect account.
362
- - **Submit for review.** Submit the product with an app version. This command never
440
+ - **Submit for review.** Your first in-app purchases must be submitted together with an
441
+ app version: attach them to the version in App Store Connect before you submit it (see
442
+ `/submit-ios-app`). Later ones can be submitted on their own. This command never
363
443
  submits, and never makes a product available for sale.
364
444
  - **Allow for Sandbox delay.** Product changes can take a while to appear in Sandbox
365
445
  purchase testing. Sandbox is for purchases, not a separate product catalogue:
@@ -368,7 +448,7 @@ future importer must authenticate ownership and re-read Apple before activating
368
448
  Not in this version:
369
449
 
370
450
  - subscriptions
371
- - review screenshot upload and submission
451
+ - submitting products for review
372
452
  - changing a live price
373
453
  - Google Play
374
454
  - the build-time binding bake and the screens' checkout (the companion PR)
@@ -129,4 +129,5 @@ direction the platform is heading — recommended, not required). Usage is unifo
129
129
  - `@himalaya/wix-stores-collections` — Stores — product collections
130
130
  - `@himalaya/wix-table-reservations` — Restaurant table reservations
131
131
  - `@himalaya/wix-users` — Current Wix user / account profile
132
+ - `@himalaya/wix-video` — Wix Video — channels, videos, covers, durations, playback (native apps; the web bridges refuse its gateway)
132
133
  - `@himalaya/wix-workflows` — Wix workflows — typed REST client
@@ -24613,6 +24613,259 @@
24613
24613
  }
24614
24614
  ]
24615
24615
  },
24616
+ {
24617
+ "name": "@himalaya/wix-video",
24618
+ "source": "stdlib/wix-sdk/video/src/index.ts",
24619
+ "description": "Wix Video — channels, videos, covers, durations, playback (native apps; the web bridges refuse its gateway)",
24620
+ "api": [
24621
+ {
24622
+ "name": "isHostedVideo",
24623
+ "kind": "function",
24624
+ "jsdoc": "/** True for a video whose file Wix hosts (it plays through `getVideoStreamUrl`). */",
24625
+ "decl": "export function isHostedVideo(video: WixVideo): boolean;"
24626
+ },
24627
+ {
24628
+ "name": "isVideoReady",
24629
+ "kind": "function",
24630
+ "jsdoc": "/** True when the video is published AND its media is ready to play. */",
24631
+ "decl": "export function isVideoReady(video: WixVideo): boolean;"
24632
+ },
24633
+ {
24634
+ "name": "isVideoProcessing",
24635
+ "kind": "function",
24636
+ "jsdoc": "/** True while an upload is still being processed (try again later). */",
24637
+ "decl": "export function isVideoProcessing(video: WixVideo): boolean;"
24638
+ },
24639
+ {
24640
+ "name": "youtubeVideoId",
24641
+ "kind": "function",
24642
+ "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. */",
24643
+ "decl": "export function youtubeVideoId(video: WixVideo): string;"
24644
+ },
24645
+ {
24646
+ "name": "videoCoverUrl",
24647
+ "kind": "function",
24648
+ "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 */",
24649
+ "decl": "export function videoCoverUrl(video: WixVideo, size: WixImageSize | null = WIX_LIST_IMAGE): string;"
24650
+ },
24651
+ {
24652
+ "name": "channelCoverUrl",
24653
+ "kind": "function",
24654
+ "jsdoc": "/** The channel's cover image, sized for display, or \"\". */",
24655
+ "decl": "export function channelCoverUrl(channel: WixVideoChannel, size: WixImageSize | null = WIX_LIST_IMAGE): string;"
24656
+ },
24657
+ {
24658
+ "name": "videoDurationSec",
24659
+ "kind": "function",
24660
+ "jsdoc": "/** The video's length in whole seconds, or undefined when the gateway gives none. */",
24661
+ "decl": "export function videoDurationSec(video: WixVideo): number | undefined;"
24662
+ },
24663
+ {
24664
+ "name": "formatVideoDuration",
24665
+ "kind": "function",
24666
+ "jsdoc": "/** \"0:23\", \"4:05\", \"1:02:03\". \"\" for an unknown length. */",
24667
+ "decl": "export function formatVideoDuration(seconds: number | undefined): string;"
24668
+ },
24669
+ {
24670
+ "name": "isPaidVideo",
24671
+ "kind": "function",
24672
+ "jsdoc": "/** True when the video has an enabled SALE or RENT price. */",
24673
+ "decl": "export function isPaidVideo(video: WixVideo): boolean;"
24674
+ },
24675
+ {
24676
+ "name": "canWatchVideo",
24677
+ "kind": "function",
24678
+ "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 */",
24679
+ "decl": "export function canWatchVideo(channel: WixVideoChannel, video: WixVideo, now = Date.now()): boolean;"
24680
+ },
24681
+ {
24682
+ "name": "latestVideos",
24683
+ "kind": "function",
24684
+ "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 */",
24685
+ "decl": "export function latestVideos(channels: WixVideoChannel[], limit = 8): WixVideoInChannel[];"
24686
+ },
24687
+ {
24688
+ "name": "WixVideoCard",
24689
+ "kind": "interface",
24690
+ "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. */",
24691
+ "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}"
24692
+ },
24693
+ {
24694
+ "name": "videoCard",
24695
+ "kind": "function",
24696
+ "jsdoc": "/** Shape one video for a card. `untitled` replaces an empty title. */",
24697
+ "decl": "export function videoCard(entry: WixVideoInChannel, untitled = \"Video\", size: WixImageSize | null = WIX_LIST_IMAGE): WixVideoCard;"
24698
+ },
24699
+ {
24700
+ "name": "WIX_VIDEO_GRAPHQL_URL",
24701
+ "kind": "const",
24702
+ "jsdoc": "/** The Wix Video GraphQL gateway (visitor and member reads). */",
24703
+ "decl": "export const WIX_VIDEO_GRAPHQL_URL = \"https://manage.wix.com/_api/vod/public/graphql\";"
24704
+ },
24705
+ {
24706
+ "name": "WixCallOptions",
24707
+ "kind": "interface",
24708
+ "jsdoc": "/** Per-call options. `site` reads a business other than the app's active one. */",
24709
+ "decl": "export interface WixCallOptions {\n site?: string;\n}"
24710
+ },
24711
+ {
24712
+ "name": "WixVideoPricing",
24713
+ "kind": "interface",
24714
+ "jsdoc": "/** One price option of a channel (`SUBSCRIPTION` | `PRICING_PLAN`) or a video (`SALE` | `RENT`). */",
24715
+ "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}"
24716
+ },
24717
+ {
24718
+ "name": "WixVideoPurchase",
24719
+ "kind": "interface",
24720
+ "jsdoc": "/** The viewer's purchase of a channel subscription or of one video. */",
24721
+ "decl": "export interface WixVideoPurchase {\n /** `ACTIVE` | `PENDING` | `CANCELED` | `EXPIRED` | … */\n status?: string;\n expiresAt?: string;\n}"
24722
+ },
24723
+ {
24724
+ "name": "WixVideoMedia",
24725
+ "kind": "interface",
24726
+ "jsdoc": "/** The uploaded or linked media of a video. */",
24727
+ "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}"
24728
+ },
24729
+ {
24730
+ "name": "WixVideoImage",
24731
+ "kind": "interface",
24732
+ "jsdoc": "/** A selectable image (cover or poster). `selected` is the image URL. */",
24733
+ "decl": "export interface WixVideoImage {\n selected?: string | null;\n}"
24734
+ },
24735
+ {
24736
+ "name": "WixVideoPage",
24737
+ "kind": "interface",
24738
+ "jsdoc": "/** A page of a paginated list. */",
24739
+ "decl": "export interface WixVideoPage<T> {\n total?: number;\n cursor?: string | null;\n entities?: T[];\n}"
24740
+ },
24741
+ {
24742
+ "name": "WixVideo",
24743
+ "kind": "interface",
24744
+ "jsdoc": "",
24745
+ "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}"
24746
+ },
24747
+ {
24748
+ "name": "WixVideoChannel",
24749
+ "kind": "interface",
24750
+ "jsdoc": "",
24751
+ "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}"
24752
+ },
24753
+ {
24754
+ "name": "WixVideoInChannel",
24755
+ "kind": "interface",
24756
+ "jsdoc": "/** A video together with the channel it belongs to. */",
24757
+ "decl": "export interface WixVideoInChannel {\n channel: WixVideoChannel;\n video: WixVideo;\n}"
24758
+ },
24759
+ {
24760
+ "name": "WixVideoErrorFlags",
24761
+ "kind": "interface",
24762
+ "jsdoc": "/** Flags every error this client throws carries, so a caller can branch without\n * matching message text. */",
24763
+ "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}"
24764
+ },
24765
+ {
24766
+ "name": "WixVideoError",
24767
+ "kind": "type",
24768
+ "jsdoc": "",
24769
+ "decl": "export type WixVideoError = Error & WixVideoErrorFlags;"
24770
+ },
24771
+ {
24772
+ "name": "wixVideoError",
24773
+ "kind": "function",
24774
+ "jsdoc": "/** Build an error carrying the `WixVideoErrorFlags` (each defaults to false). */",
24775
+ "decl": "export function wixVideoError(message: string, flags: Partial<WixVideoErrorFlags> = {}): WixVideoError;"
24776
+ },
24777
+ {
24778
+ "name": "isWixVideoWebRefusal",
24779
+ "kind": "function",
24780
+ "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. */",
24781
+ "decl": "export function isWixVideoWebRefusal(error: unknown): boolean;"
24782
+ },
24783
+ {
24784
+ "name": "wixVideoQuery",
24785
+ "kind": "function",
24786
+ "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 */",
24787
+ "decl": "export async function wixVideoQuery<T>(query: string, variables: Record<string, unknown> = {}, opts?: WixCallOptions): Promise<T>;",
24788
+ "scope": "visitor"
24789
+ },
24790
+ {
24791
+ "name": "wixVideoPages",
24792
+ "kind": "function",
24793
+ "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. */",
24794
+ "decl": "export async function wixVideoPages<T extends { id: string }>(\n fetchPage: (cursor: string | null) => Promise<WixVideoPage<T> | null | undefined>,\n): Promise<T[]>;"
24795
+ },
24796
+ {
24797
+ "name": "CHANNEL_FIELDS",
24798
+ "kind": "const",
24799
+ "jsdoc": "/** The channel fields a list or card needs. */",
24800
+ "decl": "export const CHANNEL_FIELDS = \"id title description source featuredVideoId hasPaidVideos cover { selected } videos { total }\";"
24801
+ },
24802
+ {
24803
+ "name": "CHANNEL_ACCESS_FIELDS",
24804
+ "kind": "const",
24805
+ "jsdoc": "/** The channel fields an access decision needs (`canWatchVideo`). */",
24806
+ "decl": "export const CHANNEL_ACCESS_FIELDS = \"id title pricing { type enabled required } purchase(filter: {type: SUBSCRIPTION}) { status expiresAt }\";"
24807
+ },
24808
+ {
24809
+ "name": "VIDEO_CARD_FIELDS",
24810
+ "kind": "const",
24811
+ "jsdoc": "/** The video fields a card needs: title, images, length and processing state. */",
24812
+ "decl": "export const VIDEO_CARD_FIELDS = \"id title source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled }\";"
24813
+ },
24814
+ {
24815
+ "name": "VIDEO_FIELDS",
24816
+ "kind": "const",
24817
+ "jsdoc": "/** The video fields playback and an access decision need. */",
24818
+ "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 }\";"
24819
+ },
24820
+ {
24821
+ "name": "ListChannelsOptions",
24822
+ "kind": "interface",
24823
+ "jsdoc": "",
24824
+ "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}"
24825
+ },
24826
+ {
24827
+ "name": "listChannels",
24828
+ "kind": "function",
24829
+ "jsdoc": "/**\n * Every channel of the site, in the owner's order (all pages).\n *\n * @scope visitor\n */",
24830
+ "decl": "export async function listChannels(opts: ListChannelsOptions = {}): Promise<WixVideoChannel[]>;",
24831
+ "scope": "visitor"
24832
+ },
24833
+ {
24834
+ "name": "listChannelVideos",
24835
+ "kind": "function",
24836
+ "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 */",
24837
+ "decl": "export async function listChannelVideos(channelId: string, opts?: WixCallOptions): Promise<{ channel: WixVideoChannel; videos: WixVideo[] }>;",
24838
+ "scope": "visitor"
24839
+ },
24840
+ {
24841
+ "name": "getVideo",
24842
+ "kind": "function",
24843
+ "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 */",
24844
+ "decl": "export async function getVideo(channelId: string, videoId: string, opts?: WixCallOptions): Promise<{ channel: WixVideoChannel; video: WixVideo | null } | null>;",
24845
+ "scope": "visitor"
24846
+ },
24847
+ {
24848
+ "name": "getVideoStreamUrl",
24849
+ "kind": "function",
24850
+ "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 */",
24851
+ "decl": "export async function getVideoStreamUrl(channelId: string, videoId: string, opts?: WixCallOptions): Promise<string>;",
24852
+ "scope": "visitor"
24853
+ },
24854
+ {
24855
+ "name": "ListLatestVideosOptions",
24856
+ "kind": "interface",
24857
+ "jsdoc": "",
24858
+ "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}"
24859
+ },
24860
+ {
24861
+ "name": "listLatestVideos",
24862
+ "kind": "function",
24863
+ "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 */",
24864
+ "decl": "export async function listLatestVideos(opts: ListLatestVideosOptions = {}): Promise<WixVideoInChannel[]>;",
24865
+ "scope": "visitor"
24866
+ }
24867
+ ]
24868
+ },
24616
24869
  {
24617
24870
  "name": "@himalaya/wix-workflows",
24618
24871
  "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.1386.0",
3
+ "version": "0.1388.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": "e3ab011108d918e98ce9eef090fd3e84e58228b81b990dce0393b625"
74
+ "falconPackageHash": "6ea552221f975dd6438104d636817ced2f4d49d9cbb69e0e8501fb91"
75
75
  }
@@ -9,12 +9,17 @@ identity and navigation in `config.ts`, look and feel in `tokens.json`.
9
9
 
10
10
  ## Point at your Wix site
11
11
 
12
- Your **Wix Headless `clientId` IS the site binding** — there is no separate site id to
13
- pass, because the visitor token minted from it already names your site. Set these before
14
- `himi build` / preview so calls hit YOUR site (the placeholders are not a real backend):
12
+ Your **Wix Headless `clientId` binds the data** — the visitor token minted from it already
13
+ names your site, so reads need nothing else. Set these before `himi build` / preview so calls
14
+ hit YOUR site (the placeholders are not a real backend):
15
15
 
16
16
  export WIX_CLIENT_ID="<headless-oauth-client-id>"
17
17
  export WIX_SITE_URL="https://your-site.wixsite.com/…"
18
+ export WIX_SITE_ID="<metasite-id>"
19
+
20
+ `WIX_SITE_ID` is the site that client belongs to. The member login takes it from the environment
21
+ while `WIX_CLIENT_ID` is the client the config uses. A native build needs it for member sign-up on
22
+ a CAPTCHA-protected site, push registration and in-app purchases.
18
23
 
19
24
  Full details, including how to ship a binary a customer can install against their own
20
25
  site: `himi docs branded-lite-site-binding`.
@@ -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' },
@@ -0,0 +1,21 @@
1
+ // The video carousel's two looks: the caption ON the cover (a brand label chip over the
2
+ // image) or UNDER it. Every slide carries both placements; the descriptor's
3
+ // `videoCaption` appear param picks one, and these flags hide the other
4
+ // (`hiddenWhen` hides on TRUE). So a screen switches look by changing ONE param,
5
+ // with no second carousel and no change to the slide tree.
6
+ export type VideoCaption = "overlay" | "below";
7
+
8
+ /** The placement a param asks for. Anything but "below" is the default, "overlay". */
9
+ export function videoCaptionOf(value: unknown): VideoCaption {
10
+ return value === "below" ? "below" : "overlay";
11
+ }
12
+
13
+ export interface CaptionFlags {
14
+ captionOnImage: boolean;
15
+ captionBelow: boolean;
16
+ }
17
+
18
+ /** Stamp each card with the gates its slide reads (`$item.captionOnImage` / `$item.captionBelow`). */
19
+ export function withCaption<T extends object>(cards: T[], caption: VideoCaption): (T & CaptionFlags)[] {
20
+ return cards.map((card) => ({ ...card, captionOnImage: caption === "overlay", captionBelow: caption === "below" }));
21
+ }