@wix/himalaya-cli 0.1388.0 → 0.1390.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 (97) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/commerce-contract.d.ts +24 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/commerce-core.d.ts +3 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/index.d.ts +1 -1
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +57 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +57 -0
  6. package/dist/cli.mjs +7 -7
  7. package/guides/iap-screen-adoption.md +53 -1
  8. package/guides/sdk-reference.json +8 -2
  9. package/package.json +2 -2
  10. package/templates/beauty-salon/dev/index.ts +193 -1
  11. package/templates/beauty-salon/tier5-src/_shared/iap-worker.ts +84 -3
  12. package/templates/beauty-salon/tier5-src/himi-store-account/index.ts +36 -1
  13. package/templates/beauty-salon-owner/config.ts +11 -0
  14. package/templates/beauty-salon-owner/dev/index.ts +540 -0
  15. package/templates/beauty-salon-owner/himalaya.content.json +6 -0
  16. package/templates/beauty-salon-owner/platforms.json +2 -1
  17. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  18. package/templates/branded-lite/dev/index.ts +194 -2
  19. package/templates/branded-lite/tier5-src/_shared/iap-worker.ts +84 -3
  20. package/templates/branded-lite/tier5-src/himi-store-account/index.ts +36 -1
  21. package/templates/community-club/dev/index.ts +193 -1
  22. package/templates/community-club/tier5-src/_shared/iap-worker.ts +84 -3
  23. package/templates/community-club/tier5-src/himi-store-account/index.ts +36 -1
  24. package/templates/community-club-owner/config.ts +11 -0
  25. package/templates/community-club-owner/dev/index.ts +540 -0
  26. package/templates/community-club-owner/himalaya.content.json +6 -0
  27. package/templates/community-club-owner/platforms.json +2 -1
  28. package/templates/community-club-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  29. package/templates/events-meetups/dev/index.ts +193 -1
  30. package/templates/events-meetups/tier5-src/_shared/iap-worker.ts +84 -3
  31. package/templates/events-meetups/tier5-src/himi-store-account/index.ts +36 -1
  32. package/templates/events-meetups-owner/config.ts +11 -0
  33. package/templates/events-meetups-owner/dev/index.ts +540 -0
  34. package/templates/events-meetups-owner/himalaya.content.json +6 -0
  35. package/templates/events-meetups-owner/platforms.json +2 -1
  36. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  37. package/templates/gym-fitness/dev/index.ts +193 -1
  38. package/templates/gym-fitness/tier5-src/_shared/iap-worker.ts +84 -3
  39. package/templates/gym-fitness/tier5-src/himi-store-account/index.ts +36 -1
  40. package/templates/gym-fitness-owner/config.ts +11 -0
  41. package/templates/gym-fitness-owner/dev/index.ts +540 -0
  42. package/templates/gym-fitness-owner/himalaya.content.json +6 -0
  43. package/templates/gym-fitness-owner/platforms.json +2 -1
  44. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  45. package/templates/nonprofit-cause/dev/index.ts +193 -1
  46. package/templates/nonprofit-cause/tier5-src/_shared/iap-worker.ts +84 -3
  47. package/templates/nonprofit-cause/tier5-src/himi-store-account/index.ts +36 -1
  48. package/templates/nonprofit-cause-owner/config.ts +11 -0
  49. package/templates/nonprofit-cause-owner/dev/index.ts +540 -0
  50. package/templates/nonprofit-cause-owner/himalaya.content.json +6 -0
  51. package/templates/nonprofit-cause-owner/platforms.json +2 -1
  52. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  53. package/templates/owner-lite/config.ts +11 -0
  54. package/templates/owner-lite/dev/index.ts +540 -0
  55. package/templates/owner-lite/himalaya.content.json +6 -0
  56. package/templates/owner-lite/platforms.json +2 -1
  57. package/templates/owner-lite/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  58. package/templates/podcast-creator/dev/index.ts +193 -1
  59. package/templates/podcast-creator/tier5-src/_shared/iap-worker.ts +84 -3
  60. package/templates/podcast-creator/tier5-src/himi-store-account/index.ts +36 -1
  61. package/templates/podcast-creator-owner/config.ts +11 -0
  62. package/templates/podcast-creator-owner/dev/index.ts +540 -0
  63. package/templates/podcast-creator-owner/himalaya.content.json +6 -0
  64. package/templates/podcast-creator-owner/platforms.json +2 -1
  65. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  66. package/templates/portfolio-creative/dev/index.ts +193 -1
  67. package/templates/portfolio-creative/tier5-src/_shared/iap-worker.ts +84 -3
  68. package/templates/portfolio-creative/tier5-src/himi-store-account/index.ts +36 -1
  69. package/templates/portfolio-creative-owner/config.ts +11 -0
  70. package/templates/portfolio-creative-owner/dev/index.ts +540 -0
  71. package/templates/portfolio-creative-owner/himalaya.content.json +6 -0
  72. package/templates/portfolio-creative-owner/platforms.json +2 -1
  73. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  74. package/templates/restaurant/dev/index.ts +193 -1
  75. package/templates/restaurant/tier5-src/_shared/iap-worker.ts +84 -3
  76. package/templates/restaurant/tier5-src/himi-store-account/index.ts +36 -1
  77. package/templates/restaurant-owner/config.ts +11 -0
  78. package/templates/restaurant-owner/dev/index.ts +540 -0
  79. package/templates/restaurant-owner/himalaya.content.json +6 -0
  80. package/templates/restaurant-owner/platforms.json +2 -1
  81. package/templates/restaurant-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  82. package/templates/retail-store/dev/index.ts +193 -1
  83. package/templates/retail-store/tier5-src/_shared/iap-worker.ts +84 -3
  84. package/templates/retail-store/tier5-src/himi-store-account/index.ts +36 -1
  85. package/templates/retail-store-owner/config.ts +11 -0
  86. package/templates/retail-store-owner/dev/index.ts +540 -0
  87. package/templates/retail-store-owner/himalaya.content.json +6 -0
  88. package/templates/retail-store-owner/platforms.json +2 -1
  89. package/templates/retail-store-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
  90. package/templates/yoga-studio/dev/index.ts +193 -1
  91. package/templates/yoga-studio/tier5-src/_shared/iap-worker.ts +84 -3
  92. package/templates/yoga-studio/tier5-src/himi-store-account/index.ts +36 -1
  93. package/templates/yoga-studio-owner/config.ts +11 -0
  94. package/templates/yoga-studio-owner/dev/index.ts +540 -0
  95. package/templates/yoga-studio-owner/himalaya.content.json +6 -0
  96. package/templates/yoga-studio-owner/platforms.json +2 -1
  97. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-wrist/index.ts +350 -0
@@ -3300,6 +3300,93 @@ const SCREENS = runtimeSetting({
3300
3300
  "accessibilityLabel": "My subscriptions"
3301
3301
  }
3302
3302
  },
3303
+ {
3304
+ "id": "himi_account.restore",
3305
+ "body": {
3306
+ "package": "himalaya",
3307
+ "type": "HStack",
3308
+ "props": {
3309
+ "spacing": 12,
3310
+ "children": [
3311
+ {
3312
+ "id": "",
3313
+ "body": {
3314
+ "package": "himalaya",
3315
+ "type": "Icon",
3316
+ "props": {
3317
+ "name": "refresh",
3318
+ "size": 18,
3319
+ "tint": "accentPrimary"
3320
+ }
3321
+ },
3322
+ "modifiers": {
3323
+ "accessibilityRole": "decorative"
3324
+ }
3325
+ },
3326
+ {
3327
+ "id": "",
3328
+ "body": {
3329
+ "package": "himalaya",
3330
+ "type": "Text",
3331
+ "props": {
3332
+ "text": "$service:himiStoreAccount.restoreLabel",
3333
+ "style": "bodyEmphasis"
3334
+ }
3335
+ }
3336
+ },
3337
+ {
3338
+ "id": "",
3339
+ "body": {
3340
+ "package": "himalaya",
3341
+ "type": "Spacer",
3342
+ "props": {}
3343
+ }
3344
+ }
3345
+ ]
3346
+ }
3347
+ },
3348
+ "modifiers": {
3349
+ "background": {
3350
+ "token": "surfaceElevated"
3351
+ },
3352
+ "border": {
3353
+ "width": 1,
3354
+ "color": {
3355
+ "token": "border"
3356
+ }
3357
+ },
3358
+ "cornerRadius": 8,
3359
+ "padding": {
3360
+ "leading": 14,
3361
+ "trailing": 14,
3362
+ "top": 13,
3363
+ "bottom": 13
3364
+ },
3365
+ "onTap": {
3366
+ "actionId": "tier5.himiStoreAccount.iapRestore"
3367
+ },
3368
+ "pressFeedback": "scale",
3369
+ "haptic": "light",
3370
+ "accessibilityLabel": "$service:himiStoreAccount.restoreLabel",
3371
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3372
+ }
3373
+ },
3374
+ {
3375
+ "id": "himi_account.restore_message",
3376
+ "body": {
3377
+ "package": "himalaya",
3378
+ "type": "Text",
3379
+ "props": {
3380
+ "text": "$service:himiStoreAccount.restoreMessage",
3381
+ "style": "footnote",
3382
+ "color": "textSecondary",
3383
+ "collapseWhenEmpty": true
3384
+ }
3385
+ },
3386
+ "modifiers": {
3387
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3388
+ }
3389
+ },
3303
3390
  {
3304
3391
  "id": "himi_account.addresses",
3305
3392
  "body": {
@@ -3729,6 +3816,109 @@ const SCREENS = runtimeSetting({
3729
3816
  "hiddenPath": "$service:himiStoreAccount.isMember"
3730
3817
  }
3731
3818
  },
3819
+ {
3820
+ "id": "",
3821
+ "body": {
3822
+ "package": "himalaya",
3823
+ "type": "VStack",
3824
+ "props": {
3825
+ "spacing": 8,
3826
+ "alignment": "leading",
3827
+ "children": [
3828
+ {
3829
+ "id": "himi_account.restore_guest",
3830
+ "body": {
3831
+ "package": "himalaya",
3832
+ "type": "HStack",
3833
+ "props": {
3834
+ "spacing": 12,
3835
+ "children": [
3836
+ {
3837
+ "id": "",
3838
+ "body": {
3839
+ "package": "himalaya",
3840
+ "type": "Icon",
3841
+ "props": {
3842
+ "name": "refresh",
3843
+ "size": 18,
3844
+ "tint": "accentPrimary"
3845
+ }
3846
+ },
3847
+ "modifiers": {
3848
+ "accessibilityRole": "decorative"
3849
+ }
3850
+ },
3851
+ {
3852
+ "id": "",
3853
+ "body": {
3854
+ "package": "himalaya",
3855
+ "type": "Text",
3856
+ "props": {
3857
+ "text": "$service:himiStoreAccount.restoreLabel",
3858
+ "style": "bodyEmphasis"
3859
+ }
3860
+ }
3861
+ },
3862
+ {
3863
+ "id": "",
3864
+ "body": {
3865
+ "package": "himalaya",
3866
+ "type": "Spacer",
3867
+ "props": {}
3868
+ }
3869
+ }
3870
+ ]
3871
+ }
3872
+ },
3873
+ "modifiers": {
3874
+ "background": {
3875
+ "token": "surfaceElevated"
3876
+ },
3877
+ "border": {
3878
+ "width": 1,
3879
+ "color": {
3880
+ "token": "border"
3881
+ }
3882
+ },
3883
+ "cornerRadius": 8,
3884
+ "padding": {
3885
+ "leading": 14,
3886
+ "trailing": 14,
3887
+ "top": 13,
3888
+ "bottom": 13
3889
+ },
3890
+ "onTap": {
3891
+ "actionId": "tier5.himiStoreAccount.iapRestore"
3892
+ },
3893
+ "pressFeedback": "scale",
3894
+ "haptic": "light",
3895
+ "accessibilityLabel": "$service:himiStoreAccount.restoreLabel",
3896
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3897
+ }
3898
+ },
3899
+ {
3900
+ "id": "himi_account.restore_guest_message",
3901
+ "body": {
3902
+ "package": "himalaya",
3903
+ "type": "Text",
3904
+ "props": {
3905
+ "text": "$service:himiStoreAccount.restoreMessage",
3906
+ "style": "footnote",
3907
+ "color": "textSecondary",
3908
+ "collapseWhenEmpty": true
3909
+ }
3910
+ },
3911
+ "modifiers": {
3912
+ "hiddenPath": "$service:himiStoreAccount.restoreHidden"
3913
+ }
3914
+ }
3915
+ ]
3916
+ }
3917
+ },
3918
+ "modifiers": {
3919
+ "hiddenPath": "$service:himiStoreAccount.isMember"
3920
+ }
3921
+ },
3732
3922
  {
3733
3923
  "id": "himi_account.language",
3734
3924
  "body": {
@@ -4098,6 +4288,7 @@ const SCREENS = runtimeSetting({
4098
4288
  "actionId": "tier5.himiStoreAccount.refresh",
4099
4289
  "params": {
4100
4290
  "memberAccountLinks": "1",
4291
+ "restorePurchases": "1",
4101
4292
  "copy": "{\"account.subtitle\":\"Orders, bookings, memberships and saved content in one place.\",\"storeAccount.authBody\":\"Sign in to continue — cart, bookings and memberships stay with you.\",\"storeAccount.authBodyRegister\":\"Create a member account to continue — cart, bookings and memberships stay with you.\"}"
4102
4293
  }
4103
4294
  }
@@ -4109,7 +4300,8 @@ const SCREENS = runtimeSetting({
4109
4300
  "contentType": "application/javascript+ts-bundle",
4110
4301
  "declaredCapabilities": [
4111
4302
  "wix_api",
4112
- "app_info"
4303
+ "app_info",
4304
+ "iap"
4113
4305
  ],
4114
4306
  "minBinaryVersion": "0.2.0",
4115
4307
  "bundleVersion": "0.0.1"
@@ -2,9 +2,9 @@
2
2
  // `@himalaya/iap-checkout` → createIapCommerce), shared by every screen that shows or sells an
3
3
  // IAP-backed item. Mapping, Apple prices, payment guards, the host scope and the sale live in the
4
4
  // SDK; this only supplies the copy and the hydration reset.
5
- import type { IapPriceCopy } from "@himalaya/iap";
6
- import type { IapPurchaseCopy } from "@himalaya/iap-checkout";
7
- import { copyParam } from "./himi-copy.js";
5
+ import { hostSessionFromGlobal, type IapPriceCopy } from "@himalaya/iap";
6
+ import type { IapCommerce, IapPurchaseCopy, IapRestoreResult } from "@himalaya/iap-checkout";
7
+ import { copyParam, copyTemplate } from "./himi-copy.js";
8
8
 
9
9
  /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
10
10
  export function iapPriceCopy(prefix: string): IapPriceCopy {
@@ -31,6 +31,87 @@ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
31
31
  };
32
32
  }
33
33
 
34
+ /** The Restore purchases control (App Review 3.1.1) a screen binds. */
35
+ export interface IapRestoreFields {
36
+ restoreHidden: boolean;
37
+ restoreLabel: string;
38
+ restoreDisabled: boolean;
39
+ restoreMessage: string;
40
+ }
41
+
42
+ /** Keep these out of hydration: a restore's answer belongs to the run that got it. */
43
+ export const IAP_RESTORE_KEYS = ["restoreHidden", "restoreLabel", "restoreDisabled", "restoreMessage"] as const;
44
+
45
+ /** The control before the screen knows it sells through Apple. */
46
+ export const NO_IAP_RESTORE: IapRestoreFields = { restoreHidden: true, restoreLabel: "", restoreDisabled: false, restoreMessage: "" };
47
+
48
+ export interface IapRestoreControl {
49
+ /** `sellsThroughApple` is the screen's own signal: its item's App Store route, or Account's
50
+ * purchase readiness. */
51
+ fields(sellsThroughApple: boolean): IapRestoreFields;
52
+ /** Runs the shared `commerce.restore()`, repainting as it starts and when it answers. A tap while
53
+ * one runs is ignored. */
54
+ run(): Promise<void>;
55
+ }
56
+
57
+ /** Copy from the screen's pack (`<prefix>.restorePurchasesCta`, …). The restore itself, and the access
58
+ * re-read after it, are the shared commerce's. */
59
+ export function createIapRestore(
60
+ commerce: Pick<IapCommerce, "restore">,
61
+ options: { prefix: string; repaint(): void; /** The session generation; defaults to the host's. */ generation?: () => number },
62
+ ): IapRestoreControl {
63
+ const generation = options.generation ?? (() => hostSessionFromGlobal().current().generation);
64
+ let running = false;
65
+ let message = "";
66
+ let messageGeneration = -1;
67
+
68
+ function say(text: string, forGeneration: number): void {
69
+ message = text;
70
+ messageGeneration = forGeneration;
71
+ }
72
+
73
+ async function run(): Promise<void> {
74
+ if (running) return;
75
+ running = true;
76
+ const startedFor = generation();
77
+ say(copyParam(`${options.prefix}.restoringText`, "Restoring…"), startedFor);
78
+ options.repaint();
79
+ // Stamped with the session the tap was for: an answer landing after a sign-out is never shown.
80
+ say(restoreMessage(await restored(commerce), options.prefix), startedFor);
81
+ running = false;
82
+ options.repaint();
83
+ }
84
+
85
+ return {
86
+ fields: (sellsThroughApple) => ({
87
+ restoreHidden: !sellsThroughApple,
88
+ restoreLabel: copyParam(`${options.prefix}.restorePurchasesCta`, "Restore purchases"),
89
+ restoreDisabled: running,
90
+ // The answer was for the person signed in when it ran; another session never inherits it.
91
+ restoreMessage: sellsThroughApple && messageGeneration === generation() ? message : "",
92
+ }),
93
+ run,
94
+ };
95
+ }
96
+
97
+ function restored(commerce: Pick<IapCommerce, "restore">): Promise<IapRestoreResult> {
98
+ return commerce.restore().catch(() => ({ status: "failed", code: "storeUnavailable" }) as const);
99
+ }
100
+
101
+ function restoreMessage(result: IapRestoreResult, prefix: string): string {
102
+ if (result.status === "needsSignIn") return copyParam(`${prefix}.restoreSignInText`, "Sign in to restore your purchases.");
103
+ if (result.status === "failed") return result.code === "sessionChanged" ? "" : copyParam(`${prefix}.restoreFailedText`, "Couldn't reach the App Store. Try again.");
104
+ // Every part that happened is said: a recovery never hides a refusal or a purchase still pending.
105
+ const parts = [
106
+ result.recovered === 1 ? copyParam(`${prefix}.restoredOneText`, "Restored 1 purchase.") : "",
107
+ result.recovered > 1 ? copyTemplate(`${prefix}.restoredManyText`, "Restored {count} purchases.", { count: result.recovered }) : "",
108
+ // Held unfinished because Wix refused it (another member's purchase, say).
109
+ result.refused > 0 ? copyParam(`${prefix}.restoreRefusedText`, "A purchase on this Apple ID couldn't be added to this account. Contact support.") : "",
110
+ result.pending > 0 ? copyParam(`${prefix}.restorePendingText`, "A purchase is still being confirmed. Try again in a moment.") : "",
111
+ ].filter(Boolean);
112
+ return parts.length ? parts.join(" ") : copyParam(`${prefix}.restoreUpToDateText`, "Your purchases are up to date.");
113
+ }
114
+
34
115
  /** What `notHydrated` does, for a runtime that ignores the option (a binary older than it, still
35
116
  * served new bundles): put the listed keys back to their declared values before any action runs,
36
117
  * so a persisted price or an enabled purchase action never paints as this run's. */
@@ -14,6 +14,8 @@ import { searchOrders, type WixOrder } from "@himalaya/wix-ecom";
14
14
  import { guestProfile, loadMemberProfile, strOf, fmtDate, titleCase, type AccountActivityRow } from "../_shared/himi-account";
15
15
  import { stashCopy, copyParam, copyTemplate } from "../_shared/himi-copy.js";
16
16
  import { memberAccountRoutes } from "../_shared/member-account-routes.js";
17
+ import { createIapCommerce, onlineProgramOrders, pricingPlanOrders, storesAppleSalesEnabled, storesProductOrders } from "@himalaya/iap-checkout";
18
+ import { IAP_RESTORE_KEYS, NO_IAP_RESTORE, createIapRestore, resetUnhydratedKeysOnOlderRuntime, type IapRestoreFields } from "../_shared/iap-worker.js";
17
19
 
18
20
  interface AuthRouteInput {
19
21
  intent?: string;
@@ -152,7 +154,7 @@ function authHeadline(s: S, isRegistering: boolean, isRecovering: boolean): { au
152
154
  }
153
155
  }
154
156
 
155
- interface S {
157
+ interface S extends IapRestoreFields {
156
158
  status: "loading" | "ready";
157
159
  isMember: boolean;
158
160
  notMember: boolean;
@@ -214,6 +216,27 @@ interface S {
214
216
  noMemberAddresses: boolean;
215
217
  /** Hides My Wallet where this runtime cannot reach saved cards. */
216
218
  noMemberWallet: boolean;
219
+ /** Set once a descriptor asks for the Restore purchases row (appear param
220
+ * `restorePurchases: "1"`). Only then does the worker reach the App Store layer, so a
221
+ * descriptor that renders no such row need not declare `iap`. */
222
+ restorePurchases: boolean;
223
+ }
224
+
225
+ // Restore purchases (App Review 3.1.1) for every vertical the app sells through the App Store. The
226
+ // row shows once purchasing is ready on this host (a bound iOS binary with managed StoreKit), to a
227
+ // guest too: the restore itself asks them to sign in.
228
+ const commerce = createIapCommerce({
229
+ adapters: [onlineProgramOrders, pricingPlanOrders, ...(storesAppleSalesEnabled() ? [storesProductOrders] : [])],
230
+ onChange: () => { void svc.restorePublish(); },
231
+ });
232
+ const restore = createIapRestore(commerce, { prefix: "storeAccount", repaint: () => { void svc.restorePublish(); } });
233
+ let commerceEntered = false;
234
+
235
+ /** The first entry reads readiness and recovers held purchases; later ones reconcile, as on any
236
+ * screen that sells. */
237
+ function enterCommerce(): void {
238
+ void (commerceEntered ? commerce.reconcile() : commerce.start());
239
+ commerceEntered = true;
217
240
  }
218
241
 
219
242
  function derive(s: S): S {
@@ -234,6 +257,7 @@ function derive(s: S): S {
234
257
  hideSignInChoices: isRegistering || isRecovering,
235
258
  hasActivity,
236
259
  noActivity: !hasActivity,
260
+ ...restore.fields(s.restorePurchases && commerce.readiness().status === "ready"),
237
261
  authTitle: headline.authTitle,
238
262
  authBody: headline.authBody,
239
263
  hasReturnPath,
@@ -288,6 +312,8 @@ async function probe(s: S, arg?: unknown): Promise<S> {
288
312
  const intent = authIntentFromArg(arg, s);
289
313
  const profile = await loadMemberProfile(copyParam("storeAccount.guestDetail", "Sign in to keep your cart and orders connected."));
290
314
  const memberAccountLinks = s.memberAccountLinks || strField(arg, "memberAccountLinks") === "1";
315
+ const restorePurchases = s.restorePurchases || strField(arg, "restorePurchases") === "1";
316
+ if (restorePurchases) enterCommerce();
291
317
  const routes = memberAccountLinks ? await memberAccountRoutes() : { addresses: false, wallet: false };
292
318
  let activityRows: AccountActivityRow[] = [];
293
319
  if (profile.isMember) {
@@ -313,6 +339,7 @@ async function probe(s: S, arg?: unknown): Promise<S> {
313
339
  email: profile.memberEmail || s.email,
314
340
  message: "",
315
341
  memberAccountLinks,
342
+ restorePurchases,
316
343
  noMemberAddresses: !routes.addresses,
317
344
  noMemberWallet: !routes.wallet,
318
345
  activityRows,
@@ -365,7 +392,10 @@ const svc = defineService<S, {
365
392
  signIn: (s: S) => Promise<S | undefined>;
366
393
  register: (s: S) => Promise<S | undefined>;
367
394
  signOut: (s: S) => Promise<S>;
395
+ restorePublish: (s: S) => S;
396
+ iapRestore: (s: S) => Promise<undefined>;
368
397
  }>("himiStoreAccount", {
398
+ notHydrated: [...IAP_RESTORE_KEYS],
369
399
  state: derive({
370
400
  status: "loading",
371
401
  isMember: false,
@@ -411,6 +441,8 @@ const svc = defineService<S, {
411
441
  memberAccountLinks: false,
412
442
  noMemberAddresses: true,
413
443
  noMemberWallet: true,
444
+ restorePurchases: false,
445
+ ...NO_IAP_RESTORE,
414
446
  }),
415
447
  actions: {
416
448
  load: (s, arg) => { stashCopy(arg); return probe(s, arg); },
@@ -503,8 +535,11 @@ const svc = defineService<S, {
503
535
  };
504
536
  return derive(cleared);
505
537
  },
538
+ restorePublish: (s) => derive(s),
539
+ iapRestore: async () => { await restore.run(); return undefined; },
506
540
  },
507
541
  });
508
542
 
543
+ resetUnhydratedKeysOnOlderRuntime(svc, { ...svc.getState(), ...NO_IAP_RESTORE }, IAP_RESTORE_KEYS);
509
544
  svc.load();
510
545
  export { svc };
@@ -41,6 +41,17 @@ export default defineApp({
41
41
  designTokensPath: "./tokens.json",
42
42
  navigation: {
43
43
  "navKind": "responsive",
44
+ "surfaces": {
45
+ "watch": {
46
+ "destinations": [
47
+ "home"
48
+ ],
49
+ "navKind": "single",
50
+ "screenIds": {
51
+ "home": "himi_owner_lite_wrist_home"
52
+ }
53
+ }
54
+ },
44
55
  "scheme": "himalaya-__HIMI_APP_SLUG__",
45
56
  "deepLinkPatterns": [],
46
57
  "linkableScreens": [