@wix/himalaya-cli 0.1154.0 → 0.1156.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.
Binary file
@@ -324,8 +324,9 @@ Keys are transmitted to the authenticated backend for Branded/Ping custody, not
324
324
  project settings or output. Enter Later to defer and continue building.
325
325
 
326
326
  `notify status` is read-only and needs no key files; `--refresh` checks providers live.
327
- Backend readiness does not prove notification delivery. This setup does not rewrite native
328
- configuration, select a Ping family, build a binary, register a device or send a notification.
327
+ Backend readiness does not prove notification delivery. A successful setup turns push on in
328
+ `config.ts` (the Ping family included; notification routes are derived at every build); it does
329
+ not build a binary, register a device or send a notification.
329
330
  Continue building and publishing app content. For a customer push test, use the
330
331
  `himi dist --signing byo` flow below with existing signing assets and matching native configuration.
331
332
  The default Wix-managed binary does not match the customer's Apple identity. After an uncertain
@@ -353,8 +353,9 @@ Keys are transmitted to the authenticated backend for Branded/Ping custody, not
353
353
  project settings or output. Enter Later to defer and continue building.
354
354
 
355
355
  `notify status` is read-only and needs no key files; `--refresh` checks providers live.
356
- Backend readiness does not prove notification delivery. This setup does not rewrite native
357
- configuration, select a Ping family, build a binary, register a device or send a notification.
356
+ Backend readiness does not prove notification delivery. A successful setup turns push on in
357
+ `config.ts` (the Ping family included; notification routes are derived at every build); it does
358
+ not build a binary, register a device or send a notification.
358
359
  Continue building and publishing app content. For a customer push test, use the
359
360
  `himi dist --signing byo` flow below with existing signing assets and matching native configuration.
360
361
  The default Wix-managed binary does not match the customer's Apple identity. After an uncertain
@@ -116,6 +116,7 @@ Only **unambiguous** defects, so a green run stays worth trusting:
116
116
  | `non-finite-number` | error | `NaN` / `Infinity` in worker state — the wrong-total / missing-operand reducer bug. Read live, before the JSON trip to the host flattens it to `null`. On device it renders as an **empty** label, not the text "NaN". |
117
117
  | `placeholder-in-text` | error | `undefined`, `null`, `NaN`, `[object Object]` or a JSON blob rendered as visible text. |
118
118
  | `unsubstituted-binding` | error | A raw `$service:` / `$item` token, or an unresolved `$t:` i18n key, reaching visible text. |
119
+ | `item-field-missing` | error | A list row template binds `$item.<field>`, but none of the list's real rows carries `<field>`: the web preview paints `item.<field>` as text and native paints nothing (a worker emitting `jobType` while the template reads `$item.type`). Judged only against real rows, and on the first segment. `warning` when the prop declares `collapseWhenEmpty`. |
119
120
  | `unresolved-wix-media` | error | A media-typed prop holding a raw Wix media handle/id the renderer cannot load. |
120
121
  | `binding-unresolved` | error / warning | A `$service:` binding aimed at state the worker never defines. It is an error when the missing value can leave visible UI blank. A direct prop with sibling `collapseWhenEmpty: true` is an advisory warning instead: the renderer deliberately removes that zero-size optional chrome, while the warning keeps the omitted contract auditable. |
121
122
  | `nav-param-dropped` | error | A bound `himi.open` param that resolves to nothing, so the target opens without it and error-cards. |
@@ -1,16 +1,17 @@
1
1
  # Registering digital products as App Store in-app purchases (`himi store iap`)
2
2
 
3
- `himi store iap` registers an app owner's **selected digital products** in the owner's
4
- existing App Store Connect app. It configures each product's draft metadata, an exact
3
+ `himi store iap` registers an app owner's **digital products** in the owner's existing
4
+ App Store Connect app. It configures each product's draft metadata, an exact
5
5
  initial price and explicit territories. It then exports a Wix→Apple mapping for a future
6
6
  backend import.
7
7
 
8
8
  It is an administration workflow, not a build: it never builds, signs or bumps a version,
9
- and it runs anywhere `himi` is installed. An owner needs only a manifest and a credential
10
- profile. It does not run during an app build or a customer checkout.
9
+ and it runs anywhere `himi` is installed. An owner needs only a credential profile, plus
10
+ either a manifest or a Wix site for `draft` to read. It does not run during an app build or
11
+ a customer checkout.
11
12
 
12
- This version supports **one-time non-consumable** products only. Every other product type
13
- is refused before anything reaches Apple.
13
+ It registers one-time **non-consumables**, and **consumable price tiers** for large
14
+ catalogs. Subscriptions are refused before anything reaches Apple.
14
15
 
15
16
  ## Before you start
16
17
 
@@ -33,6 +34,56 @@ is refused before anything reaches Apple.
33
34
  There is **no ambient fallback**. `--profile` is required, and exported `ASC_*` variables
34
35
  are never used to decide which account to write to.
35
36
 
37
+ ## Drafting the manifest from your Wix catalog
38
+
39
+ You don't have to write the manifest by hand. `draft` reads the site's whole catalog —
40
+ every page of Wix Stores (Catalog V3 or V1), Online Programs and Pricing Plans — and writes
41
+ a manifest of everything that can be sold:
42
+
43
+ ```bash
44
+ himi store iap draft --site <msid> --profile fit-club-owner --apple-app 6450000001 \
45
+ --bundle com.example.fitclub --base-territory USA --territories USA,CAN --out ./iap-products.json
46
+ ```
47
+
48
+ | Wix item | Drafted as |
49
+ |---|---|
50
+ | Digital store product, paid online program, one-time "unlimited" pricing plan | a non-consumable |
51
+ | Physical product | skipped: physical goods are never sold through in-app purchase |
52
+ | Recurring or time-limited pricing plan | skipped: App Store subscriptions are a separate API |
53
+ | Free, hidden (unpublished, archived or not visible) or unclassified item | skipped |
54
+ | Item priced in another currency than the base territory | skipped |
55
+ | Bookings services and events | never read: real-world services can't be in-app purchases |
56
+
57
+ - **Product ids come from the Wix id** (`<bundle>.product.<wix-id>`, and `.program.` or
58
+ `.plan.` for those sources), so drafting the same catalog again gives the same ids.
59
+ - **Prices use the rule `atLeast`:** the lowest Apple price point at or above the Wix
60
+ price. `plan` shows the Apple price each new product will get before anything is written.
61
+ - **Names and descriptions are cut to Apple's limits,** and HTML becomes plain text.
62
+ - **`--merge <manifest>` keeps an earlier manifest's mappings.** Anything it already maps
63
+ keeps its product id, and only new catalog items are added. Use it on every redraft,
64
+ because product ids are permanent.
65
+ - **`--report <file>`** saves the skipped items and their reasons.
66
+ - **The draft is a starting point.** Review or trim it if you like, then run `plan` as
67
+ usual. Nothing in App Store Connect changes until `apply`.
68
+
69
+ ### Price tiers for large catalogs: `--mode tiers`
70
+
71
+ Thousands of items would mean thousands of Apple products, each needing App Review.
72
+ `--mode tiers` drafts **one consumable per Apple price point** instead (for example
73
+ `<bundle>.tier.usd_1_99`). Each tier lists, under `unlocks`, every Wix item it pays for.
74
+
75
+ - **Wix prices are grouped by the Apple point they resolve to.** Items at 1.10, 1.50 and
76
+ 1.99 USD all go into the 1.99 tier, and the shopper pays the tier's Apple price.
77
+ - **An item priced above Apple's highest point is skipped,** with its reason.
78
+ - **This needs Apple's price grid,** read through any in-app purchase the app already has.
79
+ If the app has none yet, tiers are keyed by Wix price and the draft says so; redraft
80
+ once the first product exists.
81
+
82
+ A shopper buys the tier, and the backend grants the item they chose.
83
+
84
+ Apple can't restore a consumable, so each unlocked item must be recorded against the
85
+ shopper's Wix member account. That delivery is the backend's job, not this command's.
86
+
36
87
  ## The manifest
37
88
 
38
89
  The manifest is an explicit, versioned selection. Nothing is inferred from names or prices,
@@ -74,19 +125,27 @@ Rules, all checked locally before any request:
74
125
  - **`classification`** must be `"digital"`. Physical goods and unclassified products are
75
126
  refused. The classification is your configuration; this command does not make Apple's
76
127
  policy decisions for you.
77
- - **`productId`** must be stable, and you supply it. Apple product ids and types are
78
- **immutable** once created, so the id is never generated or regenerated.
79
- - **`price.amount`** is an exact decimal **string**. It must match one of Apple's price
80
- points exactly. It is never rounded, and a nearby price is never substituted.
128
+ - **`productId`** must be stable: drafted from the Wix id, or chosen by you. Apple product
129
+ ids and types are **immutable** once created, so an id is never regenerated.
130
+ - **`type`** is `"nonConsumable"` or `"consumable"`. A consumable price tier names
131
+ `unlocks` (a list of `{entityType, entityId, entitlement}`) instead of `wix`; every other
132
+ item names exactly one `wix` entity.
133
+ - **`price.amount`** is a decimal **string**. Under the default `price.rule` of `"exact"`
134
+ it must match one of Apple's price points exactly; it is never rounded, and a nearby
135
+ price is never substituted. With `"rule": "atLeast"` it takes the lowest point at or
136
+ above the amount, and `plan` shows which.
137
+ - **Apple's field limits:** display name 2–30 characters, description at most 45,
138
+ reference name at most 64, product id at most 100, review note at most 4,000.
81
139
  - **`territories`** is an explicit list that includes the base territory. Availability
82
140
  never extends to future territories automatically.
83
141
  - **Duplicates are refused:** a product id, Wix entity, entitlement or reference name
84
- used twice.
142
+ used twice, including a Wix entity mapped both directly and through a tier.
85
143
  - **Unknown keys are refused, at every level.** So is any string that looks like a key or a token.
86
144
 
87
145
  ## The workflow
88
146
 
89
147
  ```bash
148
+ himi store iap draft --site <msid> --profile fit-club-owner --apple-app 6450000001 --bundle com.example.fitclub --base-territory USA --out ./iap-products.json
90
149
  himi store iap plan --manifest ./iap-products.json --profile fit-club-owner --out ./iap-plan.json
91
150
  himi store iap apply --plan ./iap-plan.json --profile fit-club-owner --out ./iap-result.json
92
151
  himi store iap status --manifest ./iap-products.json --profile fit-club-owner --json
@@ -138,6 +197,19 @@ created. The plan marks this in the item's `notes`.
138
197
  it reads everything back.
139
198
  5. Writes the result file, even when the run is partial.
140
199
 
200
+ **Large catalogs.** App Store Connect allows about 3,600 API requests an hour per key.
201
+ Registering a new product takes about ten, and each run also lists the app's products once.
202
+
203
+ - **`apply --limit <n>`** creates at most `n` new products per run and reports the rest as
204
+ `deferred`. Rerunning the same plan continues where the last run stopped.
205
+ - **Only products with planned writes are re-read.** Products the plan reused are reported
206
+ from the plan.
207
+ - **Per-item registration of thousands of products still takes many hours.** Every one
208
+ also needs App Review, so prefer `--mode tiers` at that scale.
209
+ - **Rate limits are handled:** throttled reads wait for Apple's `Retry-After` and retry.
210
+ - **Plans and results with more than 50 products print a summary,** including any products
211
+ whose Apple price is higher than requested. The files always list every product.
212
+
141
213
  **When a write's outcome is unknown** (a lost response, a timeout, a 409), apply reads
142
214
  Apple again before doing anything else:
143
215
 
@@ -154,7 +226,8 @@ to save it in the same shape as the result file.
154
226
 
155
227
  Local state lives in `~/.himalaya/store-iap/<appleAppId>/`:
156
228
 
157
- - **`journal.json`** is an append-only record of identifiers and step outcomes. Nothing
229
+ - **`journal.jsonl`** is an append-only record of identifiers and step outcomes, one line
230
+ per step. Nothing
158
231
  reads it back to decide anything. Every run re-reads the target app, and a product Apple
159
232
  hasn't listed yet is caught by apply's read-before-retry, so deleting the journal is
160
233
  always safe.
@@ -165,11 +238,11 @@ Local state lives in `~/.himalaya/store-iap/<appleAppId>/`:
165
238
 
166
239
  Each item has two parts:
167
240
 
168
- - **`mapping`** — the Wix site/app/bundle, the Wix entity and entitlement, the Apple product
169
- id, the Apple resource id and the product type.
241
+ - **`mapping`** — the Wix site/app/bundle, the Wix entity and entitlement (or, for a price
242
+ tier, its `unlocks`), the Apple product id, the Apple resource id and the product type.
170
243
  - **`provisioning`**:
171
244
  - `outcome` — one of `created`, `updated`, `reused`, `conflict`, `partial`, `failed`
172
- (`status` also reports `absent` and `incomplete`)
245
+ or `deferred` (`status` also reports `absent` and `incomplete`)
173
246
  - per-step states and any conflicts
174
247
  - `unmet` — where Apple still differs from the manifest
175
248
  - the requested and observed price, availability and locales
@@ -207,9 +280,8 @@ future importer must authenticate ownership and re-read Apple before activating
207
280
 
208
281
  Not in this version:
209
282
 
210
- - subscriptions and consumables
283
+ - subscriptions
211
284
  - review screenshot upload and submission
212
285
  - changing a live price
213
- - Wix catalog discovery
214
286
  - Google Play
215
287
  - backend import and checkout activation
@@ -45,7 +45,8 @@ himi notify setup --content ./liftgear --target wix \
45
45
  The APNs environment must match the signed app. The Android package must match its
46
46
  Firebase client configuration; it need not equal the iOS bundle ID. Existing public
47
47
  bindings come from `provisioning.json`; flags override them. Keep the build configuration
48
- aligned with the selections. This command does not rewrite `config.ts` or build a binary.
48
+ aligned with the selections. A successful setup turns push on in `config.ts` (see
49
+ [Native push configuration](#native-push-configuration)); the command never builds a binary.
49
50
 
50
51
  To also request Apple signing resources, add `--apple-signing`, `--asc-key-file`,
51
52
  `--asc-key-id`, and `--asc-issuer-id`. The App Store Connect key is separate from the
@@ -82,13 +83,26 @@ repeats the provisioning POST automatically. Inspect status before retrying.
82
83
  After setup, continue normal app development, validation, publish/preview/promote and
83
84
  native distribution. Native push configuration, member authentication, device-token
84
85
  registration and a delivery/tap test are still required; for the test, see
85
- [Send a test notification](#send-a-test-notification). Setup does not enable native
86
- capabilities, choose a Ping family, send notifications or verify phone delivery; the app's
87
- `config.ts` does the first two (see [Native push configuration](#native-push-configuration)).
86
+ [Send a test notification](#send-a-test-notification). Setup turns push on in the app's
87
+ `config.ts` and picks the Ping family there (see
88
+ [Native push configuration](#native-push-configuration)); it does not send notifications or
89
+ verify phone delivery.
88
90
 
89
91
  ## Native push configuration
90
92
 
91
- The app's own `config.ts` turns push on; `himi notify setup` never edits it.
93
+ Once the setup is accepted (`ready`, or still `provisioning`), `himi notify setup` turns push on in
94
+ the app's own `config.ts`, once. Until it is on, the command does not report success.
95
+ It adds only what is missing and leaves what you wrote alone:
96
+
97
+ - a `push` block with no `routes`;
98
+ - `"push"` in `capabilities`;
99
+ - `siteId` in a `WixConsumerAuthProvider({ … })` login that has none.
100
+
101
+ It saves the original to `.himi/notify-setup-backup/`, checks the edit with
102
+ `himi validate --strict`, and puts the original back if the edit introduced an error. What it
103
+ cannot add safely, such as a login that is not a Wix member login or a `push` block of your own,
104
+ it prints as an instruction instead. `--dry-run` shows the planned edit without writing it, and
105
+ a second run changes nothing. The result:
92
106
 
93
107
  ```ts
94
108
  auth: WixConsumerAuthProvider({
@@ -99,15 +113,12 @@ auth: WixConsumerAuthProvider({
99
113
  bootAsVisitor: true,
100
114
  }),
101
115
  capabilities: ["push" /* , … */],
116
+ // Routes are derived at build time from provisioning.json and your screens; see himi validate.
102
117
  push: {
103
118
  registrar: "wix-ping",
104
119
  pingAppName: "WixBrandedWebApp",
105
120
  requestPermissionOnLaunch: true,
106
121
  payloadAdapter: "wix-ping",
107
- routes: [
108
- { match: "my-account/:site/stores/orders/:orderId",
109
- link: "<scheme>://screen/<order-screen>?id={orderId}" },
110
- ],
111
122
  },
112
123
  ```
113
124
 
@@ -119,13 +130,72 @@ push: {
119
130
  Keep `siteId` in `auth` on the site `himi notify setup` configured. A changed `siteId` or
120
131
  `clientId` ships with `himi push` like the rest of the config, and the app picks it up on
121
132
  its next launch; until then iOS stops registering rather than register the old site.
122
- - `routes` turn the Wix links inside notifications into the app's screens, one route per
123
- link shape. A notification whose link matches no route opens the app without navigating.
124
- `himi validate` warns when a route names a screen the app does not declare.
133
+ `himi validate` reports `push-auth-site` when the login is not a member login on that site.
125
134
  - The `push` block ships with `himi push`; changing it needs no new binary. The binary still
126
135
  has to carry the push entitlement: the `branded-lite` shell requests it, and the signing
127
136
  profile must grant `aps-environment` (see the QA build notes below).
128
137
 
138
+ ### Notification routes
139
+
140
+ Routes turn the Wix links inside notifications into the app's screens. When the `push` block
141
+ has no `routes` and `provisioning.json` lists the site's verticals (`himi site analyze --apply`
142
+ writes them), every build (`himi run`, `himi validate` and `himi push`) derives the routes from
143
+ the app's linkable screens: `navigation.linkableScreens`, or every screen when it lists none.
144
+
145
+ Routes are never written into `config.ts`, so adding or renaming a screen needs only the next
146
+ build. A notification gets a route only when its screen is certain: the screen `push.screens`
147
+ names, or else the destination's canonical screen id. Template apps already use those ids. The
148
+ site's verticals decide which destinations `himi validate` expects a screen for.
149
+
150
+ | Destination | Site vertical | Canonical screen | Opens |
151
+ |---|---|---|---|
152
+ | `store.order` | `store` | `himi_order` | the order |
153
+ | `bookings.session` | `bookings` | `himi_bookings` | the member's bookings |
154
+ | `groups.post` | `groups` | `himi_group` | the group |
155
+ | `forum.thread` | `forum` | `himi_forum_post` | the post |
156
+ | `forum.create` | `forum` | `himi_create_post` | the new-post screen |
157
+ | `blog.post` | `blog` | `himi_blog_post` | the post |
158
+ | `members.profile` | `members` | `himi_member` | the member |
159
+ | `events.event` | `events` | `himi_event` | the event |
160
+ | `galleries.shared` | `galleries` | `himi_galleries` | the gallery list |
161
+ | `galleries.album` | `galleries` | `himi_photo_album` | the album |
162
+ | `notifications.list` | `notifications` | `himi_notifications` | the notifications list |
163
+
164
+ For screens with other ids, name them in `push.screens`. It is for authoring only and is never
165
+ served:
166
+
167
+ ```ts
168
+ push: {
169
+ // …
170
+ screens: {
171
+ "store.order": "my_order",
172
+ "bookings.session": "my_bookings",
173
+ "blog.post": { screenId: "article", paramKey: "slug" },
174
+ },
175
+ },
176
+ ```
177
+
178
+ A screen that opens one item gets its id in the param named `id` when its linkable screen
179
+ declares one, otherwise in its only declared param key. When it declares several, name the one it
180
+ reads with `paramKey`. The key has to be one of the screen's `paramKeys`: a shell passes a screen
181
+ only the params its linkable screen declares.
182
+
183
+ `himi validate` lists, without failing even under `--strict`, each destination the site has that
184
+ no screen takes, with the fix, and the verticals Wix sends no member notification links for.
185
+
186
+ To write the table yourself, declare `routes`. A declared table, even an empty one, is used as
187
+ is:
188
+
189
+ ```ts
190
+ routes: [
191
+ { match: "my-account/:site/stores/orders/:orderId",
192
+ link: "<scheme>://screen/<order-screen>?id={orderId}" },
193
+ ],
194
+ ```
195
+
196
+ A notification whose link matches no route opens the app without navigating. `himi validate`
197
+ warns when a declared route names a screen the app does not declare.
198
+
129
199
  **Customer push QA builds use `himi dist --signing byo`.** After publishing an approved
130
200
  release, use a backend and reachable runner source that support the customer-signing flow.
131
201
  Use the app owner's account for both logins and explicitly select the deployed customer target:
@@ -217,6 +217,11 @@ images stay as `static.wixstatic.com` URLs in descriptors — do not vendor
217
217
  literals (`HIMALAYA.md` copy rule).
218
218
  - **Auth** — `wix-consumer`; boot visitor unless a job needs members.
219
219
  `himi docs wix-login-integration`. Members are **site members**.
220
+ - **Notifications** — give each vertical's detail screen its canonical id (`himi_order`,
221
+ `himi_event`, `himi_forum_post`, …) or name it in `push.screens`, and list it in
222
+ `navigation.linkableScreens` with the param it reads. After `himi notify setup`, every build
223
+ derives the notification routes from `provisioning.json` and those screens; `himi validate`
224
+ lists the ones still unrouted. `himi docs push-cli-onboarding`.
220
225
 
221
226
  ### 5. Prove it, then ship
222
227
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1154.0",
3
+ "version": "0.1156.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": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "abcc18f41ab0377e683efdd8751e550f7c7aad90c431731a54d9960c"
70
+ "falconPackageHash": "5d6bcf8d364821a37f27c35e0d3c50376b277479f44cc1e94d5c6729"
71
71
  }