@wix/himalaya-cli 0.1610.0 → 0.1612.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/stdlib/wix-sdk/loyalty/src/extensions.d.ts +2 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/index.d.ts +78 -6
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +8 -0
- package/dist/cli.mjs +2475 -1872
- package/dist/preview-assets.generated.bin +0 -0
- package/dist/quickjs-worker-entry.js +4 -0
- package/fixtures/wix-canned.json +41 -0
- package/guides/component-reference.json +1 -1
- package/guides/component-reference.md +1 -1
- package/guides/getting-started.md +3 -1
- package/guides/himi-json-output.md +18 -5
- package/guides/himi-skill.md +3 -1
- package/guides/himi-test.md +88 -0
- package/guides/one-app-section-mappings.md +3 -4
- package/guides/qa-builds.md +8 -0
- package/guides/release-notes.md +12 -0
- package/guides/sdk-reference.json +44 -4
- package/guides/templates.md +2 -0
- package/guides/tier-5-add-a-screen.md +14 -0
- package/package.json +2 -2
- package/templates/branded-lite/dev/index.ts +84 -0
- package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +99 -9
|
Binary file
|
|
@@ -134,6 +134,10 @@ async function boot(msg) {
|
|
|
134
134
|
let sweepError = null;
|
|
135
135
|
try {
|
|
136
136
|
actions = drain(ctx, runtime, msg.skipLoad ? "himi.quickjs.actions" : "himi.quickjs.loads");
|
|
137
|
+
if (msg.settleAuthoredActions) {
|
|
138
|
+
drainDetachedJobs(ctx, runtime);
|
|
139
|
+
actions = readbackJson(ctx, actionsReadback);
|
|
140
|
+
}
|
|
137
141
|
} catch (e) {
|
|
138
142
|
sweepError = String(e?.message ?? e);
|
|
139
143
|
}
|
package/fixtures/wix-canned.json
CHANGED
|
@@ -1047,6 +1047,47 @@
|
|
|
1047
1047
|
}
|
|
1048
1048
|
]
|
|
1049
1049
|
},
|
|
1050
|
+
"GET /loyalty-tiers/v1/tiers/program": {
|
|
1051
|
+
"programSettings": {
|
|
1052
|
+
"status": "ACTIVE",
|
|
1053
|
+
"baseTierDefinition": {
|
|
1054
|
+
"name": "Bronze"
|
|
1055
|
+
},
|
|
1056
|
+
"rollingWindow": {
|
|
1057
|
+
"durationInMonths": 12
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
"tiers": [
|
|
1061
|
+
{
|
|
1062
|
+
"id": "tier-0001",
|
|
1063
|
+
"tierDefinition": {
|
|
1064
|
+
"name": "Silver"
|
|
1065
|
+
},
|
|
1066
|
+
"requiredPoints": 500
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
"id": "tier-0002",
|
|
1070
|
+
"tierDefinition": {
|
|
1071
|
+
"name": "Gold"
|
|
1072
|
+
},
|
|
1073
|
+
"requiredPoints": 1000
|
|
1074
|
+
}
|
|
1075
|
+
]
|
|
1076
|
+
},
|
|
1077
|
+
"GET /loyalty-coupons/v1/coupons/my-coupons": {
|
|
1078
|
+
"loyaltyCoupons": [
|
|
1079
|
+
{
|
|
1080
|
+
"id": "lc-0001",
|
|
1081
|
+
"rewardName": "Free filter coffee",
|
|
1082
|
+
"status": "ACTIVE",
|
|
1083
|
+
"couponReference": {
|
|
1084
|
+
"couponId": "cp-0001",
|
|
1085
|
+
"code": "BEANS-0001",
|
|
1086
|
+
"deleted": false
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
]
|
|
1090
|
+
},
|
|
1050
1091
|
"POST /reviews/v1/reviews/query": {
|
|
1051
1092
|
"reviews": [
|
|
1052
1093
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "GENERATED by core/web-embed/authoring/gen-reference.mjs from core/ts/src/schema.ts — do not hand-edit. The schema is the source of truth; regenerate on schema changes.",
|
|
3
3
|
"schemaSource": "core/ts/src/schema.ts",
|
|
4
|
-
"bindingGrammar": "$service:<service>.<path> (worker state) · $item.<field> (list row) · $flag:<key> (feature flag) · $env:<key> (client environment — #1056). Bindings resolve in the text/value props marked 'binding-aware' below — INCLUDING Modifiers token refs — but only `background` and `border.color`, which are the two whose token a binding actually fills: a worker can drive per-item theming (e.g. a game board's per-square color), not just text. `shadow` is NOT binding-aware on any renderer, and a gradient is driven by its own `backgroundGradientPath` prop resolving to a gradient token NAME, not by a binding inside a token ref — so `shadow: { token: \"$item.x\" }` and `backgroundGradient: { token: \"$item.x\" }` keep the sentinel and silently lose the per-item styling. Colour slots split by LEVEL, not by component: inside modifiers a colour is a TokenRef OBJECT ({ token: \"…\" }), inside props it is a bare TokenName STRING — there is no modifiers.color or modifiers.tint (see the 'Token slots' section in component-reference.md). A few input props render the LITERAL and are NOT binding-aware — notably SearchField.placeholder and Chip.label; SearchField.value is live and binding-aware like other content props. Bindings also CANNOT target strict-enum fields (style, size, alignment, severity, presentation, contentMode, …) — those fail at render, not validation. $env: is client-resolved (no worker) and valid in modifiers.hiddenPath and FragmentSlot.casePath ONLY — keys reduceMotion/colorScheme/sizeClass/isRTL, e.g. hiddenPath:\"$env:reduceMotion\" or hiddenPath:\"$env:colorScheme==light\", casePath:\"$env:colorScheme\"; unknown key fails open (shown / defaultCase).",
|
|
4
|
+
"bindingGrammar": "$service:<service>.<path> (worker state) · $item.<field> (list row) · $flag:<key> (feature flag) · $env:<key> (client environment — #1056). Bindings resolve in the text/value props marked 'binding-aware' below — INCLUDING Modifiers token refs — but only `background` and `border.color`, which are the two whose token a binding actually fills: a worker can drive per-item theming (e.g. a game board's per-square color), not just text. `shadow` is NOT binding-aware on any renderer, and a gradient is driven by its own `backgroundGradientPath` prop resolving to a gradient token NAME, not by a binding inside a token ref — so `shadow: { token: \"$item.x\" }` and `backgroundGradient: { token: \"$item.x\" }` keep the sentinel and silently lose the per-item styling. Colour slots split by LEVEL, not by component: inside modifiers a colour is a TokenRef OBJECT ({ token: \"…\" }), inside props it is a bare TokenName STRING — there is no modifiers.color or modifiers.tint (see the 'Token slots' section in component-reference.md). A few input props render the LITERAL and are NOT binding-aware — notably SearchField.placeholder and Chip.label; SearchField.value is live and binding-aware like other content props. Bindings also CANNOT target strict-enum fields (style, size, alignment, severity, presentation, contentMode, …) — those fail at render, not validation. $env: is client-resolved (no worker) and valid in modifiers.hiddenPath and FragmentSlot.casePath ONLY — keys reduceMotion/colorScheme/sizeClass/isRTL/keyboardVisible, e.g. hiddenPath:\"$env:reduceMotion\" or hiddenPath:\"$env:colorScheme==light\", casePath:\"$env:colorScheme\"; unknown key fails open (shown / defaultCase).",
|
|
5
5
|
"componentCount": 88,
|
|
6
6
|
"notImplementedComponents": [
|
|
7
7
|
"Text",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
**88 core component types.** Every component body is `{ "package": "himalaya", "type": "<Type>", "props": { … } }`.
|
|
7
7
|
Binding grammar: `$service:<service>.<path>` (worker state) · `$item.<field>` (list row template) · `$flag:<key>` (feature flag) · `$env:<key>` (client environment — #1056).
|
|
8
8
|
Bindings resolve in the text/value props marked **binding-aware** below — **including two `Modifiers` token refs**, `background` and `border.color`, which are the only two whose token a binding actually fills: a worker can drive per-item **theming** (e.g. a game board's per-square color, `background: { token: "$item.bg" }`), not just text content. `shadow` and `backgroundGradient` are `TokenRef`s but are **not** binding-aware on any renderer — `shadow: { token: "$item.x" }` keeps the sentinel as a literal token name and silently loses the per-item styling, and a gradient is driven by its own `backgroundGradientPath` prop resolving to a gradient token NAME, not by a binding inside a token ref.
|
|
9
|
-
`$env:` is **client-resolved** (no worker) and valid in `modifiers.hiddenPath` and `FragmentSlot.casePath` **only** — keys `reduceMotion` / `colorScheme` / `sizeClass` / `isRTL
|
|
9
|
+
`$env:` is **client-resolved** (no worker) and valid in `modifiers.hiddenPath` and `FragmentSlot.casePath` **only** — keys `reduceMotion` / `colorScheme` / `sizeClass` / `isRTL` / `keyboardVisible` (an on-screen keyboard is up — `hiddenPath: "$env:keyboardVisible"` hides a header while typing), with an optional `==`/`!=` comparison (e.g. `hiddenPath: "$env:reduceMotion"`, `hiddenPath: "$env:colorScheme==light"`, `casePath: "$env:colorScheme"`). An unknown key fails open (shown / `defaultCase`).
|
|
10
10
|
A few input props render the **literal** and are **not** binding-aware — notably `SearchField.placeholder` and `Chip.label`. `SearchField.value` is live/binding-aware like other content props.
|
|
11
11
|
They **cannot target strict-enum fields** (`style`, `size`, `alignment`, `severity`, `presentation`, `contentMode`, …) — those fail at render time, not validation.
|
|
12
12
|
|
|
@@ -166,7 +166,9 @@ worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to t
|
|
|
166
166
|
lists its screens (size, workers, `onAppear`), `--show <id>` prints one entry, and
|
|
167
167
|
`--write <id> --from <file>` puts an edited entry back (refused if it breaks the file), and
|
|
168
168
|
`--add <id> --from <file>` appends a NEW screen and registers it as linkable — the authoring half
|
|
169
|
-
of mining a template.
|
|
169
|
+
of mining a template. To add a screen, clone the closest one: `--show <closest>`, change the id
|
|
170
|
+
and content, then `--add <new> --from <file> --display-name "<New title>"` (view names must be unique). `--from` takes that entry, a ScreenDescriptor
|
|
171
|
+
JSON object, or `{ "<id>": <ScreenDescriptor> }`; never edit `dev/index.ts` by hand. Not sure
|
|
170
172
|
which template fits? `himi template recommend --text "<the business>"` (or `--book <site analyze
|
|
171
173
|
dir>` for a site's verticals) ranks the catalog with reasons. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
|
|
172
174
|
list --remote` shows the full catalog (title/description/tags) — including templates published
|
|
@@ -40,6 +40,17 @@ keeps its existing fields and adds the code:
|
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
## `himi test` authored journey failures
|
|
44
|
+
|
|
45
|
+
| Code | Meaning |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `test_journey_failed` | At least one unsuppressed authored journey failed: its destination is absent, initialization/action failed, or the expected rendered values were missing. The existing `failures`, `journeys`, counts and exit 1 remain. This includes cold-entry journeys with `steps: []`. |
|
|
48
|
+
| `test_input_evidence_requires_json` | `--input-evidence` was requested without explicit `--json`. Exit 2 before build or worker execution. |
|
|
49
|
+
| `test_input_evidence_unsupported_mode` | `--input-evidence` was combined with `--browser`, or the content is a functions package rather than an app worker crawl. Exit 2 before execution. |
|
|
50
|
+
| `test_input_evidence_unavailable` | The isolated input crawl cannot establish all contained realms, cannot read a stable source archive before/after the crawl, or fails to return one complete JSON report (including interruption, process timeout or output limit). Exit 1; no host-realm fallback or positive input acceptance. |
|
|
51
|
+
|
|
52
|
+
This is a scoped migration. Other legacy `himi test` failure paths retain their existing envelopes.
|
|
53
|
+
|
|
43
54
|
## `himi icon from-one-app` codes
|
|
44
55
|
|
|
45
56
|
| Code | Meaning |
|
|
@@ -300,15 +311,17 @@ whole body gzip-encoded because it is over the plain 8 MB cap:
|
|
|
300
311
|
|---|---|
|
|
301
312
|
| `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. |
|
|
302
313
|
|
|
303
|
-
## `himi template screens --upsert` codes
|
|
314
|
+
## `himi template screens --upsert | --add | --write` codes
|
|
304
315
|
|
|
305
|
-
`--upsert` writes many generated screens at once
|
|
306
|
-
on any of them (a failed batch restores every
|
|
316
|
+
`--upsert` writes many generated screens at once; `--add <id>` and `--write <id>` write one. Their
|
|
317
|
+
`--from` refusals carry a code; nothing is written on any of them (a failed batch restores every
|
|
318
|
+
file). Other `template screens` paths, and `--add`/`--write` refusals about the package itself
|
|
319
|
+
(an existing id, a symlink, a result that would not parse), stay legacy.
|
|
307
320
|
|
|
308
321
|
| Code | Meaning |
|
|
309
322
|
|---|---|
|
|
310
|
-
| `invalid_usage` | `--upsert` was given without `--from <file>`. Exit 2. |
|
|
311
|
-
| `invalid_input` | The `--from` file
|
|
323
|
+
| `invalid_usage` | `--upsert`, `--add` or `--write` was given without `--from <file>`. Exit 2. |
|
|
324
|
+
| `invalid_input` | The `--from` file is missing, unreadable or not JSON. For `--add`/`--write` also: it is not a ScreenDescriptor object, `{ "<id>": <ScreenDescriptor> }` or the entry `--show` prints, or its key or `screenId` is not the `<id>` given; a templated route (`{ pattern, display, template }`) takes back only the text `--show` prints for it. `error` names the file. Exit 2. |
|
|
312
325
|
| `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. |
|
|
313
326
|
|
|
314
327
|
## `himi site member-app` codes
|
package/guides/himi-skill.md
CHANGED
|
@@ -189,7 +189,9 @@ worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to t
|
|
|
189
189
|
lists its screens (size, workers, `onAppear`), `--show <id>` prints one entry, and
|
|
190
190
|
`--write <id> --from <file>` puts an edited entry back (refused if it breaks the file), and
|
|
191
191
|
`--add <id> --from <file>` appends a NEW screen and registers it as linkable — the authoring half
|
|
192
|
-
of mining a template.
|
|
192
|
+
of mining a template. To add a screen, clone the closest one: `--show <closest>`, change the id
|
|
193
|
+
and content, then `--add <new> --from <file> --display-name "<New title>"` (view names must be unique). `--from` takes that entry, a ScreenDescriptor
|
|
194
|
+
JSON object, or `{ "<id>": <ScreenDescriptor> }`; never edit `dev/index.ts` by hand. Not sure
|
|
193
195
|
which template fits? `himi template recommend --text "<the business>"` (or `--book <site analyze
|
|
194
196
|
dir>` for a site's verticals) ranks the catalog with reasons. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
|
|
195
197
|
list --remote` shows the full catalog (title/description/tags) — including templates published
|
package/guides/himi-test.md
CHANGED
|
@@ -74,6 +74,80 @@ Each stage runs on hermetic data with zero network:
|
|
|
74
74
|
Engine: `core/dev-server/src/screen-crawl.ts`.
|
|
75
75
|
CLI orchestration: `tools/himi-cli/src/test.ts`.
|
|
76
76
|
|
|
77
|
+
## Machine input evidence
|
|
78
|
+
|
|
79
|
+
`himi test --content <dir> --input-evidence --json` opts into additive
|
|
80
|
+
`inputEvidence.version: 1` output. The local evaluator records actual dispatches; it
|
|
81
|
+
does not interpret a Plan or decide whether a warning is benign. The flag requires
|
|
82
|
+
explicit `--json`; browser-loop and functions-package modes refuse it with typed
|
|
83
|
+
failure codes. An older CLI may omit the field: treat that as capability missing,
|
|
84
|
+
never as a passing scan.
|
|
85
|
+
|
|
86
|
+
The CLI runs this opt-in crawl in a separate process, enables the existing compiler
|
|
87
|
+
import confinement, and establishes QuickJS/WASM realms for generated dev modules,
|
|
88
|
+
config, worker boot and worker actions before loading generated modules. Explicit
|
|
89
|
+
host-realm overrides or unavailable isolation refuse with
|
|
90
|
+
`test_input_evidence_unavailable`; there is no host fallback. Packages containing
|
|
91
|
+
symlinks are refused. The compiler accepts package-owned files and CLI-owned
|
|
92
|
+
source/staged SDK roots; it does not expand arbitrary app or ancestor dependency
|
|
93
|
+
links into trusted roots. A separate editor SDK install cannot substitute for the
|
|
94
|
+
pinned CLI's runtime in this mode. The trusted caller must keep the generated
|
|
95
|
+
source and its environment quiescent during capture and bind the final subject. The local
|
|
96
|
+
receipt CLI projects only the caller-owned public `WIX_CLIENT_ID` and `WIX_SITE_URL`
|
|
97
|
+
into contained config/dev compilation, preserving their supplied values. Other
|
|
98
|
+
host environment keys (including secrets) are omitted; tokenless/default contained
|
|
99
|
+
callers still receive an empty environment. Capture those public bindings with
|
|
100
|
+
the run environment; the CLI does not choose an identity or provision a site.
|
|
101
|
+
Packages requiring host filesystem/network work during config or descriptor construction must report
|
|
102
|
+
that limitation instead of issuing contained evidence. The isolated crawl has a
|
|
103
|
+
three-minute process limit; interruption forwards SIGTERM/SIGINT to its child.
|
|
104
|
+
|
|
105
|
+
The receipt has these fields:
|
|
106
|
+
|
|
107
|
+
| Field | Evidence |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `subject` | Actual app, tested release ID and payload hash, test run ID, source diff/untracked hashes, and SHA256 of the exact `himi-test.json` bytes parsed for the run (`absent` when missing). `sourceArchiveHash` is SHA256 of the base64 deterministic ZIP from the publish source collector, including authored `himi-test.json`. It is captured before loading generated modules and rechecked after the crawl; unreadable or changed source refuses with `test_input_evidence_unavailable`. Generated `.himi` outputs are excluded by the shared archive rules. Git hashes may explicitly be `unavailable`. |
|
|
110
|
+
| `execution` | CLI-established `devModule`, `appConfig`, `workerBoot` and `workerAction` modes, each `quickjs-wasm`. A v1 report missing these fields predates the contained CLI path; do not accept it as contained execution. This marker is meaningful only when captured from the trusted pinned producer. |
|
|
111
|
+
| `probes` | Each synthesized typed dispatch's screen/component/action/hook, contract control and injected field/value, actual argument list, precondition input/current value/state hash, post-state hash, and observed state/navigation/request/host-call/throw results. Zero probes is not proof that required controls ran. |
|
|
112
|
+
| `journeys` | Actual screen/input, each attempted step's scope, raw `declaredPayload`, effective row-resolved `descriptorPayload`, exact `testOverrides`, final merged `payload`, pre/post state hashes, executed/threw/not-dispatched status, final visible observations and evaluator-computed assertions. Unavailable actions remain not-dispatched, never successful steps. |
|
|
113
|
+
| `unavailableScreens`, `unavailableJourneys` | Requested coverage for which the evaluator did not supply v1 evidence, including older engines, invalid/collapsed screens and unavailable journeys. With positional screen IDs, these lists contain only requested roots; requested unknown IDs remain unavailable. Unrelated configured journeys do not run or fail a scoped run. These gaps are not accepted behavior. |
|
|
114
|
+
|
|
115
|
+
State hashes fingerprint the exact serialized worker snapshot captured at the dispatch
|
|
116
|
+
boundary. `currentValuePresent` distinguishes an absent current value from null.
|
|
117
|
+
Input records include the implicit screen ID, then baked descriptor input and route
|
|
118
|
+
overrides with the same precedence the worker receives.
|
|
119
|
+
Requests and host calls are attempted hermetic effects, not live-service success.
|
|
120
|
+
Journey assertion kind `observation-substring` states the existing operator exactly:
|
|
121
|
+
substring inclusion in joined visible `nodeId.field=value` lines. It does not prove
|
|
122
|
+
exact text equality, complete result sets, absence of extra rows, or that an
|
|
123
|
+
agent-authored expected value is an independently approved oracle.
|
|
124
|
+
|
|
125
|
+
Journey parameters are synthetic overrides. A journey that replaces a descriptor's
|
|
126
|
+
literal service-reference ID with a real ID tests that synthetic dispatch, not the
|
|
127
|
+
actual UI tap. Inspect `testOverrides` alongside `descriptorPayload`; a passing
|
|
128
|
+
assertion cannot waive a failing descriptor-driven flow. Worker (`tier5.*`) actions
|
|
129
|
+
keep authored params/args literal by the existing action contract; reducers read
|
|
130
|
+
their selected record from state/input rather than expecting nested service binding
|
|
131
|
+
evaluation. This evidence does not change that contract.
|
|
132
|
+
The origin fields are always present: unavailable declared/effective payloads and
|
|
133
|
+
absent test parameters are explicit `null`; an explicitly empty override is `{}`.
|
|
134
|
+
An omitted origin field is unknown, not evidence that no override occurred.
|
|
135
|
+
|
|
136
|
+
This output leaves `ok`, finding severities and suppressions unchanged. In particular,
|
|
137
|
+
`typed-value-ignored` remains a warning and can coexist with `ok: true`. Use the
|
|
138
|
+
structured finding invariant/screen/trigger/field/axis to apply a separate trusted
|
|
139
|
+
acceptance policy; do not parse message prose or reject every warning. Finding IDs
|
|
140
|
+
are stable grouping identities, not unique case/dispatch IDs. Inspect suppressed,
|
|
141
|
+
skipped and absent coverage explicitly. A different successful input cannot excuse
|
|
142
|
+
an untested or failed required input.
|
|
143
|
+
|
|
144
|
+
Attestation v2 still binds only aggregate report/coverage values alongside its
|
|
145
|
+
existing source/release/descriptor/screenshot hashes; it does not acquire new
|
|
146
|
+
acceptance semantics. A trusted host must capture the actual pinned CLI process
|
|
147
|
+
exit/stdout, bind the full report to its own run and immutable final source/release,
|
|
148
|
+
and supply independent required-control/case policy. Neither a negative-clear scan
|
|
149
|
+
nor an unkeyed receipt is whole-product acceptance or trusted agent prose.
|
|
150
|
+
|
|
77
151
|
## Typed form journeys
|
|
78
152
|
|
|
79
153
|
The crawl types into a control **only when it can read a legal value off the app itself** — an id
|
|
@@ -115,6 +189,19 @@ edge would hand it — so the journey boots the way a real tap into it would. Ke
|
|
|
115
189
|
navigation in the ordinary edge pass, which boots the destination with real source
|
|
116
190
|
parameters. Pair dynamic journeys with app-specific `dev/net-mocks.json` fixtures.
|
|
117
191
|
|
|
192
|
+
Journeys run only the initialization the bundle and descriptor actually declare: module-scope
|
|
193
|
+
calls and the root `onAppear`. Unlike the discovery crawl's convenience `load()` sweep, they do
|
|
194
|
+
not call an otherwise uncalled loader. A declaration of `actions.load` alone does not initialize
|
|
195
|
+
a cold screen on device.
|
|
196
|
+
|
|
197
|
+
Use `"steps": []` to assert the result of entering a detail screen, without adding a synthetic
|
|
198
|
+
load tap that could conceal missing initialization. Add separate journeys with different `input`
|
|
199
|
+
IDs and requirement-derived `expectText` (title, notes, metadata and numeric values). Include
|
|
200
|
+
missing/stale IDs and a not-found expectation. These are independent cold entries, not proof of
|
|
201
|
+
back/return, state carry-over or the source row's emitted navigation params; verify those with
|
|
202
|
+
real interactions as well. A green derived crawl without such expectations does not establish
|
|
203
|
+
that a detail shows the selected record.
|
|
204
|
+
|
|
118
205
|
## What it checks
|
|
119
206
|
|
|
120
207
|
Only **unambiguous** defects, so a green run stays worth trusting:
|
|
@@ -483,6 +570,7 @@ behavior. This instruction does not add an automatic suppression or retry gate t
|
|
|
483
570
|
| `--offline` | Strip every data layer — see the failure UI head-on. (Excludes `--net`.) |
|
|
484
571
|
| `--no-hostile` | Skip the empty-list / all-500 passes. |
|
|
485
572
|
| `--no-shots` | Skip the per-screen PNGs written to `.himi/shots/`. |
|
|
573
|
+
| `--input-evidence` | Requires `--json`; capture actual typed probes and authored-journey receipts in contained local worker mode (see the receipt contract above). |
|
|
486
574
|
| `--net <file>` | Use an explicit net-mocks file instead of the app's own. |
|
|
487
575
|
| `--budget <ms>` | Per-action settle budget (default 2000). |
|
|
488
576
|
| `--native` | Also decode every baked descriptor, and the release's `tokens.json`, with the real native decoders; bare `--native` runs whichever toolchains are present and names each one it could NOT check in a top-level `skippedPlatforms: [{ platform, reason }]` (never a pass), while `--native ios` or `--native android` selects one and exits 2 when it is unavailable. The Android SDK is found from `ANDROID_HOME`, then `ANDROID_SDK_ROOT`, then `sdk.dir` in the source checkout's Android test harness `local.properties`, then the default install path (`~/Library/Android/sdk` on macOS, `~/Android/Sdk` on Linux). **Source-checkout only** — the decoder oracles are that tree's own HimalayaCore Swift package and Gradle harness, which a published install does not ship, so it cannot run this at all (and its coverage ledger stays silent about native decoding rather than suggesting it). |
|
|
@@ -25,8 +25,8 @@ Source snapshots: Catalyst `617c06d43d2a5608b6876d724553269b1e5346f0`; Himalaya
|
|
|
25
25
|
| Module | 13 |
|
|
26
26
|
| Primitive | 7 |
|
|
27
27
|
| Composition | 9 |
|
|
28
|
-
| Partial |
|
|
29
|
-
| Empty |
|
|
28
|
+
| Partial | 25 |
|
|
29
|
+
| Empty | 23 |
|
|
30
30
|
| Total | 77 |
|
|
31
31
|
|
|
32
32
|
## Mappings by server group
|
|
@@ -127,7 +127,7 @@ carousel placement remain separate gaps; the mappings above stay Partial.
|
|
|
127
127
|
|
|
128
128
|
| Source sectionDefinitionId | Source viewer component | Himalaya implementation | Placement / reuse | Class | Settings, actions and gaps |
|
|
129
129
|
| --- | --- | --- | --- | --- | --- |
|
|
130
|
-
| `loyalty_status_section` | `loyalty.components.StatusSection` |
|
|
130
|
+
| `loyalty_status_section` | `loyalty.components.StatusSection` | [himi_loyalty member card](https://github.com/wix-private/himalaya/blob/adf4c8b47fd237307b278a5dbb8198a94b7feb69/apps/himi-branded-lite/dev/index.ts) (`buildLoyalty` points card) + [loyalty worker](https://github.com/wix-private/himalaya/blob/adf4c8b47fd237307b278a5dbb8198a94b7feb69/core/dev-server/tier5-bundles-src/himi-branded-lite-loyalty/index.ts "audience-lint-ok: optional pinned upstream evidence, not a local authoring dependency") | Existing base; adaptation needed | Partial | The member card on `himi_loyalty` reads the signed-in member's balance (`getCurrentMemberLoyaltyAccount`) and, beside it, the tiers program and the claimed coupons; it shows the balance, the tier name, the points-expiry line and `Rewards available (N)` (rewards the balance covers on the member's tier + ACTIVE claimed coupons), each blank when its read fails. Visitors get the sign-in gate. Not carried over: the referral-coupon share of the count (no referral spec in codegen) and the one-app `View` link (the catalog it opens is on the same screen). |
|
|
131
131
|
| `loyalty_offering_section` | `loyalty.components.OfferingSection` | [himi_loyalty](https://github.com/wix-private/himalaya/blob/97f1542f2f7d6e8cb7a902b912c87dcaa37e5a67/apps/himi-branded-lite/dev/index.ts) + [rewards worker](https://github.com/wix-private/himalaya/blob/97f1542f2f7d6e8cb7a902b912c87dcaa37e5a67/core/dev-server/tier5-bundles-src/himi-branded-lite-loyalty/index.ts "audience-lint-ok: optional pinned upstream evidence, not a local authoring dependency") | Existing base; adaptation needed | Partial | Reuse program/reward reads and required-points presentation. Preserve offering selection. Member earning/redemption operations are not established by this read-only rewards list. |
|
|
132
132
|
|
|
133
133
|
### 8. referrals-section-definitions.json
|
|
@@ -355,7 +355,6 @@ These are the exact definitions whose target cells above remain empty. “Not fo
|
|
|
355
355
|
| `events-section-definitions.json` | `events_discussion_section` | No event-scoped discussion implementation was found. Groups/social feed and Blog comments must not be selected automatically. |
|
|
356
356
|
| `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. |
|
|
357
357
|
| `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. |
|
|
358
|
-
| `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. |
|
|
359
358
|
| `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. |
|
|
360
359
|
| `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. |
|
|
361
360
|
| `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. |
|
package/guides/qa-builds.md
CHANGED
|
@@ -636,6 +636,14 @@ gradle read the tree. The shell supplies the project; it no longer supplies the
|
|
|
636
636
|
| `apps/<app>/ios/project.yml`, then its `build.gradle.kts` | the content app has source in this checkout — it already declared a version, so nothing new has to be declared |
|
|
637
637
|
| the SHELL's own declared version | an off-repo app that declares nothing anywhere. Deliberate: `CFBundleShortVersionString` is **also** the OTA rollout floor (`HimalayaApp.swift` passes it as `binaryVersion`; serve-control's `resolveRelease` skips any ring whose `minBinaryVersion` it fails, and returns nothing when none qualifies), and the shell's version is the truth about what the binary can render |
|
|
638
638
|
|
|
639
|
+
Branded Lite keeps the two apart, so the store version never moves its rollout floor. Its Android
|
|
640
|
+
app reports `BuildConfig.SHELL_VERSION` as the OTA `binaryVersion`, and its iOS app the Info.plist
|
|
641
|
+
key `HimalayaShellVersion`. Each is pinned equal to the shell's committed version. So a
|
|
642
|
+
Containers app's App Store build, which stamps the owner's version into `CFBundleShortVersionString`
|
|
643
|
+
through `APP_VERSION`, still reports the shell's version to update checks, and the build log says so.
|
|
644
|
+
A shell commit older than the key reports `CFBundleShortVersionString`, as before. Force update
|
|
645
|
+
(`minVersion`) compares the store version on both.
|
|
646
|
+
|
|
639
647
|
The **build number** (`CFBundleVersion` / `versionCode`) is seconds since 2020-01-01Z, so it is
|
|
640
648
|
monotonic and unique per app per second — App Store Connect rejects a duplicate version/build pair,
|
|
641
649
|
and Play requires `versionCode` to increase.
|
package/guides/release-notes.md
CHANGED
|
@@ -14,6 +14,18 @@ areas, always in the same order, so you can skim for the one you care about.
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## 2026-11-08
|
|
18
|
+
|
|
19
|
+
### Getting started & the CLI
|
|
20
|
+
|
|
21
|
+
- **`himi template screens --add` and `--write` now take the screen as JSON.** `--from` accepts a
|
|
22
|
+
plain ScreenDescriptor (the id comes from `--add <id>` / `--write <id>`), `{ "<id>": <ScreenDescriptor> }`,
|
|
23
|
+
or the entry `--show` prints. Before, only that `--show` entry worked, and a JSON file failed with an
|
|
24
|
+
error pointing at a line of `dev/index.ts`. A file that is not one of these shapes, has a different
|
|
25
|
+
id or cannot be read is refused with `code: "invalid_input"`, and the error names your file. To add
|
|
26
|
+
a screen, clone the closest one: `--show <closest>`, edit it, then
|
|
27
|
+
`--add <new> --from <file> --display-name "<title>"`. (#5422)
|
|
28
|
+
|
|
17
29
|
## 2026-11-04
|
|
18
30
|
|
|
19
31
|
### Branded Lite & Owner Lite
|
|
@@ -23425,19 +23425,43 @@
|
|
|
23425
23425
|
"name": "WixLoyaltyRewardConfig",
|
|
23426
23426
|
"kind": "interface",
|
|
23427
23427
|
"jsdoc": "",
|
|
23428
|
-
"decl": "export interface WixLoyaltyRewardConfig {\n tierId?: string;\n costInPoints?: number;\n amount?: number;\n percentage?: number;\n}"
|
|
23428
|
+
"decl": "export interface WixLoyaltyRewardConfig {\n tierId?: string;\n costInPoints?: number;\n amount?: string | number;\n percentage?: number;\n}"
|
|
23429
|
+
},
|
|
23430
|
+
{
|
|
23431
|
+
"name": "WixLoyaltyTierConfig",
|
|
23432
|
+
"kind": "type",
|
|
23433
|
+
"jsdoc": "/** One reward price row: what the reward costs a member on tier `tierId` (absent = the base tier). */",
|
|
23434
|
+
"decl": "export type WixLoyaltyTierConfig = WixLoyaltyRewardConfig;"
|
|
23429
23435
|
},
|
|
23430
23436
|
{
|
|
23431
23437
|
"name": "WixLoyaltyProgram",
|
|
23432
23438
|
"kind": "interface",
|
|
23433
23439
|
"jsdoc": "",
|
|
23434
|
-
"decl": "export interface WixLoyaltyProgram {\n id?: string;\n name?: string;\n /** ACTIVE | DRAFT | PAUSED */\n status?: string;\n pointDefinition?: { customName?: string; icon?: WixLoyaltyImage };\n}"
|
|
23440
|
+
"decl": "export interface WixLoyaltyProgram {\n id?: string;\n name?: string;\n /** ACTIVE | DRAFT | PAUSED */\n status?: string;\n pointDefinition?: { customName?: string; icon?: WixLoyaltyImage };\n /** Which premium loyalty features the site's plan unlocks. Absent on older responses. */\n premiumFeatures?: { loyaltyProgram?: boolean; tiers?: boolean; pointsExpiration?: boolean };\n /** The program-level expiry POLICY (not a member's date — that is `WixLoyaltyAccount.pointsExpiration`). */\n pointsExpiration?: { status?: string; monthsOfInactivity?: number; expiringPointsPercentage?: number };\n}"
|
|
23435
23441
|
},
|
|
23436
23442
|
{
|
|
23437
23443
|
"name": "WixLoyaltyReward",
|
|
23438
23444
|
"kind": "interface",
|
|
23439
23445
|
"jsdoc": "",
|
|
23440
|
-
"decl": "export interface WixLoyaltyReward {\n id: string;\n name?: string;\n type?: string;\n /** Legacy adapter field. REST costs live in the reward's tier configurations. */\n requiredPoints?: number;\n discountAmount?: { configsByTier?:
|
|
23446
|
+
"decl": "export interface WixLoyaltyReward {\n id: string;\n name?: string;\n /** DISCOUNT_AMOUNT | COUPON_REWARD | COUPON | SPI_DISCOUNT_AMOUNT | … */\n type?: string;\n /** Legacy adapter field. REST costs live in the reward's tier configurations. */\n requiredPoints?: number;\n discountAmount?: { configsByTier?: WixLoyaltyRewardConfig[] };\n couponReward?: {\n fixedAmount?: { configsByTier?: WixLoyaltyRewardConfig[] };\n percentage?: { configsByTier?: WixLoyaltyRewardConfig[] };\n freeShipping?: { configsByTier?: WixLoyaltyRewardConfig[] };\n };\n active?: boolean;\n}"
|
|
23447
|
+
},
|
|
23448
|
+
{
|
|
23449
|
+
"name": "WixLoyaltyAccount",
|
|
23450
|
+
"kind": "interface",
|
|
23451
|
+
"jsdoc": "/** The signed-in member's loyalty account (wix.loyalty.v1.account, `/my-account`). */",
|
|
23452
|
+
"decl": "export interface WixLoyaltyAccount {\n id?: string;\n points?: { balance?: number; earned?: number; adjusted?: number; redeemed?: number; expired?: number };\n /** The member's current tier; absent or without `id` while on the base tier. */\n tier?: { id?: string; points?: number };\n /** The member's own expiry — when and how many points lapse next. */\n pointsExpiration?: { expirationDate?: string; expiringPointsAmount?: number };\n rewardAvailable?: boolean;\n}"
|
|
23453
|
+
},
|
|
23454
|
+
{
|
|
23455
|
+
"name": "WixLoyaltyTiersProgram",
|
|
23456
|
+
"kind": "interface",
|
|
23457
|
+
"jsdoc": "/** The tiers program: settings (ACTIVE | PAUSED, the base tier's definition) plus the custom tiers. */",
|
|
23458
|
+
"decl": "export interface WixLoyaltyTiersProgram {\n tiers?: WixLoyaltyTier[];\n programSettings?: {\n status?: string;\n baseTierDefinition?: { name?: string; description?: string; icon?: unknown };\n rollingWindow?: { durationInMonths?: number };\n };\n}"
|
|
23459
|
+
},
|
|
23460
|
+
{
|
|
23461
|
+
"name": "WixLoyaltyCoupon",
|
|
23462
|
+
"kind": "interface",
|
|
23463
|
+
"jsdoc": "/** A reward the member already claimed as a coupon (wix.loyalty.v1.coupon). */",
|
|
23464
|
+
"decl": "export interface WixLoyaltyCoupon {\n id?: string;\n rewardName?: string;\n /** ACTIVE | APPLIED | DELETED */\n status?: string;\n couponReference?: { couponId?: string; code?: string; deleted?: boolean };\n}"
|
|
23441
23465
|
},
|
|
23442
23466
|
{
|
|
23443
23467
|
"name": "WixLoyaltyTier",
|
|
@@ -23480,6 +23504,22 @@
|
|
|
23480
23504
|
"http": "GET /_api/loyalty-earning-rules/v1/earning-rules/rules",
|
|
23481
23505
|
"scope": "visitor"
|
|
23482
23506
|
},
|
|
23507
|
+
{
|
|
23508
|
+
"name": "getLoyaltyTiersProgram",
|
|
23509
|
+
"kind": "function",
|
|
23510
|
+
"jsdoc": "/** GET /loyalty-tiers/v1/tiers/program → { tiers, programSettings }. The custom tiers plus the\n * settings that carry the BASE tier's name — the one a member with no `tier.id` is on.\n *\n * @scope visitor — readable without signing in. */",
|
|
23511
|
+
"decl": "export function getLoyaltyTiersProgram(readOptions?: WixCallOptions): Promise<WixLoyaltyTiersProgram>;",
|
|
23512
|
+
"http": "GET /loyalty-tiers/v1/tiers/program",
|
|
23513
|
+
"scope": "visitor"
|
|
23514
|
+
},
|
|
23515
|
+
{
|
|
23516
|
+
"name": "listMyLoyaltyCoupons",
|
|
23517
|
+
"kind": "function",
|
|
23518
|
+
"jsdoc": "/** GET /loyalty-coupons/v1/coupons/my-coupons → { loyaltyCoupons }. The member's claimed\n * rewards; an ACTIVE, undeleted one still counts as a reward available to use.\n *\n * @scope member — requires a signed-in site member. */",
|
|
23519
|
+
"decl": "export function listMyLoyaltyCoupons(readOptions?: WixCallOptions): Promise<{ loyaltyCoupons?: WixLoyaltyCoupon[] }>;",
|
|
23520
|
+
"http": "GET /loyalty-coupons/v1/coupons/my-coupons",
|
|
23521
|
+
"scope": "member"
|
|
23522
|
+
},
|
|
23483
23523
|
{
|
|
23484
23524
|
"name": "LoyaltyOverview",
|
|
23485
23525
|
"kind": "interface",
|
|
@@ -23514,7 +23554,7 @@
|
|
|
23514
23554
|
"name": "getCurrentMemberLoyaltyAccount",
|
|
23515
23555
|
"kind": "function",
|
|
23516
23556
|
"jsdoc": "/** Read the authenticated member's own Loyalty account; no customer ID is accepted.\n * Source contract: loyalty-v1.getCurrentMemberAccount. Anonymous visitors are refused.\n * A signed-in customer recording is still required for migration acceptance.\n * @scope member\n */",
|
|
23517
|
-
"decl": "export function getCurrentMemberLoyaltyAccount(readOptions?: WixCallOptions): Promise<{ account?:
|
|
23557
|
+
"decl": "export function getCurrentMemberLoyaltyAccount(readOptions?: WixCallOptions): Promise<{ account?: WixLoyaltyAccount }>;",
|
|
23518
23558
|
"http": "GET /loyalty-accounts/v1/accounts/my-account",
|
|
23519
23559
|
"scope": "member"
|
|
23520
23560
|
}
|
package/guides/templates.md
CHANGED
|
@@ -117,6 +117,8 @@ himi template screens # list the screens and their worker
|
|
|
117
117
|
himi template screens --show himi_saved > saved.txt # ONE screen's entry
|
|
118
118
|
himi template screens --write himi_saved --from saved.txt # put it back — re-parsed, refused if it breaks the file
|
|
119
119
|
himi template screens --add himi_sellers --from sellers.txt --parent himi_branded_home # a NEW screen, registered as linkable
|
|
120
|
+
# --add/--write --from takes a ScreenDescriptor JSON object, { "<id>": <ScreenDescriptor> }, or the --show entry.
|
|
121
|
+
# To add a screen, clone the closest one: --show it, change the id and content, then --add it.
|
|
120
122
|
himi template sweep --set "Sample content=>Our latest" --dry-run # copy inside string literals only
|
|
121
123
|
himi template trim --from-spec --links drop --dry-run # keep SPEC.md's Screen catalogue ids — read fromSpec.droppedPrimary first
|
|
122
124
|
```
|
|
@@ -779,6 +779,20 @@ This works identically on web, iOS, and Android. (Regression-guarded by
|
|
|
779
779
|
detail bundles for BOTH the primary `input.id` param and the back-compat
|
|
780
780
|
`input.screenId` recovery.)
|
|
781
781
|
|
|
782
|
+
**Prove the selected record, not just a successful build.** Each screen has its own worker,
|
|
783
|
+
even when several screens name the same bundle and service. Setting a selected id in the list's
|
|
784
|
+
service does not set the detail's state. Trace the tapped row id → navigation params →
|
|
785
|
+
`himalaya.input` → the detail's initialized state. Route input is not automatically a loader
|
|
786
|
+
argument, and `$input.id` is not a descriptor binding; read input in the worker as above.
|
|
787
|
+
|
|
788
|
+
Add cold-entry journeys (`steps: []`) in `himi-test.json` for at least two distinct records per
|
|
789
|
+
detail, asserting their requirement-derived title, metadata, notes and numeric values. Also
|
|
790
|
+
assert missing/stale ids. See [cold-entry assertions](himi-test.md#typed-form-journeys). Do not
|
|
791
|
+
manually dispatch `load` in these checks: that can make an uninitialized generated screen look
|
|
792
|
+
correct. Then drive the actual row taps and back/return on each required surface, confirming
|
|
793
|
+
that the second selection and a missing id show none of the first record's fields. Keep the
|
|
794
|
+
source-nav and return evidence separate from the screen-local cold-entry assertions.
|
|
795
|
+
|
|
782
796
|
**Alternative — a templated route that bakes a static `input`.** When the id space is
|
|
783
797
|
enumerable, a `screens.templated` route can bake the id straight into the descriptor's
|
|
784
798
|
**static `input`** instead of relying on a nav param — the `property_<id>` pattern. The
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1612.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": "ce8fd7a735a426892fa25365dcc0599b1c137be31ce634d59245ef55"
|
|
76
76
|
}
|
|
@@ -45241,6 +45241,90 @@ const SCREENS = runtimeSetting({
|
|
|
45241
45241
|
}
|
|
45242
45242
|
}
|
|
45243
45243
|
},
|
|
45244
|
+
{
|
|
45245
|
+
"id": "himi_loyalty.member_tier",
|
|
45246
|
+
"body": {
|
|
45247
|
+
"package": "himalaya",
|
|
45248
|
+
"type": "Text",
|
|
45249
|
+
"props": {
|
|
45250
|
+
"text": "$service:himiBrandedLoyalty.memberTier",
|
|
45251
|
+
"style": "footnote",
|
|
45252
|
+
"color": "textSecondary",
|
|
45253
|
+
"maxLines": 1,
|
|
45254
|
+
"collapseWhenEmpty": true
|
|
45255
|
+
}
|
|
45256
|
+
}
|
|
45257
|
+
},
|
|
45258
|
+
{
|
|
45259
|
+
"id": "himi_loyalty.member_expiry",
|
|
45260
|
+
"body": {
|
|
45261
|
+
"package": "himalaya",
|
|
45262
|
+
"type": "Text",
|
|
45263
|
+
"props": {
|
|
45264
|
+
"text": "$service:himiBrandedLoyalty.memberExpiry",
|
|
45265
|
+
"style": "caption",
|
|
45266
|
+
"color": "textSecondary",
|
|
45267
|
+
"collapseWhenEmpty": true
|
|
45268
|
+
}
|
|
45269
|
+
}
|
|
45270
|
+
},
|
|
45271
|
+
{
|
|
45272
|
+
"id": "himi_loyalty.member_expiry_urgent",
|
|
45273
|
+
"body": {
|
|
45274
|
+
"package": "himalaya",
|
|
45275
|
+
"type": "Text",
|
|
45276
|
+
"props": {
|
|
45277
|
+
"text": "$service:himiBrandedLoyalty.memberExpiryUrgent",
|
|
45278
|
+
"style": "caption",
|
|
45279
|
+
"color": "danger",
|
|
45280
|
+
"collapseWhenEmpty": true
|
|
45281
|
+
}
|
|
45282
|
+
}
|
|
45283
|
+
},
|
|
45284
|
+
{
|
|
45285
|
+
"id": "himi_loyalty.member_rewards",
|
|
45286
|
+
"body": {
|
|
45287
|
+
"package": "himalaya",
|
|
45288
|
+
"type": "HStack",
|
|
45289
|
+
"props": {
|
|
45290
|
+
"spacing": 4,
|
|
45291
|
+
"alignment": "center",
|
|
45292
|
+
"children": [
|
|
45293
|
+
{
|
|
45294
|
+
"id": "",
|
|
45295
|
+
"body": {
|
|
45296
|
+
"package": "himalaya",
|
|
45297
|
+
"type": "Icon",
|
|
45298
|
+
"props": {
|
|
45299
|
+
"name": "gift",
|
|
45300
|
+
"size": 14,
|
|
45301
|
+
"tint": "success"
|
|
45302
|
+
}
|
|
45303
|
+
},
|
|
45304
|
+
"modifiers": {
|
|
45305
|
+
"accessibilityRole": "decorative"
|
|
45306
|
+
}
|
|
45307
|
+
},
|
|
45308
|
+
{
|
|
45309
|
+
"id": "",
|
|
45310
|
+
"body": {
|
|
45311
|
+
"package": "himalaya",
|
|
45312
|
+
"type": "Text",
|
|
45313
|
+
"props": {
|
|
45314
|
+
"text": "$service:himiBrandedLoyalty.memberRewards",
|
|
45315
|
+
"style": "footnote",
|
|
45316
|
+
"color": "success",
|
|
45317
|
+
"maxLines": 1
|
|
45318
|
+
}
|
|
45319
|
+
}
|
|
45320
|
+
}
|
|
45321
|
+
]
|
|
45322
|
+
}
|
|
45323
|
+
},
|
|
45324
|
+
"modifiers": {
|
|
45325
|
+
"hiddenPath": "$service:himiBrandedLoyalty.hideMemberRewards"
|
|
45326
|
+
}
|
|
45327
|
+
},
|
|
45244
45328
|
{
|
|
45245
45329
|
"id": "",
|
|
45246
45330
|
"body": {
|