@wix/himalaya-cli 0.1608.0 → 0.1610.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-config.d.ts +7 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-adapters/wix-video.d.ts +20 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/catalog-pricing-contract.d.ts +1 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/index.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/mapping-source.d.ts +1 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/wix-payments-contract.d.ts +11 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/wix-platform/src/index.d.ts +14 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/wix-platform/src/user-props.d.ts +8 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/iap-verticals/index.d.ts +1 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/iap-verticals/videos.d.ts +5 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/index.d.ts +1 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/online-programs/src/index.d.ts +122 -1
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/video/src/index.d.ts +6 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +25 -4
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +25 -5
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +8 -3
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-online-programs.mjs +118 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-platform.mjs +65 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-video.mjs +3 -3
- package/dist/cli.mjs +157 -46
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/form-submit.service.ts +22 -2
- package/guides/iap-pricing.md +1 -0
- package/guides/iap-products.md +18 -7
- package/guides/iap-screen-adoption.md +2 -1
- package/guides/qa-builds.md +14 -1
- package/guides/recipe-dynamic-list.md +16 -0
- package/guides/recipe-form-submit.md +32 -2
- package/guides/recipe-four-state.md +2 -1
- package/guides/sdk-reference.json +144 -10
- package/guides/web-page-sdk-reference.md +1 -1
- package/guides/wix-platform-usage.md +38 -0
- package/package.json +2 -2
- package/templates/branded-lite/dev/index.ts +107 -3
- package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +62 -7
- package/templates/branded-lite/tier5-src/himi-branded-lite-videos/index.ts +82 -26
|
Binary file
|
|
@@ -46,12 +46,13 @@ function shape(s: Partial<PostFormState>): PostFormState {
|
|
|
46
46
|
// Submit stays available after every class: the user retries once they reconnect,
|
|
47
47
|
// sign in, join the site or are granted access — without restarting the form.
|
|
48
48
|
type ErrorKind = "" | "offline" | "server" | "signIn" | "membership" | "permission";
|
|
49
|
-
const SAVE_FAILED: Record<Exclude<ErrorKind, ""
|
|
49
|
+
const SAVE_FAILED: Record<Exclude<ErrorKind, ""> | "unconfirmed", string> = {
|
|
50
50
|
offline: "You're offline, so nothing was saved. Try again when you reconnect.",
|
|
51
51
|
server: "Couldn't save your changes. Try again.",
|
|
52
52
|
signIn: "Your session has ended. Sign in, then save again.",
|
|
53
53
|
membership: "Join this site before saving.",
|
|
54
54
|
permission: "You don't have permission to save this.",
|
|
55
|
+
unconfirmed: "Wix didn't confirm this, so it may or may not have been saved. Check before trying again.",
|
|
55
56
|
};
|
|
56
57
|
|
|
57
58
|
// The classifier below is `himi docs recipe-four-state`'s, VERBATIM — a test holds
|
|
@@ -106,6 +107,23 @@ function classifyError(e: unknown, authState: "signedIn" | "signedOut" | "unknow
|
|
|
106
107
|
// "unknown": a 401 then reads as a session that ended.
|
|
107
108
|
function currentAuthState(): "signedIn" | "signedOut" | "unknown" { return "unknown"; }
|
|
108
109
|
|
|
110
|
+
// A failure with no HTTP answer may come after Wix took the write (a reset, a timeout, a bad or
|
|
111
|
+
// truncated response); only one while CONNECTING proves nothing was sent. A 502–504 may have
|
|
112
|
+
// landed too — the owner templates' `writeMayHaveLanded` rule.
|
|
113
|
+
const NO_ANSWER = /NSURLErrorDomain|Error Domain=|\bjava\.(?:net|io)\.\w+|(?:UnknownHost|Connect|SocketTimeout|NoRouteToHost|SSLHandshake)Exception|^Failed to connect|^Unable to resolve host|Connection (?:refused|reset)|Network is unreachable|Software caused connection abort|ECONNRESET|ETIMEDOUT|EPIPE|Broken pipe|stream was reset|unexpected end of stream|timed out|^timeout$|Failed to fetch|NetworkError|^(?:TypeError: )?Load failed$|net::ERR_|^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i;
|
|
114
|
+
const NEVER_SENT = /NSURLErrorDomain\D*?-10(?:09|04|03|06|20|18)\b|\b(?:UnknownHost|Connect|NoRouteToHost)Exception\b|^Failed to connect to \S|^Unable to resolve host\b|\b(?:Connection refused|Network is unreachable)\b|net::ERR_(?:INTERNET_DISCONNECTED|NAME_NOT_RESOLVED|CONNECTION_REFUSED|ADDRESS_UNREACHABLE|NETWORK_ACCESS_DENIED)\b|^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i;
|
|
115
|
+
|
|
116
|
+
function writeMayHaveLanded(e: unknown): boolean {
|
|
117
|
+
const code = String((e as { code?: unknown })?.code ?? "");
|
|
118
|
+
const msg = String((e as Error)?.message ?? e ?? "");
|
|
119
|
+
if (code === "offline_write_blocked") return false; // refused before it left
|
|
120
|
+
if (code === "offline_write_outcome_unknown" || /"sent"\s*:\s*true/.test(msg)) return true;
|
|
121
|
+
// The web host's own envelope without "sent": true failed before the request left it.
|
|
122
|
+
if (/\bwebwix_(?:direct_failed|owner_disabled|bridge_disabled)\b/.test(msg)) return false;
|
|
123
|
+
if (/\b(?:HTTP|Wix) \d{3}\b/.test(msg)) return /\b(?:HTTP|Wix) (?:502|503|504)\b/.test(msg);
|
|
124
|
+
return NO_ANSWER.test(msg) && !NEVER_SENT.test(msg);
|
|
125
|
+
}
|
|
126
|
+
|
|
109
127
|
// Blog draft bodies are rich content — wrap the plain text minimally.
|
|
110
128
|
function toRichContent(text: string): WixRichContent {
|
|
111
129
|
return { nodes: [{ type: "PARAGRAPH", nodes: [{ type: "TEXT", textData: { text, decorations: [] } }] }] };
|
|
@@ -143,9 +161,11 @@ const svc = defineService<PostFormState, {
|
|
|
143
161
|
});
|
|
144
162
|
} catch (e) {
|
|
145
163
|
const kind = classifyError(e, currentAuthState()) || "server";
|
|
164
|
+
// A failure after the request left may still have created the draft: say so.
|
|
165
|
+
const outcome = writeMayHaveLanded(e) ? "unconfirmed" : kind;
|
|
146
166
|
// Echo what the user typed back into the prefill, so a failed save loses nothing.
|
|
147
167
|
// Clear the last success line: a failed attempt must not also read "Draft created.".
|
|
148
|
-
return shape({ ...s, title, body, status: "error", error: SAVE_FAILED[
|
|
168
|
+
return shape({ ...s, title, body, status: "error", error: SAVE_FAILED[outcome], savedMessage: "" });
|
|
149
169
|
}
|
|
150
170
|
},
|
|
151
171
|
},
|
package/guides/iap-pricing.md
CHANGED
|
@@ -50,6 +50,7 @@ when it registers the item with Apple:
|
|
|
50
50
|
| `onlineProgram` | An Online Programs program | `onlineProgramItem(program, commercePrice)` |
|
|
51
51
|
| `storesProduct` | A Wix Stores product | Build `{ kind: "storesProduct", id }` |
|
|
52
52
|
| `pricingPlan` | A one-time Pricing Plans plan | Build `{ kind: "pricingPlan", id }` |
|
|
53
|
+
| `wixVideo` | A Wix video with a one-time Sale price, in a channel that requires no subscription or plan | `wixVideoItem(channel, video)`; `undefined` for a free, rental-only or subscription-locked video |
|
|
53
54
|
|
|
54
55
|
## Add it to a worker
|
|
55
56
|
|
package/guides/iap-products.md
CHANGED
|
@@ -199,8 +199,8 @@ himi store iap draft --site <msid> --purchases-app <registration-id> --catalog-r
|
|
|
199
199
|
## Drafting the manifest from your Wix catalog
|
|
200
200
|
|
|
201
201
|
You don't have to write the manifest by hand. `draft` reads the site's whole catalog —
|
|
202
|
-
every page of Wix Stores (Catalog V3 or V1), Online Programs
|
|
203
|
-
a manifest of everything that can be sold:
|
|
202
|
+
every page of Wix Stores (Catalog V3 or V1), Online Programs, Pricing Plans and Wix Video — and
|
|
203
|
+
writes a manifest of everything that can be sold:
|
|
204
204
|
|
|
205
205
|
```bash
|
|
206
206
|
himi store iap draft --site <msid> --purchases-app <registration-id> --catalog-route <assigned-prefix> --apple-app 6450000001 \
|
|
@@ -210,8 +210,11 @@ himi store iap draft --site <msid> --purchases-app <registration-id> --catalog-r
|
|
|
210
210
|
| Wix item | Drafted as |
|
|
211
211
|
|---|---|
|
|
212
212
|
| Digital store product, paid online program, one-time "unlimited" pricing plan | a non-consumable |
|
|
213
|
+
| Wix video with a one-time Sale price above zero | a non-consumable, keyed by the video's 36-character id |
|
|
213
214
|
| Physical product | skipped: physical goods are never sold through in-app purchase |
|
|
214
215
|
| Recurring or time-limited pricing plan | skipped: App Store subscriptions are a separate API |
|
|
216
|
+
| Rental-only video | skipped: a rental gives access for a limited time, which a one-time purchase cannot sell |
|
|
217
|
+
| Video in a channel that requires a subscription or pricing plan | skipped: buying the video would not open it there |
|
|
215
218
|
| Free, hidden (unpublished, archived or not visible) or unclassified item | skipped |
|
|
216
219
|
| Item priced in another currency than the base territory | skipped |
|
|
217
220
|
| Proven paid online group services and online-only event tickets | per-item consumables, with separate `.booking.` / `.ticket.` namespaces |
|
|
@@ -222,8 +225,12 @@ and supported forms are implemented, and their Apple sales are on since 2026-11-
|
|
|
222
225
|
([activation record](iap-bookings-events.md#activation--2026-11-03)). Provisioning support
|
|
223
226
|
does not establish live checkout or shared-tier mapping support.
|
|
224
227
|
|
|
225
|
-
- **Product ids come from the Wix id** (`<bundle>.product.<wix-id>`, and `.program
|
|
226
|
-
`.plan.` for those sources), so drafting the same catalog again gives the same ids.
|
|
228
|
+
- **Product ids come from the Wix id** (`<bundle>.product.<wix-id>`, and `.program.`,
|
|
229
|
+
`.plan.` or `.video.` for those sources), so drafting the same catalog again gives the same ids.
|
|
230
|
+
- **Wix Video is read through its GraphQL gateway** (`manage.wix.com/_api/vod/public/graphql`;
|
|
231
|
+
Wix publishes no REST API for it) with the same site credential. A site without Wix Video simply
|
|
232
|
+
has no channels. When the gateway refuses the read (some credentials, such as an API key, may not
|
|
233
|
+
reach it), the draft says so in its notes ("no videos were drafted") and drafts the rest.
|
|
227
234
|
- **Prices use the rule `atLeast`:** the lowest Apple price point at or above the Wix
|
|
228
235
|
price. `plan` shows the Apple price each new product will get before anything is written.
|
|
229
236
|
- **Names and descriptions are cut to Apple's limits,** and HTML becomes plain text.
|
|
@@ -503,7 +510,9 @@ Status labels `provisioning.priceSource` as `manifest` by default. `--refresh-wi
|
|
|
503
510
|
authentication and reads the current site catalog without changing the manifest. It reports
|
|
504
511
|
`provisioning.priceDrift: { wix, apple, wouldBe, currency, baseTerritory, source }` when Apple's price
|
|
505
512
|
differs from the resolved target. Normal rounding is not drift. Missing or incomparable data is
|
|
506
|
-
reported as `priceCheckIssue`; a failed catalog read never falls back to the manifest.
|
|
513
|
+
reported as `priceCheckIssue`; a failed catalog read never falls back to the manifest. When the Wix
|
|
514
|
+
Video read fails and the manifest maps videos, `status --refresh-wix` and `draft --merge` fail with
|
|
515
|
+
`wix_catalog_unavailable` instead of reporting those videos as missing. A live check
|
|
507
516
|
does not reprice shared tiers. Drift or unavailable checks make the overall status incomplete.
|
|
508
517
|
|
|
509
518
|
Only `atLeast` mismatches can plan a price edit. An `exact` mismatch remains a conflict even when
|
|
@@ -538,8 +547,10 @@ In order:
|
|
|
538
547
|
of the provider's state, so a rerun connects it again (the call is safe to repeat), and a dry
|
|
539
548
|
run for an app that is already registered reports this step `unverified` instead of planned.
|
|
540
549
|
4. **One mapping per Wix item → Apple product, through Catalog `listProducts` / `createProduct`.** Online Programs map by program id, Wix Stores
|
|
541
|
-
digital products by product id,
|
|
542
|
-
purchase flow looks up), all only as non-consumables. A
|
|
550
|
+
digital products by product id, one-time unlimited Pricing Plans by plan id, and Wix videos by
|
|
551
|
+
video id (the ids the purchase flow looks up), all only as non-consumables. A mapped video gets
|
|
552
|
+
its App Store price on the Videos screens; buying it is not available yet (prices only,
|
|
553
|
+
[checkout](iap-wix-checkout.md#wix-video-prices-only)). A plan is mapped only when the site reads it
|
|
543
554
|
as one-time unlimited (one payment, valid until cancelled); a recurring, time-limited or archived
|
|
544
555
|
plan is skipped. When the site's plans cannot be read, each plan is `failed` (`request_failed`,
|
|
545
556
|
`unverified` in the dry-run diff) and not mapped. Other verticals, price tiers and consumable items
|
|
@@ -21,7 +21,8 @@ and generated templates. Historical receipts retain their recorded amounts.
|
|
|
21
21
|
| `himi-branded-lite-cart` | Full catalog app/item/options reference and quantity; line `total` | Refreshes the current cart and routes before payment; only verified physical regular carts keep checkout; Apple, mixed, multiple-digital, unknown and unverified digital carts are blocked |
|
|
22
22
|
| `himi-store-cart` | Full catalog reference; line `total` | Consistent shared prices and suppressed Wix subtotal when routes are unsettled or Apple; remains read-only |
|
|
23
23
|
| `himi-branded-lite-order` | Original catalog app/item/options and quantity | Preserves references; checks current routing and catalog facts before re-adding physical items; never reprices the paid receipt |
|
|
24
|
-
| `
|
|
24
|
+
| `himi-branded-lite-videos`, `himi-branded-lite-video` (Videos, channel and Video screens) | Wix video ID (`wixVideo`); carousel and row `priceLine`, Video `priceText` / `priceNote` / `purchaseNote` | Shared pricing for videos with a one-time Sale price, in channels that require no subscription or plan. Prices only: no Buy; `wixVideoOrders` refuses while `VIDEOS_APPLE_SALES_ENABLED` is off. Home's video rail stays unpriced |
|
|
25
|
+
| `video-courses-preview` / Atlas | `storeProductId`, plus course-specific member access mirror | Deferred: deployment and usable course access are unverified. Existing native digital-checkout guard remains; Atlas courses are Stores products, not Wix Video, so `wixVideoOrders` does not apply |
|
|
25
26
|
| `wix-spaces-product-detail`, `wix-spaces-shop`, `wix-spaces-saved`, `wix-spaces-cart` | Legacy worker sources | Retired `fixtures/wix-spaces-clone/dev` descriptors still reference these workers; no active app or generated template consumer. Legacy pricing/checkout is not migrated or certified for managed Apple IAP |
|
|
26
27
|
| `hip-shop-product`, `hip-shop-home`, `hip-shop-explore`, `hip-shop-saved`, `hip-shop-cart` | Legacy worker sources | Retired `fixtures/hip-shop/dev` descriptors reference these workers; no active app or generated template consumer. Legacy pricing/checkout is not migrated or certified for managed Apple IAP |
|
|
27
28
|
| `family-cart` (Family Cart and Himi Game) | Demo household/game economy | Not a Wix purchase path; unchanged |
|
package/guides/qa-builds.md
CHANGED
|
@@ -199,7 +199,20 @@ by this job, and its temporary files. It preserves the agent's keychain; it does
|
|
|
199
199
|
certificate or delete the original profile in Apple/Branded. A matching identity already in the
|
|
200
200
|
keychain is refused before import so cleanup cannot delete a pre-existing identity.
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
A job that is killed before its cleanup runs (SIGKILL) leaves the customer's material behind, so
|
|
203
|
+
each job records what it places before placing it: its signing folder (which holds the private key
|
|
204
|
+
until it is imported), the identity it imports into the agent keychain, and every copy of the App
|
|
205
|
+
Store Connect key (`~/.private_keys/AuthKey_<id>.p8`, also when it replaces a file already there,
|
|
206
|
+
and the temporary copy `altool` reads). The records live in `~/.himalaya/customer-secret-claims` in the account home.
|
|
207
|
+
At the start of the next customer build on that Mac, before the pre-existing check, Himi removes
|
|
208
|
+
what a job whose process has ended recorded, and leaves alone what a running job recorded, since a
|
|
209
|
+
Mac may run more than one job at once. A matching identity that a running job owns, or that no
|
|
210
|
+
record names, is still refused. SIGTERM and process exit remove the key copies directly, as they do
|
|
211
|
+
the signing directory.
|
|
212
|
+
|
|
213
|
+
Outside that runner mode, Himi creates a temporary keychain and deletes it during cleanup. That
|
|
214
|
+
keychain is recorded the same way before it is created, and the next customer build deletes one an
|
|
215
|
+
ended job left (`security delete-keychain`, which also drops it from the search list). A
|
|
203
216
|
missing password or keychain in agent-keychain mode fails the build instead of silently switching
|
|
204
217
|
modes. Private key material does not enter job parameters or the public job record. The runner
|
|
205
218
|
source SHA must advertise support for this protocol and be reachable from the pool before BYO
|
|
@@ -72,6 +72,22 @@ interface State {
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
Four-state's `shape()` spreads `...s`, so the cursors keep their values between
|
|
76
|
+
transitions. Add the paging keys to its return, with defaults so they stay strings
|
|
77
|
+
(the strict Tier-5 typecheck rejects an optional key), and an unbound
|
|
78
|
+
`hideLoadMoreError` would show the row on iOS:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
nextCursor: s.nextCursor ?? "",
|
|
82
|
+
failedCursor: s.failedCursor ?? "",
|
|
83
|
+
hideLoadMoreError: !s.failedCursor,
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Reset both cursors to `""` whenever the rows start over: a page-1 load (first
|
|
87
|
+
load, pull-to-refresh, retry) takes `nextCursor` from its response, and a
|
|
88
|
+
sign-in, membership or permission failure clears them with the rows — or a
|
|
89
|
+
load-more row and its retry outlive the list they belonged to.
|
|
90
|
+
|
|
75
91
|
`load()` fetches through a typed `@himalaya/wix-<domain>` client, normalizes, and
|
|
76
92
|
recomputes the display strings. For Stores, start with V3
|
|
77
93
|
`queryStoreProducts` / `getStoreProduct`; do not hand-call `/stores/v1/…`, which
|
|
@@ -112,7 +112,9 @@ const svc = defineService<FormState, {
|
|
|
112
112
|
// Keep what the user typed (echo it back into the prefill), and say what
|
|
113
113
|
// happened in their words — never e.message.
|
|
114
114
|
const kind = classifyError(e, currentAuthState()); // from himi docs recipe-four-state
|
|
115
|
-
|
|
115
|
+
// A failure after the request left may still have created the draft: say so.
|
|
116
|
+
const outcome = writeMayHaveLanded(e) ? "unconfirmed" : kind;
|
|
117
|
+
return { ...s, title, body, status: "error", error: SAVE_FAILED[outcome], savedMessage: "" };
|
|
116
118
|
}
|
|
117
119
|
},
|
|
118
120
|
},
|
|
@@ -126,7 +128,25 @@ const SAVE_FAILED: Record<string, string> = {
|
|
|
126
128
|
signIn: "Your session has ended. Sign in, then save again.",
|
|
127
129
|
membership: "Join this site before saving.",
|
|
128
130
|
permission: "You don't have permission to save this.",
|
|
131
|
+
unconfirmed: "Wix didn't confirm this, so it may or may not have been saved. Check before trying again.",
|
|
129
132
|
};
|
|
133
|
+
|
|
134
|
+
// A failure with no HTTP answer may come after Wix took the write (a reset, a timeout, a bad or
|
|
135
|
+
// truncated response); only one while CONNECTING proves nothing was sent. A 502–504 may have
|
|
136
|
+
// landed too — the owner templates' `writeMayHaveLanded` rule.
|
|
137
|
+
const NO_ANSWER = /NSURLErrorDomain|Error Domain=|\bjava\.(?:net|io)\.\w+|(?:UnknownHost|Connect|SocketTimeout|NoRouteToHost|SSLHandshake)Exception|^Failed to connect|^Unable to resolve host|Connection (?:refused|reset)|Network is unreachable|Software caused connection abort|ECONNRESET|ETIMEDOUT|EPIPE|Broken pipe|stream was reset|unexpected end of stream|timed out|^timeout$|Failed to fetch|NetworkError|^(?:TypeError: )?Load failed$|net::ERR_|^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i;
|
|
138
|
+
const NEVER_SENT = /NSURLErrorDomain\D*?-10(?:09|04|03|06|20|18)\b|\b(?:UnknownHost|Connect|NoRouteToHost)Exception\b|^Failed to connect to \S|^Unable to resolve host\b|\b(?:Connection refused|Network is unreachable)\b|net::ERR_(?:INTERNET_DISCONNECTED|NAME_NOT_RESOLVED|CONNECTION_REFUSED|ADDRESS_UNREACHABLE|NETWORK_ACCESS_DENIED)\b|^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i;
|
|
139
|
+
|
|
140
|
+
function writeMayHaveLanded(e: unknown): boolean {
|
|
141
|
+
const code = String((e as { code?: unknown })?.code ?? "");
|
|
142
|
+
const msg = String((e as Error)?.message ?? e ?? "");
|
|
143
|
+
if (code === "offline_write_blocked") return false; // refused before it left
|
|
144
|
+
if (code === "offline_write_outcome_unknown" || /"sent"\s*:\s*true/.test(msg)) return true;
|
|
145
|
+
// The web host's own envelope without "sent": true failed before the request left it.
|
|
146
|
+
if (/\bwebwix_(?:direct_failed|owner_disabled|bridge_disabled)\b/.test(msg)) return false;
|
|
147
|
+
if (/\b(?:HTTP|Wix) \d{3}\b/.test(msg)) return /\b(?:HTTP|Wix) (?:502|503|504)\b/.test(msg);
|
|
148
|
+
return NO_ANSWER.test(msg) && !NEVER_SENT.test(msg);
|
|
149
|
+
}
|
|
130
150
|
```
|
|
131
151
|
|
|
132
152
|
- **Field rules stay on the fields.** Required and format checks belong in the
|
|
@@ -164,7 +184,17 @@ reports on:
|
|
|
164
184
|
For "open what I just created", expose the target from the worker
|
|
165
185
|
(`savedScreenId` + a `hideSavedLink` boolean) and gate a button with the
|
|
166
186
|
`hiddenWhen` modifier bound to it — see `apps/himi-owner-lite`'s
|
|
167
|
-
`open_saved` button, and the four-state recipe for the gating idiom.
|
|
187
|
+
`open_saved` button, and the four-state recipe for the gating idiom. Add both
|
|
188
|
+
keys to `FormState`, set them on every outcome (`hideSavedLink: true` on a
|
|
189
|
+
failure), and list them in the opt-out too, so a cold boot never links the last
|
|
190
|
+
run's post — the runnable example does exactly this:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
savedScreenId: string; // in FormState
|
|
194
|
+
hideSavedLink: boolean;
|
|
195
|
+
// …
|
|
196
|
+
notHydrated: ["status", "error", "savedMessage", "savedScreenId", "hideSavedLink"],
|
|
197
|
+
```
|
|
168
198
|
|
|
169
199
|
## Edit mode (prefill + same submit)
|
|
170
200
|
|
|
@@ -50,6 +50,7 @@ function shape(s: Partial<State>): State {
|
|
|
50
50
|
const errorKind: ErrorKind = status === "error" ? (s.errorKind || "server") : "";
|
|
51
51
|
const blank = items.length === 0; // nothing on screen to keep
|
|
52
52
|
return {
|
|
53
|
+
...s, // the screen's own keys ride along: a list's nextCursor / failedCursor, a query
|
|
53
54
|
status, items, errorKind,
|
|
54
55
|
hideLoading: !(status === "loading" && blank),
|
|
55
56
|
notEmpty: !(status === "ready" && blank),
|
|
@@ -116,7 +117,7 @@ function currentAuthState(): "signedIn" | "signedOut" | "unknown" { return "unkn
|
|
|
116
117
|
async function load(s: State): Promise<State> {
|
|
117
118
|
try {
|
|
118
119
|
const items = (await fetchRows()).map(normalize);
|
|
119
|
-
return shape({ status: "ready", items });
|
|
120
|
+
return shape({ status: "ready", items }); // a fresh load starts over
|
|
120
121
|
} catch (e) {
|
|
121
122
|
const errorKind = classifyError(e, currentAuthState()); // use host/session state when available
|
|
122
123
|
// Keep the rows: a refresh that fails must not blank a screen that had content —
|
|
@@ -2281,7 +2281,7 @@
|
|
|
2281
2281
|
"name": "IAP_CATALOG_KINDS",
|
|
2282
2282
|
"kind": "const",
|
|
2283
2283
|
"jsdoc": "/** The Wix entity types `himi store iap` writes into its Wix→Apple mapping, so a mapping\n * backend can serve that export without translating kinds. */",
|
|
2284
|
-
"decl": "export const IAP_CATALOG_KINDS = [\"onlineProgram\", \"storesProduct\", \"pricingPlan\", \"bookingsService\", \"eventsTicket\"] as const;"
|
|
2284
|
+
"decl": "export const IAP_CATALOG_KINDS = [\"onlineProgram\", \"storesProduct\", \"pricingPlan\", \"bookingsService\", \"eventsTicket\", \"wixVideo\"] as const;"
|
|
2285
2285
|
},
|
|
2286
2286
|
{
|
|
2287
2287
|
"name": "IapCatalogKind",
|
|
@@ -2379,6 +2379,12 @@
|
|
|
2379
2379
|
"jsdoc": "/** An Online Programs program as a shared IAP catalog item. A program without a Wix ID cannot be\n * mapped (a title fallback is not an identity), so it has no item and its screen shows no price. */",
|
|
2380
2380
|
"decl": "export function onlineProgramItem(program: { id?: string | null }, commerceDisplayPrice?: string): IapCatalogItem | undefined;"
|
|
2381
2381
|
},
|
|
2382
|
+
{
|
|
2383
|
+
"name": "wixVideoItem",
|
|
2384
|
+
"kind": "function",
|
|
2385
|
+
"jsdoc": "/**\n * A Wix video as a shared IAP catalog item, only when the App Store may sell it: an enabled one-time\n * `SALE` price above zero, in a channel whose pricing was read and that requires neither a subscription\n * nor a pricing plan (a bought video stays locked there). Rental and subscription access are never sold.\n */",
|
|
2386
|
+
"decl": "export function wixVideoItem(\n channel: { pricing?: VideoPricingOption[] | null },\n video: { id?: string | null; pricing?: VideoPricingOption[] | null },\n commerceDisplayPrice?: string,\n): IapCatalogItem | undefined;"
|
|
2387
|
+
},
|
|
2382
2388
|
{
|
|
2383
2389
|
"name": "PurchasesAppBinding",
|
|
2384
2390
|
"kind": "interface",
|
|
@@ -2451,11 +2457,17 @@
|
|
|
2451
2457
|
"jsdoc": "/** One-time unlimited plans sell through the App Store. Proven live in Sandbox on 2026-10-19: a member's\n * online plan order is charged with one cashier item whose id is the plan id, so StartPayment maps it to the\n * plan's Apple product, and the order reads ACTIVE and PAID. Recurring and time-limited plans stay outside\n * this rule; their Billing lifecycle is unfinished. */",
|
|
2452
2458
|
"decl": "export const PRICING_PLAN_APPLE_SALES_ENABLED = true;"
|
|
2453
2459
|
},
|
|
2460
|
+
{
|
|
2461
|
+
"name": "VIDEOS_APPLE_SALES_ENABLED",
|
|
2462
|
+
"kind": "const",
|
|
2463
|
+
"jsdoc": "/**\n * Whether Wix Video videos sell through the App Store. OFF: prices only. A video with a one-time Sale\n * price above zero is mapped under its video id as a one-time unlock (`wixVideoItem`), and screens show\n * its App Store price, but no purchase path exists yet: Wix Video has no public order API, and the\n * Videos order adapter refuses every sale until this is on.\n */",
|
|
2464
|
+
"decl": "export const VIDEOS_APPLE_SALES_ENABLED = false;"
|
|
2465
|
+
},
|
|
2454
2466
|
{
|
|
2455
2467
|
"name": "AppleSaleOptions",
|
|
2456
2468
|
"kind": "interface",
|
|
2457
2469
|
"jsdoc": "/**\n * The one definition `himi store iap connect` writes mappings by and the mapping source reads them by,\n * keyed by catalog kind. wix-iap StartPayment maps the cashier order's single item id, and Online\n * Programs builds that order with the program id as the item id; programs are one-time unlocks.\n * Wix Stores and one-time unlimited Pricing Plans join only with their independent switches.\n */",
|
|
2458
|
-
"decl": "export interface AppleSaleOptions {\n storesAppleSalesEnabled?: boolean;\n storesItemPaymentEnabled?: boolean;\n pricingPlanAppleSalesEnabled?: boolean;\n bookingsAppleSalesEnabled?: boolean;\n eventsAppleSalesEnabled?: boolean;\n}"
|
|
2470
|
+
"decl": "export interface AppleSaleOptions {\n storesAppleSalesEnabled?: boolean;\n storesItemPaymentEnabled?: boolean;\n pricingPlanAppleSalesEnabled?: boolean;\n bookingsAppleSalesEnabled?: boolean;\n eventsAppleSalesEnabled?: boolean;\n videosAppleSalesEnabled?: boolean;\n}"
|
|
2459
2471
|
},
|
|
2460
2472
|
{
|
|
2461
2473
|
"name": "appleSaleRules",
|
|
@@ -2473,7 +2485,13 @@
|
|
|
2473
2485
|
"name": "APPLE_PROVISIONING_RULES",
|
|
2474
2486
|
"kind": "const",
|
|
2475
2487
|
"jsdoc": "/** Catalog setup may precede activation; purchases still require the independent sales switches. */",
|
|
2476
|
-
"decl": "export const APPLE_PROVISIONING_RULES: Readonly<Record<string, AppleSaleRule>> = {\n ...APPLE_SALE_RULES, bookingsService: ATTENDANCE_SALE_RULE, eventsTicket: ATTENDANCE_SALE_RULE,\n};"
|
|
2488
|
+
"decl": "export const APPLE_PROVISIONING_RULES: Readonly<Record<string, AppleSaleRule>> = {\n ...APPLE_SALE_RULES, bookingsService: ATTENDANCE_SALE_RULE, eventsTicket: ATTENDANCE_SALE_RULE, wixVideo: VIDEO_SALE_RULE,\n};"
|
|
2489
|
+
},
|
|
2490
|
+
{
|
|
2491
|
+
"name": "APPLE_PRICING_RULES",
|
|
2492
|
+
"kind": "const",
|
|
2493
|
+
"jsdoc": "/** What the mapping source prices by: every sale rule, plus the verticals whose App Store prices show\n * before their purchase is on (Wix Video). A priced item with no sale rule has no checkout adapter. */",
|
|
2494
|
+
"decl": "export const APPLE_PRICING_RULES: Readonly<Record<string, AppleSaleRule>> = { ...APPLE_SALE_RULES, wixVideo: VIDEO_SALE_RULE };"
|
|
2477
2495
|
},
|
|
2478
2496
|
{
|
|
2479
2497
|
"name": "HostCallExpectation",
|
|
@@ -2873,7 +2891,7 @@
|
|
|
2873
2891
|
"name": "\"./iap-verticals/index.js\"",
|
|
2874
2892
|
"kind": "reexport",
|
|
2875
2893
|
"jsdoc": "",
|
|
2876
|
-
"decl": "export { onlineProgramOrders, pricingPlanOrders, storesProductOrders, bookingOrders, eventTicketOrders, classifyCart } from \"./iap-verticals/index.js\";"
|
|
2894
|
+
"decl": "export { onlineProgramOrders, pricingPlanOrders, storesProductOrders, bookingOrders, eventTicketOrders, wixVideoOrders, classifyCart } from \"./iap-verticals/index.js\";"
|
|
2877
2895
|
},
|
|
2878
2896
|
{
|
|
2879
2897
|
"name": "\"./order-refs.js\"",
|
|
@@ -5845,6 +5863,18 @@
|
|
|
5845
5863
|
"jsdoc": "/** Capability version, for bundles that gate on min binary version. */",
|
|
5846
5864
|
"decl": "export const version = 2;"
|
|
5847
5865
|
},
|
|
5866
|
+
{
|
|
5867
|
+
"name": "\"./user-props.js\"",
|
|
5868
|
+
"kind": "reexport",
|
|
5869
|
+
"jsdoc": "",
|
|
5870
|
+
"decl": "export { USER_PROPS_MAX_BYTES, type UserPropsErrorCode } from \"./user-props.js\";"
|
|
5871
|
+
},
|
|
5872
|
+
{
|
|
5873
|
+
"name": "userProps",
|
|
5874
|
+
"kind": "const",
|
|
5875
|
+
"jsdoc": "/** The signed-in Wix user's small server-side per-app preference records. Enable with\n * `wixPlatform: { userProps: { app: \"<name>\" } }` in `config.ts`. Rejects with `err.code`:\n * `not_signed_in` (no Wix user — a member or visitor session counts as none), `not_configured`,\n * `too_large`, `invalid_patch`, `request_failed`, or `unavailable` (web). */",
|
|
5876
|
+
"decl": "export const userProps = {\n /** The signed-in Wix user's props for the app named by `wixPlatform.userProps.app`. */\n get(): Promise<Record<string, unknown>> {\n return userPropsGet();\n },\n /** Deep-merge `patch` into a FRESH server read and write the result back; resolves with it.\n * The patch wins, objects merge recursively, arrays are replaced. Writes are serialized per\n * process natively, so concurrent writers (two workers, or an app service and a screen) never\n * lose each other's keys. Values must be JSON; a result over 64 KB rejects `too_large`. */\n set(patch: Record<string, unknown>): Promise<Record<string, unknown>> {\n return userPropsSet(patch);\n },\n};"
|
|
5877
|
+
},
|
|
5848
5878
|
{
|
|
5849
5879
|
"name": "\"./offline.js\"",
|
|
5850
5880
|
"kind": "reexport",
|
|
@@ -24698,6 +24728,12 @@
|
|
|
24698
24728
|
"http": "POST /online-programs/v3/participants/stats",
|
|
24699
24729
|
"scope": "visitor"
|
|
24700
24730
|
},
|
|
24731
|
+
{
|
|
24732
|
+
"name": "WixJoinApplicationPaymentStatus",
|
|
24733
|
+
"kind": "type",
|
|
24734
|
+
"jsdoc": "/** `JoinApplication.paymentStatus` (Join Applications v3). */",
|
|
24735
|
+
"decl": "export type WixJoinApplicationPaymentStatus =\n | \"NO_PAYMENT_REQUIRED\"\n | \"PAYMENT_PENDING\"\n | \"PAYMENT_IN_PROGRESS\"\n | \"PENDING_OWNER_APPROVAL\"\n | \"PAYMENT_SUCCESSFUL\"\n | \"PAYMENT_FAILED\"\n | \"PAYMENT_CANCELED\"\n | string;"
|
|
24736
|
+
},
|
|
24701
24737
|
{
|
|
24702
24738
|
"name": "WixJoinApplicationStatus",
|
|
24703
24739
|
"kind": "type",
|
|
@@ -24708,7 +24744,7 @@
|
|
|
24708
24744
|
"name": "WixJoinApplication",
|
|
24709
24745
|
"kind": "interface",
|
|
24710
24746
|
"jsdoc": "",
|
|
24711
|
-
"decl": "export interface WixJoinApplication {\n id?: string;\n programId?: string;\n memberId?: string;\n status?: WixJoinApplicationStatus;\n paymentStatus?: string;\n /** Set once the application became a participant. */\n participantId?: string;\n createdDate?: string;\n updatedDate?: string;\n}"
|
|
24747
|
+
"decl": "export interface WixJoinApplication {\n id?: string;\n revision?: string;\n programId?: string;\n memberId?: string;\n status?: WixJoinApplicationStatus;\n paymentStatus?: WixJoinApplicationPaymentStatus;\n /** Wix Payments / Pricing Plans data behind `paymentStatus` (order, offline transaction, plan ids, coupon). */\n paymentInfo?: { paymentOrderId?: string; offlineTransactionId?: string; paidPlanIds?: string[]; couponId?: string };\n /** Set once the application became a participant. */\n participantId?: string;\n createdDate?: string;\n updatedDate?: string;\n}"
|
|
24712
24748
|
},
|
|
24713
24749
|
{
|
|
24714
24750
|
"name": "WixGetCurrentJoinApplicationResponse",
|
|
@@ -24743,6 +24779,104 @@
|
|
|
24743
24779
|
"decl": "export function listProgramCategories(): Promise<WixListProgramCategoriesResponse>;",
|
|
24744
24780
|
"http": "GET /_api/program-categories-service/v3/categories",
|
|
24745
24781
|
"scope": "member"
|
|
24782
|
+
},
|
|
24783
|
+
{
|
|
24784
|
+
"name": "WixEnrollmentStatus",
|
|
24785
|
+
"kind": "type",
|
|
24786
|
+
"jsdoc": "/** Participant `enrollmentStatus` (Participants v3). */",
|
|
24787
|
+
"decl": "export type WixEnrollmentStatus =\n | \"JOINED\"\n | \"SUSPENDED\"\n | \"AUTO_REMOVED\"\n | \"REMOVED\"\n | \"LEFT\"\n | string;"
|
|
24788
|
+
},
|
|
24789
|
+
{
|
|
24790
|
+
"name": "WixProgressStatus",
|
|
24791
|
+
"kind": "type",
|
|
24792
|
+
"jsdoc": "/** Participant `progressStatus` (Participants v3). */",
|
|
24793
|
+
"decl": "export type WixProgressStatus =\n | \"NOT_STARTED\"\n | \"IN_PROGRESS\"\n | \"COMPLETED\"\n | \"FAILED\"\n | \"NOT_APPLICABLE\"\n | string;"
|
|
24794
|
+
},
|
|
24795
|
+
{
|
|
24796
|
+
"name": "listMyParticipations",
|
|
24797
|
+
"kind": "function",
|
|
24798
|
+
"jsdoc": "/**\n * Every page of `queryMemberParticipants`, concatenated — the member's whole membership snapshot,\n * which `participationsByProgram` then matches to the programs on screen. Bounded by `maxPages`\n * (default 10; at the upstream page cap of 100 that is at most 1,000 rows) so a runaway cursor\n * can never spin a worker forever. The server filter of `queryMemberParticipants` already leaves\n * out enrollments the member left or was removed from, so a program the member left and never\n * re-joined simply has no row here; this walk adds no filter of its own.\n *\n * @scope member — requires the signed-in site member; `memberId` must be that member's own id.\n */",
|
|
24799
|
+
"decl": "export async function listMyParticipations(memberId: string, opts: { maxPages?: number } = {}): Promise<WixParticipant[]>;",
|
|
24800
|
+
"scope": "member"
|
|
24801
|
+
},
|
|
24802
|
+
{
|
|
24803
|
+
"name": "participationsByProgram",
|
|
24804
|
+
"kind": "function",
|
|
24805
|
+
"jsdoc": "/**\n * The participation to show per program when a member has several rows for one program: the\n * NEWEST by `createdDate` wins — the One App member-list rule. Participants without a `programId`\n * are skipped. Defensive, for a caller that feeds a BROADER read than `listMyParticipations`\n * (whose server filter already omits them): a withdrawn enrollment (LEFT / REMOVED / AUTO_REMOVED)\n * is dropped only AFTER the newest row is chosen, so a newer withdrawal can never resurrect an\n * older joined row, and a re-join after a leave is the standing.\n */",
|
|
24806
|
+
"decl": "export function participationsByProgram(participants: readonly WixParticipant[]): Map<string, WixParticipant>;"
|
|
24807
|
+
},
|
|
24808
|
+
{
|
|
24809
|
+
"name": "WixQueryJoinApplicationsResponse",
|
|
24810
|
+
"kind": "interface",
|
|
24811
|
+
"jsdoc": "",
|
|
24812
|
+
"decl": "export interface WixQueryJoinApplicationsResponse {\n joinApplications?: WixJoinApplication[];\n pagingMetadata?: { count?: number; total?: number; hasNext?: boolean; cursors?: { next?: string | null; prev?: string | null } };\n}"
|
|
24813
|
+
},
|
|
24814
|
+
{
|
|
24815
|
+
"name": "PENDING_JOIN_APPLICATION_STATUSES",
|
|
24816
|
+
"kind": "const",
|
|
24817
|
+
"jsdoc": "/** `JoinApplication.status` values that describe a member BEFORE they are a participant — the ones a\n * card shows. Payment is NOT a status: an approved-but-unpaid application stays `APPROVED` with\n * `paymentStatus` PAYMENT_PENDING (what `joinProgram` above reads), so `APPROVED` covers it and the\n * caller reads `paymentStatus` to tell \"pay to join\" from \"joined, participant pending\". An\n * application that already became a participant keeps its status and carries `participantId`:\n * callers prefer the participation and ignore such rows. */",
|
|
24818
|
+
"decl": "export const PENDING_JOIN_APPLICATION_STATUSES: readonly WixJoinApplicationStatus[] = Object.freeze([\n \"INVITED\",\n \"APPROVAL_PENDING\",\n \"APPROVED\",\n \"SUSPENDED\",\n]);"
|
|
24819
|
+
},
|
|
24820
|
+
{
|
|
24821
|
+
"name": "JoinApplicationsQuery",
|
|
24822
|
+
"kind": "interface",
|
|
24823
|
+
"jsdoc": "",
|
|
24824
|
+
"decl": "export interface JoinApplicationsQuery {\n memberId: string;\n /** Programs to look at; omitted reads the member's open applications across every program. An empty list makes no call. */\n programIds?: readonly string[];\n /** Defaults to the pre-participant statuses (`PENDING_JOIN_APPLICATION_STATUSES`). */\n statuses?: readonly WixJoinApplicationStatus[];\n /** Page size, 1..100; defaults to the cap (100). A member can hold several applications to one program\n * (an old approved one that became a participant, a new pending re-application), so the page is never\n * sized to the program count. */\n limit?: number;\n /** `pagingMetadata.cursors.next` of the previous page. A cursor page carries NO filter, per the API. */\n cursor?: string;\n}"
|
|
24825
|
+
},
|
|
24826
|
+
{
|
|
24827
|
+
"name": "queryJoinApplications",
|
|
24828
|
+
"kind": "function",
|
|
24829
|
+
"jsdoc": "/**\n * The member's open join applications for a set of programs in ONE call — what says \"pending\n * request\", \"request approved, pay to join\" or \"invited\" before any participant exists. The\n * per-program `getCurrentJoinApplication` above answers one program; this answers a page.\n *\n * `POST /online-programs/participants/v3/join-applications/query`.\n *\n * @scope member — requires the signed-in site member; the reference states a member may read their own applications.\n */",
|
|
24830
|
+
"decl": "export function queryJoinApplications(query: JoinApplicationsQuery): Promise<WixQueryJoinApplicationsResponse>;",
|
|
24831
|
+
"http": "POST /online-programs/participants/v3/join-applications/query",
|
|
24832
|
+
"scope": "member"
|
|
24833
|
+
},
|
|
24834
|
+
{
|
|
24835
|
+
"name": "listJoinApplications",
|
|
24836
|
+
"kind": "function",
|
|
24837
|
+
"jsdoc": "/**\n * Every page of `queryJoinApplications`, concatenated, bounded by `maxPages` (default 5 → at most\n * 500 applications). What a page of program cards reads when the member's applications may exceed\n * one page; the single-page call is enough for one program.\n *\n * @scope member — requires the signed-in site member; reads that member's own applications.\n */",
|
|
24838
|
+
"decl": "export async function listJoinApplications(query: Omit<JoinApplicationsQuery, \"cursor\">, opts: { maxPages?: number } = {}): Promise<WixJoinApplication[]>;",
|
|
24839
|
+
"scope": "member"
|
|
24840
|
+
},
|
|
24841
|
+
{
|
|
24842
|
+
"name": "WixCancelJoinApplicationResponse",
|
|
24843
|
+
"kind": "interface",
|
|
24844
|
+
"jsdoc": "",
|
|
24845
|
+
"decl": "export interface WixCancelJoinApplicationResponse { joinApplication?: WixJoinApplication }"
|
|
24846
|
+
},
|
|
24847
|
+
{
|
|
24848
|
+
"name": "cancelJoinApplication",
|
|
24849
|
+
"kind": "function",
|
|
24850
|
+
"jsdoc": "/**\n * Withdraw the member's own pending request — the \"Cancel request\" action on a card whose\n * application is `APPROVAL_PENDING`. This is request withdrawal ONLY: an invitation (`INVITED`)\n * is answered with the separate `…/{id}/decline` operation, which this SDK does not bind, and\n * the cancel route rejects it with an invalid-status error. The application moves to `CANCELLED`\n * and is removed asynchronously, so a list read right after may still show it — re-read, do not\n * assume.\n *\n * `POST /online-programs/participants/v3/join-applications/{id}/cancel`.\n *\n * @scope member — requires the signed-in site member withdrawing their own application.\n */",
|
|
24851
|
+
"decl": "export function cancelJoinApplication(joinApplicationId: string): Promise<WixCancelJoinApplicationResponse>;",
|
|
24852
|
+
"http": "POST /online-programs/participants/v3/join-applications/{…}/cancel",
|
|
24853
|
+
"scope": "member"
|
|
24854
|
+
},
|
|
24855
|
+
{
|
|
24856
|
+
"name": "participantsShownByProgram",
|
|
24857
|
+
"kind": "function",
|
|
24858
|
+
"jsdoc": "/** `participantsCount + autoRemovedCount` per program — the number the One App card prints as \"N participants\". */",
|
|
24859
|
+
"decl": "export function participantsShownByProgram(response: WixGetParticipationStatsResponse): Map<string, number>;"
|
|
24860
|
+
},
|
|
24861
|
+
{
|
|
24862
|
+
"name": "WixGetPremiumStatsResponse",
|
|
24863
|
+
"kind": "interface",
|
|
24864
|
+
"jsdoc": "",
|
|
24865
|
+
"decl": "export interface WixGetPremiumStatsResponse {\n premiumEnabled?: boolean;\n activeParticipantsCount?: number;\n activeParticipantsLimit?: number;\n}"
|
|
24866
|
+
},
|
|
24867
|
+
{
|
|
24868
|
+
"name": "getPremiumStats",
|
|
24869
|
+
"kind": "function",
|
|
24870
|
+
"jsdoc": "/**\n * Whether the site's Online Programs are over the free-tier participant cap — the One App hides a\n * joined card's progress and price while this holds.\n * `GET /online-programs/participants/v3/stats/premium`.\n *\n * @scope member — exercised with a signed-in member session; the reference lists only the Manage scope (see the section note).\n */",
|
|
24871
|
+
"decl": "export function getPremiumStats(): Promise<WixGetPremiumStatsResponse>;",
|
|
24872
|
+
"http": "GET /online-programs/participants/v3/stats/premium",
|
|
24873
|
+
"scope": "member"
|
|
24874
|
+
},
|
|
24875
|
+
{
|
|
24876
|
+
"name": "isParticipationBlocked",
|
|
24877
|
+
"kind": "function",
|
|
24878
|
+
"jsdoc": "/** True when participation is blocked by the free-tier cap (no premium, count over limit). */",
|
|
24879
|
+
"decl": "export function isParticipationBlocked(stats: WixGetPremiumStatsResponse | undefined): boolean;"
|
|
24746
24880
|
}
|
|
24747
24881
|
]
|
|
24748
24882
|
},
|
|
@@ -27201,8 +27335,8 @@
|
|
|
27201
27335
|
{
|
|
27202
27336
|
"name": "CHANNEL_FIELDS",
|
|
27203
27337
|
"kind": "const",
|
|
27204
|
-
"jsdoc": "/** The channel fields a list or card needs. */",
|
|
27205
|
-
"decl": "export const CHANNEL_FIELDS = \"id title description source featuredVideoId hasPaidVideos cover { selected } videos { total }\";"
|
|
27338
|
+
"jsdoc": "/** The channel fields a list or card needs, including the viewer's subscription (`canWatchVideo`). */",
|
|
27339
|
+
"decl": "export const CHANNEL_FIELDS = \"id title description source featuredVideoId hasPaidVideos cover { selected } pricing { type enabled required } purchase(filter: {type: SUBSCRIPTION}) { status expiresAt } videos { total }\";"
|
|
27206
27340
|
},
|
|
27207
27341
|
{
|
|
27208
27342
|
"name": "CHANNEL_ACCESS_FIELDS",
|
|
@@ -27213,14 +27347,14 @@
|
|
|
27213
27347
|
{
|
|
27214
27348
|
"name": "VIDEO_CARD_FIELDS",
|
|
27215
27349
|
"kind": "const",
|
|
27216
|
-
"jsdoc": "/** The video fields a card needs: title, images, length and
|
|
27217
|
-
"decl": "export const VIDEO_CARD_FIELDS = \"id title source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled }\";"
|
|
27350
|
+
"jsdoc": "/** The video fields a card needs: title, images, length, processing state, and the pricing and the\n * viewer's purchases an access decision needs (`canWatchVideo`). */",
|
|
27351
|
+
"decl": "export const VIDEO_CARD_FIELDS = \"id title source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled price currency } purchase(filter: {type: SALE}) { status } rent_purchase: purchase(filter: {type: RENT}) { status }\";"
|
|
27218
27352
|
},
|
|
27219
27353
|
{
|
|
27220
27354
|
"name": "VIDEO_FIELDS",
|
|
27221
27355
|
"kind": "const",
|
|
27222
27356
|
"jsdoc": "/** The video fields playback and an access decision need. */",
|
|
27223
|
-
"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 }\";"
|
|
27357
|
+
"decl": "export const VIDEO_FIELDS = \"id title description source status cover { selected } poster { selected } content { status duration thumbnailUrl externalUrl externalId } pricing { type enabled price currency } purchase(filter: {type: SALE}) { status } rent_purchase: purchase(filter: {type: RENT}) { status }\";"
|
|
27224
27358
|
},
|
|
27225
27359
|
{
|
|
27226
27360
|
"name": "ListChannelsOptions",
|
|
@@ -57,4 +57,4 @@ the SDK sources, so it matches the shipped SDKs.
|
|
|
57
57
|
- [`ui`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fui?format=md) — Imperative popup capability. 2 methods · not in Brandy.
|
|
58
58
|
- [`wix_cashier`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fwix_cashier?format=md) — Native cashier. 2 methods.
|
|
59
59
|
- [`wix_chat`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fwix_chat?format=md) — Host-owned realtime Chat event subscriptions. 2 methods.
|
|
60
|
-
- [`wix_platform`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fwix_platform?format=md) — Wix Platform SDK, worker-facing bindings.
|
|
60
|
+
- [`wix_platform`](https://apps.wix.com/_api/himi-serve/authoring/resource/web-sdk%2Fwix_platform?format=md) — Wix Platform SDK, worker-facing bindings. 29 methods · not in Brandy.
|
|
@@ -58,6 +58,7 @@ await monitoring.captureError("thing failed", { code: "E1" });
|
|
|
58
58
|
| `session` | `headers()`, `signedInstanceStatus()` |
|
|
59
59
|
| `auth` | `getIdentity()`, `isAuthenticated()`, `getAccessToken()`, `signIn()`, `signOut()`, `signedInstance(businessId, type?)` |
|
|
60
60
|
| `monitoring` | `captureError(msg, extra?)`, `captureException(class, msg, extra?)`, `setUser(id, traits?)`, `setTag(k, v)`, `addBreadcrumb(msg)` |
|
|
61
|
+
| `userProps` | `get()`, `set(patch)` — the signed-in Wix user's server-side per-app preferences ([below](#userprops--server-side-per-user-preferences)) |
|
|
61
62
|
| `version` | the SDK surface version constant |
|
|
62
63
|
|
|
63
64
|
## Real vs dev seam — and best-effort
|
|
@@ -116,6 +117,43 @@ sign-in from a successful one, so a correct four-state screen still showed a dea
|
|
|
116
117
|
web member path itself is a login bridge that only a local dev host serves; a hosted preview
|
|
117
118
|
is visitor-only. See [wix-login-integration](wix-login-integration.md) for that whole picture.
|
|
118
119
|
|
|
120
|
+
## `userProps` — server-side per-user preferences
|
|
121
|
+
|
|
122
|
+
Small per-user, per-app records kept on Wix's servers, so they survive a reinstall and follow
|
|
123
|
+
the user across devices (e.g. "the user already saw the publish prompt"). Enable it with the app
|
|
124
|
+
name the records live under:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// apps/<app>/config.ts
|
|
128
|
+
wixPlatform: { userProps: { app: "my-app" } },
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
import { userProps } from "@himalaya/wix-platform";
|
|
133
|
+
|
|
134
|
+
const props = await userProps.get(); // Record<string, unknown>
|
|
135
|
+
const merged = await userProps.set({ prompts: { publish: true } });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- `set(patch)` deep-merges `patch` into a **fresh** server read and writes the result back:
|
|
139
|
+
the patch wins, objects merge recursively, arrays are replaced. The server write replaces the
|
|
140
|
+
whole record, so the read-merge-write runs natively and is **serialized per process** — two
|
|
141
|
+
workers (or an app service and a screen) writing at once never lose each other's keys.
|
|
142
|
+
- Values are JSON; a result over 64 KB rejects `too_large`. `platform` is reserved as a
|
|
143
|
+
top-level key (it is the server namespace the props live under), so a patch that sets it
|
|
144
|
+
rejects `invalid_patch`; nested `platform` keys are ordinary data.
|
|
145
|
+
- Like `auth.signIn()`, it **rejects** with an `err.code`: `not_signed_in` (no Wix user — a site
|
|
146
|
+
member or visitor session counts as none, since these records belong to a Wix account),
|
|
147
|
+
`not_configured` (no `wixPlatform.userProps.app`), `request_failed`, `invalid_patch`, and
|
|
148
|
+
`unavailable` on web, where the host has no transport for the write. Handle the rejection;
|
|
149
|
+
for data that need not follow the user, local storage is the simpler choice.
|
|
150
|
+
- iOS, macOS (shares the iOS runtime) and Android read `GET apps.wix.com/_api/app-state/v1/user/<app>/state`
|
|
151
|
+
and write `PUT apps.wix.com/_api/mobile-client-storage/v1/user-preferences` through the app's
|
|
152
|
+
account-level owner Wix transport, the one the shell's own `HimalayaWixCache.wrap` binds. A
|
|
153
|
+
per-site chain never replaces it, and a Himi Player tenant does not bind it yet, so there it
|
|
154
|
+
answers `not_signed_in`. A user-state response the runtime cannot read rejects
|
|
155
|
+
`request_failed` and writes nothing, because the write replaces the whole record.
|
|
156
|
+
|
|
119
157
|
## Testing a worker that uses the SDK
|
|
120
158
|
|
|
121
159
|
Fake the SDK's dispatch (the members call `dispatch("capability.wix_platform.<method>")`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1610.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": [
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"artifactId": "himalaya-cli"
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"falconPackageHash": "
|
|
75
|
+
"falconPackageHash": "fd34bb941dc2b1092069d5876a747cc0048164fc1b1ce148a32257b2"
|
|
76
76
|
}
|