@wix/himalaya-cli 0.1142.0 → 0.1144.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 (47) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/delivery-contract.d.ts +104 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/delivery.d.ts +33 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/sdk/iap/src/index.d.ts +5 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap.mjs +509 -3
  5. package/dist/cli.mjs +31387 -30592
  6. package/dist/preview-assets.generated.bin +0 -0
  7. package/guides/getting-started.md +29 -11
  8. package/guides/himi-skill.md +29 -11
  9. package/guides/sdk-reference.json +132 -0
  10. package/guides/templates.md +33 -0
  11. package/guides/trim-a-template.md +13 -0
  12. package/package.json +2 -2
  13. package/templates/beauty-salon/dev/index.ts +2 -0
  14. package/templates/beauty-salon/tier5-src/himi-booking-time/index.ts +2 -2
  15. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +7 -1
  16. package/templates/branded-lite/dev/index.ts +3 -1
  17. package/templates/branded-lite/tier5-src/himi-booking-time/index.ts +2 -2
  18. package/templates/branded-lite/tier5-src/himi-events-rsvp/index.ts +2 -2
  19. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +7 -1
  20. package/templates/community-club/dev/index.ts +2 -1
  21. package/templates/community-club/tier5-src/himi-events-rsvp/index.ts +2 -2
  22. package/templates/community-club/tier5-src/himi-store-account/index.ts +7 -1
  23. package/templates/events-meetups/dev/index.ts +2 -1
  24. package/templates/events-meetups/tier5-src/himi-events-rsvp/index.ts +2 -2
  25. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +7 -1
  26. package/templates/gym-fitness/dev/index.ts +3 -1
  27. package/templates/gym-fitness/tier5-src/himi-booking-time/index.ts +2 -2
  28. package/templates/gym-fitness/tier5-src/himi-events-rsvp/index.ts +2 -2
  29. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +7 -1
  30. package/templates/nonprofit-cause/dev/index.ts +2 -1
  31. package/templates/nonprofit-cause/tier5-src/himi-events-rsvp/index.ts +2 -2
  32. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +7 -1
  33. package/templates/podcast-creator/dev/index.ts +2 -1
  34. package/templates/podcast-creator/tier5-src/himi-events-rsvp/index.ts +2 -2
  35. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +7 -1
  36. package/templates/portfolio-creative/dev/index.ts +2 -1
  37. package/templates/portfolio-creative/tier5-src/himi-events-rsvp/index.ts +2 -2
  38. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +7 -1
  39. package/templates/restaurant/dev/index.ts +2 -1
  40. package/templates/restaurant/tier5-src/himi-events-rsvp/index.ts +2 -2
  41. package/templates/restaurant/tier5-src/himi-store-account/index.ts +7 -1
  42. package/templates/retail-store/dev/index.ts +1 -0
  43. package/templates/retail-store/tier5-src/himi-store-account/index.ts +7 -1
  44. package/templates/yoga-studio/dev/index.ts +3 -1
  45. package/templates/yoga-studio/tier5-src/himi-booking-time/index.ts +2 -2
  46. package/templates/yoga-studio/tier5-src/himi-events-rsvp/index.ts +2 -2
  47. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +7 -1
Binary file
@@ -75,7 +75,11 @@ or `--template owner-lite` (owner CRUD/management: lists, details, forms). That
75
75
  worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to the approved
76
76
  `SPEC.md` when most of its screens fit, or **mine** it when few do — keep the
77
77
  `tier5-src/_shared/` helpers and Wix worker chains and write your own screens. Do not delete
78
- `tier5-src/` + `dev/` and start from blank. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
78
+ `tier5-src/` + `dev/` and start from blank. Never read that file whole: `himi template screens`
79
+ lists its screens (size, workers, `onAppear`), `--show <id>` prints one entry, and
80
+ `--write <id> --from <file>` puts an edited entry back (refused if it breaks the file). Not sure
81
+ which template fits? `himi template recommend --text "<the business>"` (or `--book <site analyze
82
+ 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
79
83
  list --remote` shows the full catalog (title/description/tags) — including templates published
80
84
  to Wix — and `--remote` on `himi init` fetches one from there (the bundled copy is
81
85
  the offline fallback). Publish any app of your own as a template with `himi template publish` —
@@ -102,7 +106,12 @@ done. Print one yourself with `himi template prompt <name>`.
102
106
  **Consent preflight before a template preview.** `branded-lite` and `owner-lite` are Wix-integrated
103
107
  full apps; their workers can request Wix data when they boot. Do not start their worker or web
104
108
  host under a synthetic/local-only authorization unless you have first removed or mocked every such
105
- startup path and verified the network log is empty of Wix/external requests. Prefer a blank,
109
+ startup path and verified the network log is empty of Wix/external requests. You do not have to
110
+ read the workers to know: `himi init --template` answers it in its envelope's `adopt` block
111
+ (`bootsOffline`, and `.himi/adopt.json` → `bootRequests` names each screen action and the Wix
112
+ modules or `fetch` it can reach; `himi template inspect` recomputes it after edits). It is a
113
+ static answer on the safe side — "may request" — so `bootsOffline: true` clears the preflight and
114
+ `false` means authorize, mock or remove those paths first. Prefer a blank,
106
115
  local-only scaffold for that stage. A browser render that causes an unapproved request is a failed
107
116
  consent gate, not a harmless preview.
108
117
 
@@ -113,10 +122,11 @@ web host and call it owner-private, and do not let a template worker boot there
113
122
  network paths are authorized and preflighted.
114
123
 
115
124
  **Adopt, don't just rename, a template.** The approved `SPEC.md` and `MOBILE-UX.md` are the
116
- plan, and the plan wins over anything `himi init` seeds. `init` seeds placeholder copies of
117
- both and can overwrite plan files already in the package root, so keep a copy of the approved
118
- plan until `init` has run. After `init`, open both files:
119
- if either holds `init`'s boilerplate rather than the approved plan, put the approved plan back in
125
+ plan, and the plan wins over anything `himi init` seeds. `init` keeps a `SPEC.md` /
126
+ `MOBILE-UX.md` already in the target folder — its envelope lists them under `kept` — and seeds
127
+ its skeleton only where none exists, so write the approved plan there before `init`. After
128
+ `init`, open both files: if either holds `init`'s boilerplate rather than the approved plan (an
129
+ older CLI, or a plan kept elsewhere), put the approved plan back in
120
130
  its place — never the reverse — and finish it with the actual customer, data/auth assumptions,
121
131
  placeholder copy, flow/state matrix, and compact/phone/expanded + device evidence before calling
122
132
  the app ready. Keep scenario switches out of customer UI. A labelled preview fallback is acceptable
@@ -126,18 +136,26 @@ only when the host cannot perform the task and it cannot write customer data.
126
136
  product. Right after `init`:
127
137
 
128
138
  1. **Inventory what you inherited** before editing: its screens, local and remote data, auth,
129
- capabilities, demo/fallback behaviour, and template-specific copy. Write it down — it is the
130
- list the next steps work through.
139
+ capabilities, demo/fallback behaviour, and template-specific copy. A current `himi init
140
+ --template` writes it for you as `.himi/adopt.json` (`himi template inspect` rewrites it) —
141
+ the list the next steps work through.
131
142
  2. **Replace the template's identity**: its app name, addresses, sample content, and every
132
143
  `YOUR_*` / `replace with` placeholder. A placeholder left in is shipped as copy —
133
- `himi ux audit --strict` fails on one as `template-placeholder`.
144
+ `himi ux audit --strict` fails on one as `template-placeholder`. `adopt.json` lists every
145
+ placeholder and sample-copy hit; `himi template sweep --set "Old=>New"` replaces them inside
146
+ string literals only.
134
147
  3. **Put each inherited risky flow in the acceptance matrix** — booking/cancellation, payments,
135
- deletion, media, maps, entitlement — or remove it (`himi template trim`). A flow the plan does
136
- not mention is a flow nobody approved.
148
+ deletion, media, maps, entitlement — or remove it (`himi template trim`; `--from-spec` keeps
149
+ exactly the screen ids in `SPEC.md`'s Screen catalogue — dry-run first and add every
150
+ `fromSpec.droppedPrimary` target, or a kept screen loses its own primary action while `validate`
151
+ stays green). A flow the plan does not mention is a
152
+ flow nobody approved. `.himi/adopt-matrix.md` proposes the `## Data access` rows from the
153
+ template's actions (writes, payments, destructive) — proposals for the owner, never approvals.
137
154
  4. **Classify every control** as primary, secondary, destructive, or preview/fallback, and keep a
138
155
  destructive or writing control only when the approved data-access matrix allows it.
139
156
  5. **Prove each adopted flow, not just each screen**: with fixtures or `dev/net-mocks.json`, prove
140
157
  typed input → submit → the persisted result with a typed journey in `himi-test.json`
158
+ (`.himi/himi-test.stub.json` has a skeleton per form: fill the values and expected text)
141
159
  (`--deep` replays action chains but never types a field value), then prove the destination
142
160
  and cancel/back recovery separately — a typed journey is screen-local and does not follow
143
161
  navigation, so use a render of the destination or a device drive. A fresh-screen crawl alone
@@ -104,7 +104,11 @@ or `--template owner-lite` (owner CRUD/management: lists, details, forms). That
104
104
  worker layer — trim it or mine it (`himi docs trim-a-template`): **trim** to the approved
105
105
  `SPEC.md` when most of its screens fit, or **mine** it when few do — keep the
106
106
  `tier5-src/_shared/` helpers and Wix worker chains and write your own screens. Do not delete
107
- `tier5-src/` + `dev/` and start from blank. For a small **non-Wix** pattern, `himi init --template chat-bot --remote`. `himi template
107
+ `tier5-src/` + `dev/` and start from blank. Never read that file whole: `himi template screens`
108
+ lists its screens (size, workers, `onAppear`), `--show <id>` prints one entry, and
109
+ `--write <id> --from <file>` puts an edited entry back (refused if it breaks the file). Not sure
110
+ which template fits? `himi template recommend --text "<the business>"` (or `--book <site analyze
111
+ 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
108
112
  list --remote` shows the full catalog (title/description/tags) — including templates published
109
113
  to Wix — and `--remote` on `himi init` fetches one from there (the bundled copy is
110
114
  the offline fallback). Publish any app of your own as a template with `himi template publish` —
@@ -131,7 +135,12 @@ done. Print one yourself with `himi template prompt <name>`.
131
135
  **Consent preflight before a template preview.** `branded-lite` and `owner-lite` are Wix-integrated
132
136
  full apps; their workers can request Wix data when they boot. Do not start their worker or web
133
137
  host under a synthetic/local-only authorization unless you have first removed or mocked every such
134
- startup path and verified the network log is empty of Wix/external requests. Prefer a blank,
138
+ startup path and verified the network log is empty of Wix/external requests. You do not have to
139
+ read the workers to know: `himi init --template` answers it in its envelope's `adopt` block
140
+ (`bootsOffline`, and `.himi/adopt.json` → `bootRequests` names each screen action and the Wix
141
+ modules or `fetch` it can reach; `himi template inspect` recomputes it after edits). It is a
142
+ static answer on the safe side — "may request" — so `bootsOffline: true` clears the preflight and
143
+ `false` means authorize, mock or remove those paths first. Prefer a blank,
135
144
  local-only scaffold for that stage. A browser render that causes an unapproved request is a failed
136
145
  consent gate, not a harmless preview.
137
146
 
@@ -142,10 +151,11 @@ web host and call it owner-private, and do not let a template worker boot there
142
151
  network paths are authorized and preflighted.
143
152
 
144
153
  **Adopt, don't just rename, a template.** The approved `SPEC.md` and `MOBILE-UX.md` are the
145
- plan, and the plan wins over anything `himi init` seeds. `init` seeds placeholder copies of
146
- both and can overwrite plan files already in the package root, so keep a copy of the approved
147
- plan until `init` has run. After `init`, open both files:
148
- if either holds `init`'s boilerplate rather than the approved plan, put the approved plan back in
154
+ plan, and the plan wins over anything `himi init` seeds. `init` keeps a `SPEC.md` /
155
+ `MOBILE-UX.md` already in the target folder — its envelope lists them under `kept` — and seeds
156
+ its skeleton only where none exists, so write the approved plan there before `init`. After
157
+ `init`, open both files: if either holds `init`'s boilerplate rather than the approved plan (an
158
+ older CLI, or a plan kept elsewhere), put the approved plan back in
149
159
  its place — never the reverse — and finish it with the actual customer, data/auth assumptions,
150
160
  placeholder copy, flow/state matrix, and compact/phone/expanded + device evidence before calling
151
161
  the app ready. Keep scenario switches out of customer UI. A labelled preview fallback is acceptable
@@ -155,18 +165,26 @@ only when the host cannot perform the task and it cannot write customer data.
155
165
  product. Right after `init`:
156
166
 
157
167
  1. **Inventory what you inherited** before editing: its screens, local and remote data, auth,
158
- capabilities, demo/fallback behaviour, and template-specific copy. Write it down — it is the
159
- list the next steps work through.
168
+ capabilities, demo/fallback behaviour, and template-specific copy. A current `himi init
169
+ --template` writes it for you as `.himi/adopt.json` (`himi template inspect` rewrites it) —
170
+ the list the next steps work through.
160
171
  2. **Replace the template's identity**: its app name, addresses, sample content, and every
161
172
  `YOUR_*` / `replace with` placeholder. A placeholder left in is shipped as copy —
162
- `himi ux audit --strict` fails on one as `template-placeholder`.
173
+ `himi ux audit --strict` fails on one as `template-placeholder`. `adopt.json` lists every
174
+ placeholder and sample-copy hit; `himi template sweep --set "Old=>New"` replaces them inside
175
+ string literals only.
163
176
  3. **Put each inherited risky flow in the acceptance matrix** — booking/cancellation, payments,
164
- deletion, media, maps, entitlement — or remove it (`himi template trim`). A flow the plan does
165
- not mention is a flow nobody approved.
177
+ deletion, media, maps, entitlement — or remove it (`himi template trim`; `--from-spec` keeps
178
+ exactly the screen ids in `SPEC.md`'s Screen catalogue — dry-run first and add every
179
+ `fromSpec.droppedPrimary` target, or a kept screen loses its own primary action while `validate`
180
+ stays green). A flow the plan does not mention is a
181
+ flow nobody approved. `.himi/adopt-matrix.md` proposes the `## Data access` rows from the
182
+ template's actions (writes, payments, destructive) — proposals for the owner, never approvals.
166
183
  4. **Classify every control** as primary, secondary, destructive, or preview/fallback, and keep a
167
184
  destructive or writing control only when the approved data-access matrix allows it.
168
185
  5. **Prove each adopted flow, not just each screen**: with fixtures or `dev/net-mocks.json`, prove
169
186
  typed input → submit → the persisted result with a typed journey in `himi-test.json`
187
+ (`.himi/himi-test.stub.json` has a skeleton per form: fill the values and expected text)
170
188
  (`--deep` replays action chains but never types a field value), then prove the destination
171
189
  and cancel/back recovery separately — a typed journey is screen-local and does not follow
172
190
  navigation, so use a render of the destination or a device drive. A fresh-screen crawl alone
@@ -1331,6 +1331,132 @@
1331
1331
  "source": "core/sdk/iap/src/index.ts",
1332
1332
  "description": "Tier-5 typed binding to the platform's in-app-purchase capability.",
1333
1333
  "api": [
1334
+ {
1335
+ "name": "SessionSnapshot",
1336
+ "kind": "interface",
1337
+ "jsdoc": "/** Trusted host session snapshot. Not a credential: the backend adapter resolves real\n * credentials from its own authenticated transport, bound to this `generation`. */",
1338
+ "decl": "export interface SessionSnapshot {\n /** Changes on every sign-in, sign-out, member or site change. */\n generation: number;\n kind: \"member\" | \"visitor\" | \"owner\" | \"signedOut\";\n}"
1339
+ },
1340
+ {
1341
+ "name": "SessionProvider",
1342
+ "kind": "interface",
1343
+ "jsdoc": "/** Supplied by the host, never by worker UI state. */",
1344
+ "decl": "export interface SessionProvider {\n current(): SessionSnapshot;\n /** Called after `current()` changes; returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n}"
1345
+ },
1346
+ {
1347
+ "name": "PurchaseContext",
1348
+ "kind": "interface",
1349
+ "jsdoc": "/** A catalog item resolved by the backend for the captured member. */",
1350
+ "decl": "export interface PurchaseContext {\n productId: string;\n /** Canonical UUID the backend associates with this site/member. */\n appAccountToken: string;\n /** Opaque site/member scope the acknowledgement must echo. */\n scopeKey: string;\n}"
1351
+ },
1352
+ {
1353
+ "name": "MemberContext",
1354
+ "kind": "interface",
1355
+ "jsdoc": "/** The current member's association, used to recover purchases without a catalog item. */",
1356
+ "decl": "export interface MemberContext {\n appAccountToken: string;\n scopeKey: string;\n}"
1357
+ },
1358
+ {
1359
+ "name": "ContextResult",
1360
+ "kind": "type",
1361
+ "jsdoc": "",
1362
+ "decl": "export type ContextResult<T> =\n | { status: \"ok\"; context: T }\n | { status: \"authRequired\" | \"rejected\" }\n | { status: \"retryable\"; retryAfterMs?: number };"
1363
+ },
1364
+ {
1365
+ "name": "ProcessingAcknowledgement",
1366
+ "kind": "interface",
1367
+ "jsdoc": "/** Bound to one transaction and scope. Means verification AND durable access persistence\n * completed; an HTTP 2xx, a receipt or a job id is not an acknowledgement. */",
1368
+ "decl": "export interface ProcessingAcknowledgement {\n transactionId: string;\n productId: string;\n environment: AppleTransaction[\"environment\"];\n scopeKey: string;\n}"
1369
+ },
1370
+ {
1371
+ "name": "SubmitProofResult",
1372
+ "kind": "type",
1373
+ "jsdoc": "",
1374
+ "decl": "export type SubmitProofResult =\n | { status: \"processed\" | \"alreadyProcessed\"; acknowledgement: ProcessingAcknowledgement }\n | { status: \"retryable\"; retryAfterMs?: number }\n | { status: \"authRequired\" | \"ownershipConflict\" | \"rejected\" };"
1375
+ },
1376
+ {
1377
+ "name": "DeliveryBackend",
1378
+ "kind": "interface",
1379
+ "jsdoc": "/** PROPOSED backend agreement. Every call carries the session captured when the work\n * started, never whichever session is current when the request is sent. Responses are\n * decoded strictly; anything unrecognised is treated as invalid, never as success. */",
1380
+ "decl": "export interface DeliveryBackend {\n getPurchaseContext(session: SessionSnapshot, catalogRef: string): Promise<ContextResult<PurchaseContext>>;\n getMemberContext(session: SessionSnapshot): Promise<ContextResult<MemberContext>>;\n /** Must be idempotent per store/environment/transaction id; delivery is at-least-once. */\n submitProof(session: SessionSnapshot, transaction: AppleTransaction): Promise<SubmitProofResult>;\n}"
1381
+ },
1382
+ {
1383
+ "name": "TransactionStore",
1384
+ "kind": "interface",
1385
+ "jsdoc": "/** The three managed verbs; `appleManagedStore` in index.ts is the production binding. */",
1386
+ "decl": "export interface TransactionStore {\n purchaseForVerification(input: { productId: string; appAccountToken: string }): Promise<VerificationPurchaseResult>;\n getUnfinishedTransactions(): Promise<UnfinishedTransactionsResult>;\n finishTransaction(input: { transactionId: string }): Promise<FinishTransactionResult>;\n}"
1387
+ },
1388
+ {
1389
+ "name": "Clock",
1390
+ "kind": "interface",
1391
+ "jsdoc": "",
1392
+ "decl": "export interface Clock {\n /** Epoch milliseconds. */\n now(): number;\n}"
1393
+ },
1394
+ {
1395
+ "name": "DeliveryPhase",
1396
+ "kind": "type",
1397
+ "jsdoc": "",
1398
+ "decl": "export type DeliveryPhase = \"idle\" | \"purchasing\" | \"awaitingApproval\" | \"cancelled\" | \"signInRequired\" | \"unavailable\";"
1399
+ },
1400
+ {
1401
+ "name": "DeliveryErrorCode",
1402
+ "kind": "type",
1403
+ "jsdoc": "/** `sessionChanged`: a purchase was requested under an earlier session and was not carried out. */",
1404
+ "decl": "export type DeliveryErrorCode = TransactionErrorCode | \"contextUnavailable\" | \"contextRejected\" | \"proofRejected\"\n | \"sessionChanged\";"
1405
+ },
1406
+ {
1407
+ "name": "AccessStatus",
1408
+ "kind": "type",
1409
+ "jsdoc": "",
1410
+ "decl": "export type AccessStatus = \"pending\" | \"confirming\" | \"waitingToRetry\" | \"signInRequired\"\n | \"ownershipConflict\" | \"rejected\" | \"confirmed\";"
1411
+ },
1412
+ {
1413
+ "name": "CompletionStatus",
1414
+ "kind": "type",
1415
+ "jsdoc": "",
1416
+ "decl": "export type CompletionStatus = \"pending\" | \"finished\" | \"notFound\" | \"retryRequired\";"
1417
+ },
1418
+ {
1419
+ "name": "DeliveryItemState",
1420
+ "kind": "interface",
1421
+ "jsdoc": "/** Safe to render and persist: no proof, account token, transaction or member id. */",
1422
+ "decl": "export interface DeliveryItemState {\n productId: string;\n environment: AppleTransaction[\"environment\"];\n /** Present in the last native unfinished enumeration; null before one succeeded. */\n held: boolean | null;\n /** `confirmed` only from a matching acknowledgement for the current session. */\n access: AccessStatus;\n completion: CompletionStatus;\n attempts: number;\n nextAttemptAt: number | null;\n errorCode: DeliveryErrorCode | null;\n}"
1423
+ },
1424
+ {
1425
+ "name": "DeliveryState",
1426
+ "kind": "interface",
1427
+ "jsdoc": "",
1428
+ "decl": "export interface DeliveryState {\n phase: DeliveryPhase;\n errorCode: DeliveryErrorCode | null;\n items: DeliveryItemState[];\n /** From the last successful enumeration; null when unknown or it failed. */\n unfinishedCount: number | null;\n /** Earliest `nextAttemptAt`; hosts schedule `tick()` for it (e.g. a worker `_poll`). */\n nextWakeAt: number | null;\n}"
1429
+ },
1430
+ {
1431
+ "name": "PurchaseCoordinatorDependencies",
1432
+ "kind": "interface",
1433
+ "jsdoc": "",
1434
+ "decl": "export interface PurchaseCoordinatorDependencies {\n store: TransactionStore;\n backend: DeliveryBackend;\n session: SessionProvider;\n clock: Clock;\n /** Extra milliseconds added to a backoff step; inject for deterministic tests. */\n jitter?: (baseDelayMs: number) => number;\n /** Receives a fresh safe snapshot after every change. */\n onState?: (state: DeliveryState) => void;\n}"
1435
+ },
1436
+ {
1437
+ "name": "PurchaseCoordinator",
1438
+ "kind": "interface",
1439
+ "jsdoc": "",
1440
+ "decl": "export interface PurchaseCoordinator {\n /** Reconciles unfinished purchases now and again after every later session change. */\n start(): Promise<DeliveryState>;\n /** Buys a catalog item; the backend resolves its product id and account token.\n * The same member's purchase of the same item already in progress is shared, not repeated. */\n purchase(catalogRef: string): Promise<DeliveryState>;\n /** Re-enumerates unfinished purchases and delivers eligible ones (e.g. on resume). */\n reconcile(): Promise<DeliveryState>;\n /** Unblocks every waiting or blocked item with a fresh attempt budget, then reconciles. */\n retry(): Promise<DeliveryState>;\n /** Runs whatever retries are due at `clock.now()`. */\n tick(): Promise<DeliveryState>;\n getState(): DeliveryState;\n /** Drops the session subscription and suppresses every later callback. */\n dispose(): void;\n}"
1441
+ },
1442
+ {
1443
+ "name": "MAX_AUTOMATIC_ATTEMPTS",
1444
+ "kind": "const",
1445
+ "jsdoc": "/** Automatic attempts per item per reconciliation run; the item is never discarded after. */",
1446
+ "decl": "export const MAX_AUTOMATIC_ATTEMPTS = 5;"
1447
+ },
1448
+ {
1449
+ "name": "MAX_RETRY_DELAY_MS",
1450
+ "kind": "const",
1451
+ "jsdoc": "",
1452
+ "decl": "export const MAX_RETRY_DELAY_MS = 60_000;"
1453
+ },
1454
+ {
1455
+ "name": "createPurchaseCoordinator",
1456
+ "kind": "function",
1457
+ "jsdoc": "/** Creates a coordinator owned by the host/service lifecycle, not by a screen or render.\n * Closures, not classes: Tier-5 bundles must also evaluate on Hermes, which has no `class`. */",
1458
+ "decl": "export function createPurchaseCoordinator(deps: PurchaseCoordinatorDependencies): PurchaseCoordinator;"
1459
+ },
1334
1460
  {
1335
1461
  "name": "ProductType",
1336
1462
  "kind": "type",
@@ -1462,6 +1588,12 @@
1462
1588
  "kind": "function",
1463
1589
  "jsdoc": "/** Trusted callers must first obtain durable backend processing acknowledgement. */",
1464
1590
  "decl": "export async function finishTransaction(input: { transactionId: string }): Promise<FinishTransactionResult>;"
1591
+ },
1592
+ {
1593
+ "name": "appleManagedStore",
1594
+ "kind": "const",
1595
+ "jsdoc": "/** The managed native verbs as the delivery coordinator's store. */",
1596
+ "decl": "export const appleManagedStore: TransactionStore = { purchaseForVerification, getUnfinishedTransactions, finishTransaction };"
1465
1597
  }
1466
1598
  ]
1467
1599
  },
@@ -93,6 +93,39 @@ as a raw descriptor (megabytes for the full templates), so replacing it is often
93
93
  editing it. Both paths, and how `dev/index.ts`, `tier5-src/`, and `navigation.linkableScreens`
94
94
  relate: `himi docs trim-a-template`.
95
95
 
96
+ ## Adopt — what the CLI works out for you
97
+
98
+ Starting from a template is a fork of a large app, and most of what an agent needs to know about it
99
+ is mechanical. `himi init --template` answers it in the envelope's `adopt` block and writes the
100
+ details beside the app:
101
+
102
+ | File | What it holds |
103
+ |---|---|
104
+ | `.himi/adopt.json` | every screen (size, workers, `onAppear`), every worker's Wix modules / `wixApi` / `fetch` reach, navigation, **`bootRequests`** + **`bootsOffline`**, placeholder and sample-copy hits |
105
+ | `.himi/adopt-matrix.md` | proposed `## Data access` rows from the template's actions — writes, payments, destructive — in the exact table `himi test` reads. Proposals for the owner, never approvals |
106
+ | `.himi/himi-test.stub.json` | a typed-journey skeleton per form (every field, then its submit) with `<FILL>` slots |
107
+
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:
111
+
112
+ ```sh
113
+ himi template recommend --text "a salon where clients book and buy products" # rank the catalog (or --verticals / --book)
114
+ himi template inspect # recompute the inventory after edits (--dry-run writes nothing)
115
+ himi template screens # list dev/index.ts screens without reading the file
116
+ himi template screens --show himi_saved > saved.txt # ONE screen's entry
117
+ himi template screens --write himi_saved --from saved.txt # put it back — re-parsed, refused if it breaks the file
118
+ himi template sweep --set "Sample content=>Our latest" --dry-run # copy inside string literals only
119
+ himi template trim --from-spec --links drop --dry-run # keep SPEC.md's Screen catalogue ids — read fromSpec.droppedPrimary first
120
+ ```
121
+
122
+ `sweep` works on the TypeScript AST, so comments and code are never candidates — only string
123
+ literals, and of those only prose (has whitespace), a template placeholder (`YOUR_*`), or the value
124
+ of a copy key (`title`, `text`, `label`, `placeholder`, …; `name` only in `config.ts`, because in a
125
+ descriptor it is an Icon id). A one-word literal elsewhere — a service name, a component type, an
126
+ action id, a URL — is wiring, and is never rewritten. Every changed file is syntax-checked before
127
+ anything is written.
128
+
96
129
  **Point a `branded-lite` (or vertical overlay) scaffold at your site.** Set
97
130
  `WIX_CLIENT_ID` (Headless OAuth client on that site) and `WIX_SITE_URL` before build /
98
131
  preview — the `clientId` **is** the site binding (no separate MSID field). Placeholders
@@ -48,6 +48,19 @@ himi template trim --keep himi_shop,himi_product,himi_cart,himi_account --dry-ru
48
48
  himi template trim --keep himi_shop,himi_product,himi_cart,himi_account --links drop # do it
49
49
  ```
50
50
 
51
+ **From the plan:** `himi template trim --from-spec [SPEC.md] --links drop` takes `--keep` from the
52
+ first column of `SPEC.md`'s `## Screen catalogue` (backticked screen ids; prose rows are ignored).
53
+ Its report adds `fromSpec.specOnly` — the plan screens the template does not have, which you author
54
+ (the "mine" part) — and `fromSpec.droppedPrimary`: each kept screen whose own primary action (a list
55
+ row's tap, a CTA) opened a screen the plan left out. `--links drop` removes those taps and `validate`
56
+ stays green, so a Services list that listed its services but could no longer book one passed every
57
+ check. Dry-run first, add each `droppedPrimary` target to the catalogue (or confirm the plan drops
58
+ that flow), then trim for real. A tap whose target a WORKER computes is invisible to trim; `himi test`
59
+ reports it as `nav-target-missing`. A trim that keeps its tree (a success, or `--force`) also
60
+ refreshes `.himi/adopt.json`; it leaves an existing `adopt-matrix.md` / `himi-test.stub.json` alone,
61
+ since those are the files you fill in (`himi template inspect` regenerates all three). `himi template screens`
62
+ lists the ids to put in that column.
63
+
51
64
  Run it in (or point `--content` at) the app `himi init --template` scaffolded — not the
52
65
  template itself, which it refuses. `--keep` takes `SCREENS` ids and templated route ids (the
53
66
  part before the brackets: `himi_product` for `himi_product[_{id}]`). The home screen is always
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1142.0",
3
+ "version": "0.1144.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "da6ce19c2485b32baf1d9f2aca5df0f7110f9b0dbff3115b10b40ac9"
70
+ "falconPackageHash": "36c29f9cff9394a99971f075c550109e68d2701bcd059b1ece8369b2"
71
71
  }
@@ -2493,6 +2493,7 @@ const SCREENS = runtimeSetting({
2493
2493
  "type": "TextField",
2494
2494
  "props": {
2495
2495
  "placeholder": "Password",
2496
+ "valuePath": "$service:himiStoreAccount.password",
2496
2497
  "secure": true,
2497
2498
  "autoCapitalization": "none",
2498
2499
  "onChange": {
@@ -27653,6 +27654,7 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
27653
27654
  "type": "TextField",
27654
27655
  "props": {
27655
27656
  "placeholder": "Password",
27657
+ "valuePath": "$service:himiBookingTime.password",
27656
27658
  "secure": true,
27657
27659
  "autoCapitalization": "none",
27658
27660
  "onChange": {
@@ -603,7 +603,7 @@ const svc = defineService<S, {
603
603
  message: copyParam("time.signedInReady", "Signed in. You can book this time now."),
604
604
  });
605
605
  } catch {
606
- return derive({ ...s, message: copyParam("time.signInFailed", "Sign in failed. Try again.") });
606
+ return derive({ ...s, password: "", message: copyParam("time.signInFailed", "Sign in failed. Try again.") });
607
607
  }
608
608
  },
609
609
  register: async (s) => {
@@ -626,7 +626,7 @@ const svc = defineService<S, {
626
626
  message: copyParam("time.registerReady", "Account created. You can book this time now."),
627
627
  });
628
628
  } catch {
629
- return derive({ ...s, message: copyParam("time.registerFailed", "Account creation failed. Try another email.") });
629
+ return derive({ ...s, password: "", message: copyParam("time.registerFailed", "Account creation failed. Try another email.") });
630
630
  }
631
631
  },
632
632
  bookSelected: async (s) => {
@@ -77,7 +77,10 @@ async function authenticate(s: S, submit: () => Promise<unknown>, notMemberMessa
77
77
  if (!next.isMember) return derive({ ...next, message: notMemberMessage });
78
78
  return resumeAfterAuth(derive({ ...next, message: "" }));
79
79
  } catch {
80
- return derive({ ...s, message: failedMessage });
80
+ // Clear the password: service state is snapshotted to disk (the worker-state
81
+ // store), so a failed attempt must not leave it there in cleartext. The Password
82
+ // field binds `valuePath` to it, so the field empties too and a retry re-types.
83
+ return derive({ ...s, password: "", message: failedMessage });
81
84
  } finally {
82
85
  authInFlight = false;
83
86
  }
@@ -168,6 +171,9 @@ interface S {
168
171
  noReturnPath: boolean;
169
172
  returnCta: string;
170
173
  email: string;
174
+ /** Held only from typing to the submit. Service state is persisted, so every
175
+ * submitted sign-in/register clears it (success, not-a-member, and failure), as
176
+ * do recovery and sign-out. Sign-in <-> register keeps it: the field stays up. */
171
177
  password: string;
172
178
  firstName: string;
173
179
  lastName: string;
@@ -3080,6 +3080,7 @@ const SCREENS = runtimeSetting({
3080
3080
  "type": "TextField",
3081
3081
  "props": {
3082
3082
  "placeholder": "Password",
3083
+ "valuePath": "$service:himiStoreAccount.password",
3083
3084
  "secure": true,
3084
3085
  "autoCapitalization": "none",
3085
3086
  "onChange": {
@@ -82309,6 +82310,7 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
82309
82310
  "type": "TextField",
82310
82311
  "props": {
82311
82312
  "placeholder": "Password",
82313
+ "valuePath": "$service:himiBookingTime.password",
82312
82314
  "secure": true,
82313
82315
  "autoCapitalization": "none",
82314
82316
  "onChange": {
@@ -85183,7 +85185,7 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
85183
85185
  "type": "TextField",
85184
85186
  "props": {
85185
85187
  "placeholder": "Password",
85186
- "value": "",
85188
+ "valuePath": "$service:himiEventsRsvp.password",
85187
85189
  "secure": true,
85188
85190
  "autoCapitalization": "none",
85189
85191
  "onChange": {
@@ -603,7 +603,7 @@ const svc = defineService<S, {
603
603
  message: copyParam("time.signedInReady", "Signed in. You can book this time now."),
604
604
  });
605
605
  } catch {
606
- return derive({ ...s, message: copyParam("time.signInFailed", "Sign in failed. Try again.") });
606
+ return derive({ ...s, password: "", message: copyParam("time.signInFailed", "Sign in failed. Try again.") });
607
607
  }
608
608
  },
609
609
  register: async (s) => {
@@ -626,7 +626,7 @@ const svc = defineService<S, {
626
626
  message: copyParam("time.registerReady", "Account created. You can book this time now."),
627
627
  });
628
628
  } catch {
629
- return derive({ ...s, message: copyParam("time.registerFailed", "Account creation failed. Try another email.") });
629
+ return derive({ ...s, password: "", message: copyParam("time.registerFailed", "Account creation failed. Try another email.") });
630
630
  }
631
631
  },
632
632
  bookSelected: async (s) => {
@@ -179,9 +179,9 @@ const svc = defineService<S, {
179
179
  try {
180
180
  await dispatch("himi.wix.signInDirect", { email: s.email, password: s.password });
181
181
  const p = await probeMember({ ...s, isMember: true, identityLine: s.email });
182
- return derive({ ...p, message: p.isMember ? copyParam("rsvp.signedInReady", "Signed in. You can RSVP now.") : copyParam("rsvp.signInCheckDetails", "Sign in failed. Check your details.") });
182
+ return derive({ ...p, password: "", message: p.isMember ? copyParam("rsvp.signedInReady", "Signed in. You can RSVP now.") : copyParam("rsvp.signInCheckDetails", "Sign in failed. Check your details.") });
183
183
  } catch {
184
- return derive({ ...s, message: copyParam("rsvp.signInFailed", "Sign in failed. Try again.") });
184
+ return derive({ ...s, password: "", message: copyParam("rsvp.signInFailed", "Sign in failed. Try again.") });
185
185
  }
186
186
  },
187
187
  rsvp: async (s) => {
@@ -77,7 +77,10 @@ async function authenticate(s: S, submit: () => Promise<unknown>, notMemberMessa
77
77
  if (!next.isMember) return derive({ ...next, message: notMemberMessage });
78
78
  return resumeAfterAuth(derive({ ...next, message: "" }));
79
79
  } catch {
80
- return derive({ ...s, message: failedMessage });
80
+ // Clear the password: service state is snapshotted to disk (the worker-state
81
+ // store), so a failed attempt must not leave it there in cleartext. The Password
82
+ // field binds `valuePath` to it, so the field empties too and a retry re-types.
83
+ return derive({ ...s, password: "", message: failedMessage });
81
84
  } finally {
82
85
  authInFlight = false;
83
86
  }
@@ -168,6 +171,9 @@ interface S {
168
171
  noReturnPath: boolean;
169
172
  returnCta: string;
170
173
  email: string;
174
+ /** Held only from typing to the submit. Service state is persisted, so every
175
+ * submitted sign-in/register clears it (success, not-a-member, and failure), as
176
+ * do recovery and sign-out. Sign-in <-> register keeps it: the field stays up. */
171
177
  password: string;
172
178
  firstName: string;
173
179
  lastName: string;
@@ -2115,6 +2115,7 @@ const SCREENS = runtimeSetting({
2115
2115
  "type": "TextField",
2116
2116
  "props": {
2117
2117
  "placeholder": "Password",
2118
+ "valuePath": "$service:himiStoreAccount.password",
2118
2119
  "secure": true,
2119
2120
  "autoCapitalization": "none",
2120
2121
  "onChange": {
@@ -18328,7 +18329,7 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
18328
18329
  "type": "TextField",
18329
18330
  "props": {
18330
18331
  "placeholder": "Password",
18331
- "value": "",
18332
+ "valuePath": "$service:himiEventsRsvp.password",
18332
18333
  "secure": true,
18333
18334
  "autoCapitalization": "none",
18334
18335
  "onChange": {
@@ -179,9 +179,9 @@ const svc = defineService<S, {
179
179
  try {
180
180
  await dispatch("himi.wix.signInDirect", { email: s.email, password: s.password });
181
181
  const p = await probeMember({ ...s, isMember: true, identityLine: s.email });
182
- return derive({ ...p, message: p.isMember ? copyParam("rsvp.signedInReady", "Signed in. You can RSVP now.") : copyParam("rsvp.signInCheckDetails", "Sign in failed. Check your details.") });
182
+ return derive({ ...p, password: "", message: p.isMember ? copyParam("rsvp.signedInReady", "Signed in. You can RSVP now.") : copyParam("rsvp.signInCheckDetails", "Sign in failed. Check your details.") });
183
183
  } catch {
184
- return derive({ ...s, message: copyParam("rsvp.signInFailed", "Sign in failed. Try again.") });
184
+ return derive({ ...s, password: "", message: copyParam("rsvp.signInFailed", "Sign in failed. Try again.") });
185
185
  }
186
186
  },
187
187
  rsvp: async (s) => {
@@ -77,7 +77,10 @@ async function authenticate(s: S, submit: () => Promise<unknown>, notMemberMessa
77
77
  if (!next.isMember) return derive({ ...next, message: notMemberMessage });
78
78
  return resumeAfterAuth(derive({ ...next, message: "" }));
79
79
  } catch {
80
- return derive({ ...s, message: failedMessage });
80
+ // Clear the password: service state is snapshotted to disk (the worker-state
81
+ // store), so a failed attempt must not leave it there in cleartext. The Password
82
+ // field binds `valuePath` to it, so the field empties too and a retry re-types.
83
+ return derive({ ...s, password: "", message: failedMessage });
81
84
  } finally {
82
85
  authInFlight = false;
83
86
  }
@@ -168,6 +171,9 @@ interface S {
168
171
  noReturnPath: boolean;
169
172
  returnCta: string;
170
173
  email: string;
174
+ /** Held only from typing to the submit. Service state is persisted, so every
175
+ * submitted sign-in/register clears it (success, not-a-member, and failure), as
176
+ * do recovery and sign-out. Sign-in <-> register keeps it: the field stays up. */
171
177
  password: string;
172
178
  firstName: string;
173
179
  lastName: string;
@@ -2299,6 +2299,7 @@ const SCREENS = runtimeSetting({
2299
2299
  "type": "TextField",
2300
2300
  "props": {
2301
2301
  "placeholder": "Password",
2302
+ "valuePath": "$service:himiStoreAccount.password",
2302
2303
  "secure": true,
2303
2304
  "autoCapitalization": "none",
2304
2305
  "onChange": {
@@ -16326,7 +16327,7 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
16326
16327
  "type": "TextField",
16327
16328
  "props": {
16328
16329
  "placeholder": "Password",
16329
- "value": "",
16330
+ "valuePath": "$service:himiEventsRsvp.password",
16330
16331
  "secure": true,
16331
16332
  "autoCapitalization": "none",
16332
16333
  "onChange": {
@@ -179,9 +179,9 @@ const svc = defineService<S, {
179
179
  try {
180
180
  await dispatch("himi.wix.signInDirect", { email: s.email, password: s.password });
181
181
  const p = await probeMember({ ...s, isMember: true, identityLine: s.email });
182
- return derive({ ...p, message: p.isMember ? copyParam("rsvp.signedInReady", "Signed in. You can RSVP now.") : copyParam("rsvp.signInCheckDetails", "Sign in failed. Check your details.") });
182
+ return derive({ ...p, password: "", message: p.isMember ? copyParam("rsvp.signedInReady", "Signed in. You can RSVP now.") : copyParam("rsvp.signInCheckDetails", "Sign in failed. Check your details.") });
183
183
  } catch {
184
- return derive({ ...s, message: copyParam("rsvp.signInFailed", "Sign in failed. Try again.") });
184
+ return derive({ ...s, password: "", message: copyParam("rsvp.signInFailed", "Sign in failed. Try again.") });
185
185
  }
186
186
  },
187
187
  rsvp: async (s) => {