@wix/himalaya-cli 0.1573.0 → 0.1574.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 (42) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/message-format.d.ts +59 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +3 -3
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/l10n.mjs +8 -3
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +3 -3
  5. package/dist/cli.mjs +2618 -1172
  6. package/dist/preview-assets.generated.bin +0 -0
  7. package/guides/himi-json-output.md +41 -2
  8. package/guides/himi-mcp.md +1 -1
  9. package/guides/himi-test.md +3 -2
  10. package/guides/iap-products.md +1 -1
  11. package/guides/importing-oneapp-translations.md +115 -0
  12. package/guides/index.json +6 -0
  13. package/guides/one-app-section-mappings.md +16 -4
  14. package/guides/one-app-to-himi-app.md +167 -10
  15. package/guides/qa-builds.md +21 -1
  16. package/guides/templates.md +3 -2
  17. package/guides/testing-himi-apps.md +1 -1
  18. package/package.json +3 -3
  19. package/templates/beauty-salon/dev/index.ts +22 -6
  20. package/templates/beauty-salon-owner/dev/index.ts +22 -6
  21. package/templates/branded-lite/dev/index.ts +22 -6
  22. package/templates/community-club/dev/index.ts +22 -6
  23. package/templates/community-club-owner/dev/index.ts +22 -6
  24. package/templates/events-meetups/dev/index.ts +22 -6
  25. package/templates/events-meetups-owner/dev/index.ts +22 -6
  26. package/templates/gym-fitness/dev/index.ts +22 -6
  27. package/templates/gym-fitness-owner/dev/index.ts +22 -6
  28. package/templates/himi-chat/dev/index.ts +22 -6
  29. package/templates/nonprofit-cause/dev/index.ts +22 -6
  30. package/templates/nonprofit-cause-owner/dev/index.ts +22 -6
  31. package/templates/owner-lite/dev/index.ts +22 -6
  32. package/templates/podcast-creator/dev/index.ts +22 -6
  33. package/templates/podcast-creator-owner/dev/index.ts +22 -6
  34. package/templates/portfolio-creative/dev/index.ts +22 -6
  35. package/templates/portfolio-creative-owner/dev/index.ts +22 -6
  36. package/templates/restaurant/dev/index.ts +22 -6
  37. package/templates/restaurant-owner/dev/index.ts +22 -6
  38. package/templates/retail-store/dev/index.ts +22 -6
  39. package/templates/retail-store-owner/dev/index.ts +22 -6
  40. package/templates/surface-showcase/dev/index.ts +22 -6
  41. package/templates/yoga-studio/dev/index.ts +22 -6
  42. package/templates/yoga-studio-owner/dev/index.ts +22 -6
Binary file
@@ -6,7 +6,7 @@ repurposed when human wording changes. New codes may be added; consumers must
6
6
  handle an unknown code as a failure, without parsing prose.
7
7
 
8
8
  **Rollout:** this contract is enforced for **`himi push`**, including `--only`,
9
- first, for **`himi store iap connect`**, for **`himi site member-app`**, for **`himi fidelity`**, and for the **`himi base44`** entity and function verbs. Other subcommands still have legacy failures without codes and are a
9
+ first, for **`himi store iap connect`**, for **`himi site member-app`**, for **`himi fidelity`**, for the **`himi base44`** entity and function verbs, and for **`himi l10n import`**. Other subcommands still have legacy failures without codes and are a
10
10
  follow-up migration. This is an additive change: existing `error`, `errors`,
11
11
  `message`, `hint`, status, body, and gate-specific fields stay as they were.
12
12
  Some legacy envelopes have no singular `error` or `hint`; consumers must not
@@ -114,6 +114,34 @@ prints neither.
114
114
 
115
115
  This is not a whole-command migration: the command's other refusals still have no code.
116
116
 
117
+ ## `himi l10n import` codes
118
+
119
+ Every refusal from `himi l10n import` carries one of these. Usage-grammar refusals exit 2;
120
+ operational refusals exit 1. A successful import (exit 0) reports `unsupported`,
121
+ `missingInSource`, `converted`, `duplicateSources`, `keyConflicts`, `staleKeys`, `invalidKeys` and `warnings` inside
122
+ its `ok: true` envelope — those are findings, not failures.
123
+
124
+ | Code | Meaning |
125
+ |---|---|
126
+ | `invalid_usage` | The subcommand, `<dir>` positional, app selector (`--app`/`--content`) or `--locales`/`--source` value is missing or malformed (a bare `--locales` is refused, not read as "all"). Nothing was read or written. Exit 2. |
127
+ | `unsupported_format` | `--format` is missing or names a format other than `oneapp`; `formats` lists what exists. Exit 2. |
128
+ | `app_not_found` | `--app <slug>` is not a slug, or `apps/<slug>/config.ts` does not exist under the current directory. Exit 2. |
129
+ | `messages_dir_not_found` | `<dir>` does not exist or is not a directory. Exit 1. |
130
+ | `no_messages_found` | `<dir>` holds no `messages_<locale>.json` file. Exit 1. |
131
+ | `source_locale_missing` | There is no `messages_<source>.json` — the source locale supplies every row's `source` string, so nothing can be converted without it. `found` lists the tags that do exist. Exit 1. |
132
+ | `locale_file_missing` | A `--locales` tag has no `messages_<tag>.json`; `missing` and `found` name them. Exit 1. |
133
+ | `messages_not_utf8` | A messages file is not valid UTF-8, has a UTF-16 byte-order mark, or contains NUL bytes (a UTF-16 export, even an ASCII-only one, is the usual cause). A UTF-8 BOM is tolerated and stripped. Exit 1. |
134
+ | `messages_unreadable` | The messages directory cannot be listed, or a messages file cannot be read (a directory or a FIFO in its place is refused up front, so the import never blocks on one), is not JSON, is not an object, or has non-string values (`keys` names up to five offenders). Exit 1. |
135
+ | `catalog_unreadable` | An existing `apps/<app>/l10n/<tag>.json` cannot be parsed, or holds a message that is not an object. The import refuses rather than overwrite translations it could not read; the file is left untouched. Exit 1. |
136
+ | `unsafe_path` | A `messages_<locale>.json` inside the messages directory, the app's `l10n` directory, or one of its catalogs is a symbolic link. The import never follows one: it would read a file the translation directory points at into the generated catalog, or write outside the app. Nothing was written. (The directory named on the command line may itself be a link — that is the caller's choice.) Exit 1. |
137
+ | `catalog_write_failed` | Writing a catalog failed, or the app's `l10n` path cannot be inspected or created (for example `l10n` is a regular file); `written` lists what landed before the failure. Exit 1. |
138
+ | `import_failed` | The conversion itself threw on input the earlier checks accepted. Nothing was written. Exit 1. |
139
+ | `invalid_flag_value` | The CLI's global flag grammar refused a flag before the command ran (for example `--overwrite=false`: a switch takes no value). `problems` lists each one. Exit 2. |
140
+
141
+ This is not a whole-command-group migration: only `l10n import` exists today, and all of
142
+ its refusals are coded — including the global flag-grammar refusal, which carries a `code`
143
+ for `l10n` only.
144
+
117
145
  ## `himi store iap status` codes
118
146
 
119
147
  Status adds these stable codes to incomplete JSON results. Existing result fields and exit
@@ -143,7 +171,7 @@ existing `problems` list.
143
171
  | Code | Meaning |
144
172
  |---|---|
145
173
  | `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. |
146
- | `browser_unavailable` | The headless Chromium the screenshots are taken in is not installed. Run `npx playwright install chromium` once. Nothing was rendered or written. Exit 2. |
174
+ | `browser_unavailable` | The headless Chromium the screenshots are taken in is not installed. Run the install command the error names once (it uses the CLI's own Playwright, so it installs the Chromium revision the CLI launches). Nothing was rendered or written. Exit 2. |
147
175
 
148
176
  An invalid manifest fails with `manifest_invalid`, like the other `store iap` commands.
149
177
 
@@ -217,6 +245,17 @@ whole body gzip-encoded because it is over the plain 8 MB cap:
217
245
  |---|---|
218
246
  | `compressed_publish_unsupported` | The publish body went out gzipped and the publishing service did not accept it compressed. It either read the bytes as malformed JSON, or measured the decompressed body against the 8 MB uncompressed limit (HTTP 413). Either the service does not accept compressed bodies yet, or the credential is a self-serve one, which keeps the 8 MB uncompressed limit. Nothing was written. |
219
247
 
248
+ ## `himi template screens --upsert` codes
249
+
250
+ `--upsert` writes many generated screens at once. Its refusals carry a code; nothing is written
251
+ on any of them (a failed batch restores every file). Other `template screens` paths stay legacy.
252
+
253
+ | Code | Meaning |
254
+ |---|---|
255
+ | `invalid_usage` | `--upsert` was given without `--from <file>`. Exit 2. |
256
+ | `invalid_input` | The `--from` file cannot be read or is not JSON. Exit 2. |
257
+ | `upsert_refused` | The JSON is not `{ "<screenId>": <ScreenDescriptor> }`, an id or `screenId` does not match, the result would not parse as plain SCREENS entries, or a target file is a symlink. `error` names which. Exit 2. |
258
+
220
259
  ## `himi site member-app` codes
221
260
 
222
261
  `himi site member-app set|clear|show <site>` prints one envelope. A refusal from the publishing service
@@ -112,7 +112,7 @@ runs a session with it.
112
112
  | `himi_build` | Compile the app's worker bundles. |
113
113
  | `himi_validate` | Lint the app without publishing anything. |
114
114
  | `himi_test` | The behavioral crawl — every action each screen declares, against the app's mocked network. Never reaches live data. |
115
- | `himi_render` | Render one screen and return the PNG to the agent (needs Playwright's Chromium: `npx playwright install chromium`). |
115
+ | `himi_render` | Render one screen and return the PNG to the agent (needs Playwright's Chromium: run the install command `himi render --png` prints). |
116
116
  | `himi_progress` | Read or add to the build journal the owner follows. |
117
117
  | `himi_preview_start` / `_status` / `_stop` | The live preview — see below. |
118
118
  | `himi_whoami` | Which account the stored sign-in belongs to. |
@@ -492,8 +492,9 @@ behavior. This instruction does not add an automatic suppression or retry gate t
492
492
 
493
493
  **Screenshots** are on by default when a headless browser is installed: one PNG per screen
494
494
  to `.himi/shots/`, showing the settled state at phone width. Absent browser ⇒ the report
495
- carries `shots.unavailable` and the ledger names the one-time `npx playwright install
496
- chromium`. Fully hermetic; `--no-shots` opts out.
495
+ carries `shots.unavailable` and the ledger names the one-time install command for the
496
+ CLI's own Playwright (`node '<CLI>/node_modules/playwright/cli.js' install chromium`; a bare
497
+ `npx playwright install chromium` can install another revision). Fully hermetic; `--no-shots` opts out.
497
498
 
498
499
  ### Exit codes
499
500
 
@@ -411,7 +411,7 @@ product's review screenshot from the app's own purchase screen, so nobody has to
411
411
 
412
412
  **One-time setup.** The screenshots are taken in a headless Chromium on your machine, so no phone,
413
413
  simulator or Xcode is needed. The Playwright library comes with himi; the browser is a separate
414
- download (~170MB) that you run once: `npx playwright install chromium`. Without it the command stops
414
+ download (~170MB) that you run once, with the install command the CLI prints (`node '<CLI>/node_modules/playwright/cli.js' install chromium`). Without it the command stops
415
415
  before rendering anything and says so (`browser_unavailable`). A `--dry-run` doesn't need it.
416
416
 
417
417
  1. **It renders the screen.** For each item it opens that Wix item's detail screen: `himi_product_<id>`,
@@ -0,0 +1,115 @@
1
+ # Importing OneApp translations
2
+
3
+ An app moving to Himi from the OneApp engine already has its copy translated: one flat
4
+ JSON file per locale, `messages_<locale>.json`, mapping an engine key to an ICU
5
+ MessageFormat string (`{ "DOWNLOAD_BTN": "Download", … }`). Himi's per-app catalog
6
+ (`l10n/<tag>.json` in the app's content directory) is a different shape, and re-keying
7
+ thousands of strings by hand is error-prone. `himi l10n import` does the conversion:
8
+
9
+ ```bash
10
+ himi l10n import --format oneapp ./strings --app my-app --locales en,he --json
11
+ ```
12
+
13
+ reads `./strings/messages_en.json` + `./strings/messages_he.json` and writes the app's
14
+ `l10n/en.json` + `l10n/he.json` (`--app <slug>` names an app in a checkout; off-repo,
15
+ point `--content` at the app's directory). After the import, a descriptor (or a worker,
16
+ via `t()`) references each message by its OneApp key: `$t:DOWNLOAD_BTN`.
17
+
18
+ ## What it does
19
+
20
+ - **One catalog per locale**, in the existing shape: rows keyed by the extractor's
21
+ content hash of the **source-locale** string, each carrying `source` (the `--source`
22
+ locale's text, default `en`), `key` (the OneApp key — what `$t:<KEY>` resolves) and
23
+ `translation` (that locale's text; empty in the source locale's own catalog).
24
+ - **Default locales: every `messages_<tag>.json` found** in the directory. `--locales`
25
+ narrows the list; a requested tag with no file is a refusal, not a silent skip.
26
+ - **Placeholders**: OneApp's legacy `{{name}}` spelling (spaces and hyphens allowed:
27
+ `{{ name }}`, `{{user-name}}`) becomes ICU `{name}` and is listed under `converted`. A
28
+ double brace that is not a plain name (`{{first name}}`) is kept verbatim and listed under
29
+ `unsupported`, because the runtime would paint it literally; so is a `{{name}}` inside an
30
+ ICU quoted span (`'{{name}}'`), whose braces ICU makes literal. The rewrite walks the
31
+ pattern with the runtime's own ICU tokenizer and edits the original text in place, so branch
32
+ order and spacing are preserved (the row id is a hash of that text) and a genuine ICU double brace (`one {{count}} other {{count} files}` — branch brace plus
33
+ argument brace) is never corrupted.
34
+ - **ICU subset**: each message is parsed with the subset the runtime formats — simple
35
+ arguments, `plural` with `=N`/`#` (the Hebrew dual included) and `select`. Anything
36
+ outside it — `selectordinal`, number/date skeletons, plural `offset:`, a plural nested
37
+ in another plural, a quoted `'#'` inside a plural branch, branch text the tokenizer cannot
38
+ parse (a stray comma between branches, a quoted brace), a branch selector the runtime can
39
+ never select (`one {a};other {b}` — the `;other` is part of the key — or a non-canonical
40
+ `=01`/`=1.0`, which never equals the number), a plural or select
41
+ with no `other` branch, HTML tags — is kept **verbatim**
42
+ (the runtime degrades to visible text by contract) and listed under `unsupported` with key
43
+ and locale.
44
+ - **Merging**: an existing catalog's manual translations and keys survive a re-import
45
+ unless `--overwrite`; provenance fields and rows the import does not touch survive the
46
+ import. An existing catalog that cannot be parsed is refused, never clobbered.
47
+ - **Changed source text**: row ids are content hashes, so editing a source string moves its
48
+ key to a new row. The old row keeps its text (for the source-string swap) but loses the
49
+ key, so exactly one row answers each `$t:` key; the dropped keys are listed under
50
+ `staleKeys`. This happens with or without `--overwrite`, and in a target locale whose file
51
+ has no entry for the key yet. When only the row id moved (the text did not change), the
52
+ manual translation moves with the key.
53
+ - **The source-string extractor** keeps a prior row only while a descriptor still contains
54
+ its text or references its key as `$t:`. A key used only from a worker through `t()`
55
+ therefore needs the import re-run after an extract.
56
+ - **Keys a descriptor cannot reference**: a `$t:` binding accepts letters, digits, `_`, `.`
57
+ and `-`. A OneApp key outside that (`settings:title`, `Home Header`) is still written —
58
+ a worker's `t()` takes any string — but listed under `invalidKeys`, since no `$t:<key>`
59
+ can reach it.
60
+ - **Native rows**: when the app lives inside the Himalaya source checkout, its English
61
+ catalog also receives the native login-gate rows (the same rows the extractor adds), so
62
+ the derived-output check stays green; other locales hold translations only.
63
+ - **Symbolic links are never followed**: a `messages_<locale>.json` that is a link, a linked
64
+ `l10n` directory, or a linked catalog is refused (`unsafe_path`) before anything is read
65
+ or written, so an untrusted translation package cannot pull another local file into a
66
+ catalog or redirect a write outside the app. Catalogs are written through a temporary
67
+ file and a rename, never in place.
68
+ - **Deterministic output**: ids sorted, no timestamp — re-running over the same inputs
69
+ produces no diff.
70
+ - **Edge cases reported, never silent**: a key present in a target locale but missing in
71
+ the source is skipped and listed under `missingInSource`; OneApp keys sharing one source
72
+ string all survive, each on its own row id, and the group is listed under
73
+ `duplicateSources` (no key keeps the plain id, so a key added later cannot take an id
74
+ from one already imported).
75
+
76
+ ## The report
77
+
78
+ `--json` prints one envelope:
79
+
80
+ ```json
81
+ {
82
+ "ok": true,
83
+ "written": [{ "tag": "he", "path": "my-app/l10n/he.json", "count": 412 }],
84
+ "unsupported": [{ "key": "UPDATED_AT", "locale": "he", "construct": "date argument \"when\" (short)" }],
85
+ "missingInSource": [{ "key": "ONLY_IN_HE", "locales": ["he"] }]
86
+ }
87
+ ```
88
+
89
+ plus `converted`, `duplicateSources`, `keyConflicts`, `staleKeys`, `invalidKeys` and
90
+ `warnings` when non-empty.
91
+ Failures carry a stable `code` — see
92
+ [the JSON failure contract](himi-json-output.md#himi-l10n-import-codes).
93
+
94
+ ## Declare the locales
95
+
96
+ A catalog only bakes and serves for a locale the app declares, and only `en`, `he` and
97
+ `es` can be declared. The import checks each imported tag against the app's `locale`
98
+ config and warns with the line to add to its `locale` block:
99
+
100
+ ```ts
101
+ translations: ["es", "he"]
102
+ ```
103
+
104
+ A tag outside those three (`fr`, or a region tag like `es-MX`) still gets its catalog
105
+ written, but is warned about separately and is never offered as config — declaring it would
106
+ make the config fail.
107
+
108
+ `--source` should be the app's default locale: the runtime falls back to a row's source text
109
+ only for the default locale, so importing another locale as the source warns.
110
+
111
+ ## Scope
112
+
113
+ The importer is generic for any OneApp-format catalog (flat key → ICU string). Fetching
114
+ messages from a translation service, exporting back, and other source formats are out of
115
+ scope — adapters can be added behind `--format` later.
package/guides/index.json CHANGED
@@ -74,6 +74,12 @@
74
74
  "category": "CLI & Loop",
75
75
  "file": "himi-json-output.md"
76
76
  },
77
+ {
78
+ "id": "importing-oneapp-translations",
79
+ "title": "Importing OneApp translations",
80
+ "category": "CLI & Loop",
81
+ "file": "importing-oneapp-translations.md"
82
+ },
77
83
  {
78
84
  "id": "himi-mcp",
79
85
  "title": "himi in your coding agent (MCP)",
@@ -110,8 +110,8 @@ The **Himalaya implementation** cell is blank for every unassigned row. Notes on
110
110
 
111
111
  | Source sectionDefinitionId | Source viewer component | Himalaya implementation | Placement / reuse | Class | Settings, actions and gaps |
112
112
  | --- | --- | --- | --- | --- | --- |
113
- | `referrals_refer_section` | `referrals.components.ReferWidget` | | | Empty | No consumer referral-code/link sharing and referral-status feature was found. Do not map to Loyalty or generic sharing without a user decision. |
114
- | `referrals_description_section` | `referrals.components.DescriptionWidget` | | | Empty | No referral-program description/data implementation was found. Text primitives alone do not resolve the business-bound referral content. |
113
+ | `referrals_refer_section` | `referrals.components.ReferWidget` | | | Empty | No consumer referral-code/link sharing and referral-status feature was found. Do not map to Loyalty or generic sharing without a user decision. **Probe first:** `GET /_api/referral-programs/v1/program` answered the One App visitor identity in a 2026-11 migration, and the program copy was built live in an app-local section. The member's own referral link/code was not generated; resolve it before you mark the Refer action supported. |
114
+ | `referrals_description_section` | `referrals.components.DescriptionWidget` | | | Empty | No referral-program description/data implementation was found. Text primitives alone do not resolve the business-bound referral content. **Probe first:** `GET /_api/referral-programs/v1/program` answered the One App visitor identity in a 2026-11 migration, and the program copy was built live in an app-local section. The member's own referral link/code was not generated; resolve it before you mark the Refer action supported. |
115
115
 
116
116
  ### 9. member-list-section-definitions.json
117
117
 
@@ -330,8 +330,8 @@ These are the exact definitions whose target cells above remain empty. “Not fo
330
330
  | `events-section-definitions.json` | `events_guests_section` | The Events detail has only a best-effort attendee summary/avatar strip using an owner-scoped guest query. It is not a full consumer guest-list section; leave the mapping unassigned. |
331
331
  | `groups-section-definitions.json` | `single_group_feed_section` | A feed exists, but it selects a relevant/home group internally and does not accept the configured fixed-group selector. No verified single-group feed adapter exists; keep this target empty until a policy/implementation is chosen. |
332
332
  | `loyalty-section-definitions.json` | `loyalty_status_section` | The existing Loyalty worker lists the program and rewards but does not load this member's points balance/tier/status. A rewards catalog is not a loyalty-status widget. |
333
- | `referrals-section-definitions.json` | `referrals_refer_section` | No consumer referral-code/link sharing and referral-status feature was found. Do not map to Loyalty or generic sharing without a user decision. |
334
- | `referrals-section-definitions.json` | `referrals_description_section` | No referral-program description/data implementation was found. Text primitives alone do not resolve the business-bound referral content. |
333
+ | `referrals-section-definitions.json` | `referrals_refer_section` | No consumer referral-code/link sharing and referral-status feature was found. Do not map to Loyalty or generic sharing without a user decision. A visitor-readable program read exists; see the row above. |
334
+ | `referrals-section-definitions.json` | `referrals_description_section` | No referral-program description/data implementation was found. Text primitives alone do not resolve the business-bound referral content. A visitor-readable program read exists; see the row above. |
335
335
  | `pages-section-definitions.json` | `pages_section` | No matching Wix Pages content fetch/render adapter was found. Generic CMS or RichContent renderers do not identify the original page entity or its data contract. |
336
336
  | `photoalbum-section-definitions.json` | `photoAlbums_list_section` | Existing Photo Albums code is an empty-state placeholder with no album API wired. Pro Gallery uses a different data source and is not a substitute; choose an implementation explicitly. |
337
337
  | `photoalbum-section-definitions.json` | `photoAlbums_photos_list` | Existing Photo Albums code is an empty-state placeholder with no album API wired. Pro Gallery uses a different data source and is not a substitute; choose an implementation explicitly. |
@@ -377,3 +377,15 @@ These rows have reusable code, but their remaining requirements must not disappe
377
377
  ## GitHub implementation references
378
378
 
379
379
  The linked sources are in private Wix repositories. The inventory and gap decisions above are usable as historical capability evidence without local filesystem references. Links identify implementation code, not production deployment or exact feature parity.
380
+
381
+ ## Definitions outside the pinned inventory
382
+
383
+ Migrated apps contain section definitions that the pinned inventory above does not list. They are
384
+ outside its 77 definitions and have no verified mapping. Resolve each one with the workflow's probe
385
+ procedure (`himi docs one-app-to-himi-app`, "Probe an unmapped section") and record the result.
386
+
387
+ | Source sectionDefinitionId | Seen in | Status and leads |
388
+ | --- | --- | --- |
389
+ | `forum_post_list_section` | A 2026-11 Branded migration | Unmapped. A single-section page can point at the Forum template screen; an inline post list needs an app-local section over the Forum posts read. |
390
+ | `shared_gallery_list_section` | A 2026-11 Branded migration | Unmapped and left unresolved in that migration. Do not substitute Pro Gallery or Photo Albums without verifying the source data contract. |
391
+ | `woa_member_restaurants_reservation` | A 2026-11 Branded migration | Unmapped. That migration adapted it to a "Reserve a Table" button that opens the template reservation screen; record it as an adaptation, not as the source section. |
@@ -259,6 +259,16 @@ Apply these interpretation rules:
259
259
  a feature to account for; they do not alone prove visibility or access for every
260
260
  visitor or member. Resolve those rules separately. This check is specific to this
261
261
  migration workflow; it does not change the behavior of `himi validate`.
262
+ The check maps a source screen to its target **only** through that screen's `route`
263
+ row (`source: "/screens/<id>"`) in `ONE-APP-COVERAGE.json`, through `target.screenId`;
264
+ a row of any other kind does not map a screen, and an unmapped source screen is checked
265
+ under its own id. A control counts only as an entry of `actions`; a matching `topBar.items`
266
+ entry (a leading profile avatar, for example) does not, because iOS draws no toolbar items
267
+ yet and web/Android draw only some trailing ones. A `branded-lite` screen
268
+ that declares its own `topBar` replaces `topBarDefault`, so the shared controls are
269
+ missing on it: `himi_account`, `himi_shop`, `himi_services`, `himi_agenda`, `himi_events`,
270
+ `himi_blog` and the CMS collection screens do. Delete that `topBar` so the screen inherits
271
+ the default, or add every source control to its `actions`.
262
272
  - Apply the collected app theme before the first build: run
263
273
  `himi one-app validate-design --evidence-dir .himi/one-app --content . --fix`,
264
274
  resolve any remaining mismatches/unresolved values, then rebuild. The source is
@@ -486,7 +496,7 @@ reference's top-tabs row in R04 and its top-tabs acceptance case:
486
496
  - `missing_icon`: choose an icon that matches the page content and record it as a
487
497
  target adaptation. `missing_label`: use a label from the page content and record it.
488
498
  - `tab_overflow`: more than five tabs need the explicit overflow arrangement of
489
- viewer rule R05. Do not drop tabs to fit the bar.
499
+ viewer rule R05. Do not drop tabs to fit the bar. See [Large top-tab apps](#large-top-tab-apps).
490
500
  - `free_instance_converted`: the source editor offers bottom tabs to Branded apps
491
501
  only. Record the converted navigation as a target adaptation in the report.
492
502
  - `nested_top_tabs`: a bottom entry holds a `TOP_TABS` screen. It is out of this
@@ -495,12 +505,53 @@ reference's top-tabs row in R04 and its top-tabs acceptance case:
495
505
  With `--top-tabs keep`, the plan has `reason: "policy-keep"` and the viewer
496
506
  reference's top-tabs rules apply.
497
507
 
508
+ #### Large top-tab apps
509
+
510
+ A source with many top-tab pages (33 in one migration) converts to this arrangement:
511
+
512
+ - The first four pages in source order are tabs; a fifth **More** tab lists every other
513
+ visible page in source order, and each row pushes its page. Hidden pages get no row.
514
+ - `appEditor.outline.*` icons with no Himi name (lightning, rocket, light bulb, megaphone,
515
+ brochure, content card) need a fallback icon from `himi icons`, such as `star`,
516
+ `bell` or `doc`. Record each fallback as an adaptation.
517
+ - A source `goToScreen` to a page that is now a **tab** must select that tab. A descriptor
518
+ `himi.open` pushes a second copy of the page instead, so give static pages a small nav
519
+ worker whose action calls `navigate(s, { tab: "<tabId>" })`. A `goToScreen` to a page
520
+ in More can use `himi.open`.
521
+ - A `goToScreen` whose layout item does not exist does nothing in the source. Keep it a
522
+ no-op (a worker action that returns the state unchanged) and list it as an unresolved
523
+ route.
524
+ - A page whose only section is one vertical (Pricing, Groups, Forum, Events, Programs,
525
+ Loyalty, Galleries, Shop, Services, Forms) can point at that vertical's template screen
526
+ instead of a copy. Record its route row against the template screen id.
527
+ - Only the first tab keeps the source header; the others take the compact default bar.
528
+
498
529
  The current [viewer reference](https://apps.wix.com/_api/himi-serve/docs/one-app-viewer.md)
499
530
  supersedes simplified historical ordering, routing and layout examples. Pinned
500
531
  mapping rows are research leads: inspect the current CLI's component, SDK and
501
532
  template contracts before using them. A previously empty row does not prove the
502
533
  feature is impossible today.
503
534
 
535
+ ### Probe an unmapped section
536
+
537
+ A section whose catalogue row is "Empty", or whose definition is not in the catalogue at
538
+ all, is not unresolved until you probe it:
539
+
540
+ 1. Find the section's data contract: its `sectionDefinitionId`, the source viewer component
541
+ in the catalogue, and the Wix REST API of that domain (Wix docs or `@himalaya/wix-*`).
542
+ 2. Choose a read-only call. Confirm in the service contract that it does not write on read
543
+ (see the announcement getter above).
544
+ 3. Send it through the target transport with the app's One App visitor identity
545
+ (`himi render --live`, or the hosted preview), not with an owner credential.
546
+ 4. A 2xx with the source content means you can build the section app-locally. Record the
547
+ call, the response shape and the identity in the migration report, and mark the row
548
+ `supported` or `adapted`. A 401/403/428 for the visitor is an access result: keep the
549
+ row `unresolved` and record the status.
550
+
551
+ For example, `GET /_api/referral-programs/v1/program` answers the visitor identity, so
552
+ the referral program copy can be built live. The member's own referral link is a
553
+ separate, member-scoped contract.
554
+
504
555
  ### Standalone launch tab
505
556
 
506
557
  When the reconstructed navigation has a tab bar, preserve the app's actual tab
@@ -708,6 +759,22 @@ For a marked One App migration, `himi validate --strict` fails when the icon is
708
759
  missing, still byte-identical to a bundled template, or its pixel checks cannot run. A passing mechanical check
709
760
  cannot establish ownership of arbitrary custom artwork; compare the real mark too.
710
761
 
762
+ Expect a large ledger: a 33-page app had 796 rows, 396 of them top-bar controls. Map it
763
+ in batches with the executable ledger mapping recipe above, not by hand. Each row's
764
+ `source` is a JSON pointer into `raw-structure.json`, and the shape says what to set:
765
+
766
+ | `source` shape | `target` |
767
+ | --- | --- |
768
+ | `/screens/<id>` (`route`) | `{ "screenId": "<target>" }`. `validate-topbar` maps screens only through these rows. |
769
+ | `/screens/<id>/layout/items/<i>` | the target screen of that layout item (a top-tab page). |
770
+ | `…/layout/items/<i>/elements/<j>` | that page's screen; an `interaction` row also names the `control` (the section's element id). |
771
+ | `/screens/<id>/topBar/…` | the screen, and the `control` id of the matching top-bar action (`cart`, `notifications`, `chat`, `profile`). |
772
+ | `/sections/<id>` | the screen of the page that holds that section. |
773
+ | `/businessProfile…` | the business-profile screen. |
774
+
775
+ Decide per row, and let the recipe list every row it could not map or decide. Never
776
+ invent a status for an undecided row. Run `coverage check` after each run.
777
+
711
778
  Map each row to a target screen and, for interactions or top-bar controls, a
712
779
  target control. A `design` row is app-wide, so it needs no screen or control: it is
713
780
  satisfied by the design check instead. Set its `status` to `supported`, `adapted`, `conditional`, or
@@ -843,6 +910,31 @@ not share mutable filters accidentally. Keep static authored content in the app,
843
910
  and products, services, availability, posts and member records live on the same site.
844
911
  Refresh data after operations without reconnecting layout to the One App editor.
845
912
 
913
+ ### Write generated screens as static SCREENS entries
914
+
915
+ `himi template trim`, `himi template screens --show/--write` and
916
+ `himi one-app validate-topbar` read `dev/index.ts` statically. They see only plain
917
+ `"<screenId>": { "screenId": "<screenId>", … },` entries inside the `SCREENS` literal.
918
+ A screen merged in at runtime (`Object.assign`, an imported module), a spread, or a
919
+ function call in the literal makes them fail with "expected a `"<screenId>": {` entry",
920
+ "homeScreenId … is not a screen in the SCREENS map" or "no target screen mapping to an
921
+ authored descriptor". Blank lines and whole-line `//` comments between entries are
922
+ accepted.
923
+
924
+ So generate descriptors as data and write them with the CLI:
925
+
926
+ ```sh
927
+ himi template screens --upsert --from .himi/one-app/gen/screens.json
928
+ ```
929
+
930
+ The file is one JSON object, `{ "<screenId>": <ScreenDescriptor>, … }`. Each existing
931
+ id is replaced in place; each new id is added and registered in
932
+ `navigation.linkableScreens`. One invalid entry refuses the batch and restores every
933
+ file. A builder call such as `detailedHeader(...)` written inline in `dev/index.ts`
934
+ cannot be inspected: call the builder in your generator and serialize the value it
935
+ returns. Run the generator inside the app (after `npm install`), so
936
+ `@wix/himalaya/screens` resolves to the app's own dependency.
937
+
846
938
  ### Screen-content contract
847
939
 
848
940
  Use the current [screen design guide](https://apps.wix.com/_api/himi-serve/docs/designing-himi-screens.md) and the app's
@@ -942,7 +1034,12 @@ a new destination to read the site's current data; never invent or retain a temp
942
1034
  Use `siteUrl` from the successful collection's manifest for the auth binding. It is
943
1035
  the first usable value from `website.publicUrl`, then legacy `website.url`: an HTTPS
944
1036
  origin and optional base path, with no credentials, custom port, query or fragment.
945
- `site client ensure` does not return it. Keep visitor/member gates from the reconstruction plan. Verify that the
1037
+ `site client ensure` does not return it.
1038
+ With the One App binding, `himi push` bakes the current product and service detail
1039
+ screens through the site's One App visitor instance. A `dev/index.ts` scaffolded by an
1040
+ older CLI reads only `clientId` for this, so it bakes no detail screens; the preview
1041
+ still resolves them live.
1042
+ Keep visitor/member gates from the reconstruction plan. Verify that the
946
1043
  built app reads the intended site (and, with a client, uses the exact registered
947
1044
  redirect). The MetaSite and a public client ID are app configuration; the collection
948
1045
  credential is not.
@@ -987,8 +1084,15 @@ as unverified in the migration report.
987
1084
 
988
1085
  The UX audit requires app-specific network fixtures. `himi test --record` records
989
1086
  only boot-time reads with the app's visitor identity, then crawls hermetically.
990
- Review its recorded/refused requests; do not run a source getter known to mutate
991
- on read (such as the legacy announcement default getter). Keep actual recordings
1087
+ Before you run it, run `himi template inspect` and read `.himi/adopt.json`
1088
+ `bootRequests[]`: every entry is a screen that can request when it appears. An entry
1089
+ with `trigger: "module-load"` is a worker that calls an action while its module
1090
+ evaluates (`svc.load();` at the bottom of the file — the pattern every migration
1091
+ worker uses); the screen descriptor names nothing, but the read runs as soon as the
1092
+ screen boots the worker. Each boot path must be a catalogue or directory read.
1093
+ Stop if one submits, orders, books, pays, or calls a getter known to mutate on read
1094
+ (such as the legacy announcement default getter). Then review the recorded and
1095
+ refused requests. Keep actual recordings
992
1096
  under `.himi/one-app/fixtures/net-mocks.json`. For local test/render discovery,
993
1097
  make `dev/net-mocks.json` a relative symlink to that private file: the source
994
1098
  archiver skips symlinks as well as `.himi/`. Verify the archive excludes both
@@ -996,19 +1100,56 @@ before publishing. Portable fixtures committed as regular files must instead be
996
1100
  synthetic test data; do not publish source catalogs, member records or credentials
997
1101
  as test evidence. Re-run affected checks after changing fixtures.
998
1102
 
1103
+ `himi push` refuses with `headless_run_failed` while `himi test` has an error finding,
1104
+ including one on an inherited template screen. Boot-only fixtures cannot cover a read
1105
+ that a tap starts (for example, choosing a category on `himi_shop`), so the hermetic
1106
+ crawl reports it stuck on loading although it works live. Reproduce it live first
1107
+ (`himi run` or the hosted preview). If it works, add a fixture for that read, or add a
1108
+ `himi-test.json` `skip[]` entry with the `screen`, `action`, `invariant` and a `reason`
1109
+ that states the live evidence (date, data shown). If it fails live, fix it. Never push
1110
+ with `--force` to pass this gate.
1111
+
1112
+ In `SPEC.md` `## Data access`, name an inherited template write the way `himi test`
1113
+ reads it: its action (`selectCategory` or `tier5.himiBrandedShop.selectCategory`), its
1114
+ endpoint (`POST /stores/v3/products/query`, with `*` for one path segment), or its
1115
+ capability or kind (`payment`, `signup`). Prose approves nothing.
1116
+
999
1117
  Render and inspect actual reconstructed screens at compact and wider widths,
1000
1118
  including loading, empty, error and denied states. Compare the source's resolved
1001
1119
  design and composition; when source visual evidence is available compare matched
1002
1120
  locale, platform and data state. Record actual visual evidence and any native
1003
1121
  platform not exercised; never infer native parity from a web screenshot.
1004
1122
 
1123
+ Known web limits:
1124
+
1125
+ - A short screen paints its root background only as tall as its content; below it the
1126
+ web host shows its own (light) background. On a dark source, check a short page and
1127
+ record the difference; native fills the screen.
1128
+ - `himi render --png` needs the Chromium of the Playwright that `himi` bundles. Run the
1129
+ install command the render hint prints. A bare `npx playwright install chromium`
1130
+ from the app directory can install another revision.
1131
+ - A Detailed header (`CollapsingHeader`) scrolls with the page in current releases. An
1132
+ older CLI drew it in a 640px scroll box, so the page scrolled inside it and
1133
+ `himi render --png` showed only the first viewport; update the CLI before you compare.
1134
+ - The One App editor preview shows demo data for member widgets ("This is a demo image
1135
+ and won't appear live") and draws some vertical pages blank. It is not a live baseline:
1136
+ compare against the published app or the source evidence instead.
1137
+
1138
+ Fonts follow the typography rules of the design check in step 1: a source family Himi
1139
+ does not ship is replaced only with the owner's explicit instruction, recorded as an
1140
+ `adapted` design row. Also resolve every other pointer the report lists under
1141
+ `notCompared` (button `uppercase`, for example); a passing design check does not cover them.
1142
+
1005
1143
  Record actual mobile UX checks for the current release, then audit them. For each
1006
1144
  platform driven, use `himi ux record --platform ios --status passed --checks <actual-checks>`
1007
1145
  (or `android`, and `failed` for a real failure). An unavailable platform requires
1008
1146
  `himi ux record --platform android --status unavailable --reason "<actual limitation>"`
1009
1147
  and no fabricated checks. Resolve failures, run `himi ux audit --strict`, and keep
1010
1148
  these records bound to the final content immediately before push. An edit changes
1011
- the release; repeat the affected checks and record that content. Never bypass this
1149
+ the release; repeat the affected checks and record that content. Any content change
1150
+ (a fixture, a trimmed screen, a recompressed icon) makes a new release hash, so redo
1151
+ `himi ux record` and `himi one-app validate-design --record --release-hash <new>` as
1152
+ the last step before `himi push`, after every size or test fix. Never bypass this
1012
1153
  gate with `--force` or claim an unavailable native platform passed.
1013
1154
 
1014
1155
  Once the implemented app passes its available checks, publish only a draft:
@@ -1017,11 +1158,27 @@ Once the implemented app passes its available checks, publish only a draft:
1017
1158
  himi push --draft --target bo
1018
1159
  ```
1019
1160
 
1020
- Keep the editable source archive included. If publication returns
1021
- `publish_body_too_large`, remove unused generated definitions or apply lossless
1022
- asset compression, then repeat affected validation. Prove emitted screens or
1023
- decoded pixels are unchanged. Do not use `--no-source` or remove authored features
1024
- to fit the upload limit; report a remaining size blocker instead.
1161
+ Keep the editable source archive included. The publish body is limited to 8,388,608
1162
+ bytes. It is about base64(source archive + worker bundles + assets + icons) plus the
1163
+ descriptor JSON, so a large app can exceed it: one 33-page migration measured
1164
+ 11,259,935 bytes. Measure early, not at the end: after `himi build`, add up
1165
+ `dist/bundles/*`, the icons and a zip of the content directory without the excluded
1166
+ paths below. `himi push` refuses an oversized body locally, before it sends anything,
1167
+ with `publish_body_too_large`; its `breakdown`
1168
+ gives each part's bytes (`appSource`, `screens`, `bundles`, `assets`, `icon`, `other`)
1169
+ and the largest entries by name. Reduce it in this order, and repeat affected
1170
+ validation after each step:
1171
+
1172
+ 1. Trim the template screens the plan does not use, and the ones a reconstructed page
1173
+ replaced (`himi template trim --keep …`). Their descriptors and workers ride the body.
1174
+ 2. Recompress images losslessly (the 1024 icon is in the body and in the archive). Prove
1175
+ the decoded pixels are unchanged.
1176
+ 3. Remove unused generated definitions.
1177
+
1178
+ The archive already leaves out `.himi/`, `.claude/`, `.agents/`, `HIMALAYA.md`,
1179
+ `node_modules/`, `dist/` and symlinks. Do not use `--no-source` or remove authored
1180
+ features to fit the upload limit; report a remaining size blocker with the breakdown
1181
+ instead.
1025
1182
 
1026
1183
  Use the target's existing authentication flow when needed. Capture the returned
1027
1184
  release and hosted preview URL, then open that exact URL in the browser and verify
@@ -1193,7 +1193,8 @@ himi dist --app <app> --platform android --aab --wait --target bo
1193
1193
  activates a draft release: `GET /v1/admin/site/store-build-check?app=&site=[&shell=]` answers
1194
1194
  `200 { ok: true, android: { packageName } }` or the same refusals, through `CheckIfBuildIsAllowed`,
1195
1195
  so it locks nothing. It names no release, so it cannot give
1196
- `409 release_predates_store_builds`; a draft the editor just built is on a current commit. It never
1196
+ `409 release_predates_store_builds`; a draft the editor just built is on a current commit. It is
1197
+ not sent the App name and icon either, so it cannot give their refusals (below). It never
1197
1198
  falls back to the locking call.
1198
1199
  - **A Containers app's TestFlight build is an App Store build.** For an app bound to a Wix site,
1199
1200
  `--platform ios --destination testflight --version <x.y.z>` with no `--signing` builds the app for
@@ -1226,6 +1227,25 @@ himi dist --app <app> --platform android --aab --wait --target bo
1226
1227
  502 (the prepared files failed Himalaya's checks, the check's code in `reason`: a fix on Wix's
1227
1228
  side, not a retry), `store_builds_unavailable` 503. A named `--signing byo` keeps the
1228
1229
  customer-signed path above, from the site's legacy BuildSecrets.
1230
+ - **A store build ships under the owner's App name and icon.** Both store builds above (a Containers
1231
+ app's `--aab` and its TestFlight build) need `appName` and `appIconUrl` on `POST /v1/admin/dist`
1232
+ (`himi dist --app-name <name> --app-icon-url <url>`): the App name and App icon the owner set in
1233
+ the Containers editor, which the Containers server reads off the project and sends. They win over
1234
+ the release's app-config `name` and icon, which the generation agent picked. Any other build
1235
+ takes them too when they are sent, and keeps the release's when they aren't. The name is trimmed,
1236
+ 1 to 200 characters, with no control characters and no `$` (Xcode would expand `$(NAME)` from the
1237
+ pool's environment into the binary), and can't start with `@` or `?` (Android would read it as a
1238
+ resource reference). The icon must be a Wix Media file, exactly
1239
+ `https://static.wixstatic.com/media/<file>`. Its 1024x1024 PNG rendition
1240
+ (`<url>/v1/fill/w_1024,h_1024,al_c,q_100/icon.png`) is fetched and checked last before the store
1241
+ id is locked, and again when the pool reads its contract (`GET /v1/dist/contract`), twice at most.
1242
+ If it can't be read then, the contract answers `503 app_icon_unavailable` and the build fails,
1243
+ rather than baking the release's or the shell's icon. iOS gets the icon re-encoded with no alpha
1244
+ channel, which App Store Connect refuses. The job records `appInfo`, and
1245
+ `GET /v1/owner/rebuild-reasons` reports `app_name` and `app_icon` as `unknown` for such a build.
1246
+ Refusals, each with `code` equal to `error`: `store_build_app_info_missing`, `app_info_incomplete`
1247
+ (one without the other), `invalid_app_name`, `invalid_app_icon_url` and `app_icon_invalid` (not a
1248
+ 1024x1024 PNG a platform can use) 400, `app_icon_unavailable` 503 (Wix Media couldn't be read).
1229
1249
 
1230
1250
  **Why the Wix production key, and only for the bundle.** There are two Android keys: the committed
1231
1251
  QA keystore and the Wix production key (WixOne). WixOne is the Play **upload** key every Branded
@@ -106,8 +106,9 @@ details beside the app:
106
106
  | `.himi/himi-test.stub.json` | a typed-journey skeleton per form (every field, then its submit) with `<FILL>` slots |
107
107
 
108
108
  `bootsOffline` is the consent preflight: it is `false` when any screen's `onAppear` dispatches into
109
- a worker that can reach Wix or the network, and `bootRequests` names each one. It is static and errs
110
- on the safe side ("may request"). The commands behind it:
109
+ a worker that can reach Wix or the network, or when a screen boots such a worker that calls an
110
+ action while its module evaluates (`svc.load();` at the top level). `bootRequests` names each one,
111
+ with `trigger: "onAppear"` or `"module-load"`. It is static and errs on the safe side ("may request"). The commands behind it:
111
112
 
112
113
  ```sh
113
114
  himi template recommend --text "a salon where clients book and buy products" # rank the catalog (or --verticals / --book)
@@ -267,7 +267,7 @@ render, local browser, zero network. `--no-shots` opts out (sweeps should).
267
267
 
268
268
  Absence is said, never silent: without the browser the report carries
269
269
  `shots.unavailable` and the ledger names the one-time remedy —
270
- `npx playwright install chromium`. Web fidelity, honestly named: these are the web
270
+ the install command `himi render --png` prints. Web fidelity, honestly named: these are the web
271
271
  renderer's pixels; native truth still comes from a device (`himi push` a preview).
272
272
 
273
273
  Hygiene: a **full** run owns `.himi/shots/` and clears it first, so a deleted screen
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1573.0",
3
+ "version": "0.1574.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": [
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@modelcontextprotocol/server": "^2.0.0",
38
- "@rive-app/canvas": "^2.21.0",
38
+ "@rive-app/canvas": "2.38.3",
39
39
  "ajv": "^8.17.1",
40
40
  "ajv-formats": "^3.0.1",
41
41
  "cheerio": "^1.0.0",
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "39a44e1a057f7d34899d550863397025bfdec7b4f241a6e45b677246"
74
+ "falconPackageHash": "3931950af337466648e08fa667462e52b854969f6441f512f82b9241"
75
75
  }