@wix/himalaya-cli 0.1323.0 → 0.1325.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 (193) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/checkout/src/index.d.ts +6 -10
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/order-refs.d.ts +1 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/purchase-fields.d.ts +1 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/index.d.ts +9 -35
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +79 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +221 -14
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/index.d.ts +0 -2
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/social-feed/src/index.d.ts +38 -21
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/display.d.ts +1 -0
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/checkout.mjs +30 -13
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +20 -2
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +2890 -54
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +80 -0
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +45 -17
  15. package/dist/cli.mjs +5 -4
  16. package/fixtures/wix-canned.json +61 -6
  17. package/guides/iap-pricing.md +9 -0
  18. package/guides/iap-screen-adoption.md +77 -0
  19. package/guides/importable-sdks.md +2 -0
  20. package/guides/index.json +6 -0
  21. package/guides/release-notes.md +25 -0
  22. package/guides/sdk-reference.json +539 -32
  23. package/guides/wix-api-usage.md +5 -0
  24. package/package.json +2 -2
  25. package/templates/beauty-salon/config.ts +1 -1
  26. package/templates/beauty-salon/dev/index.ts +46 -18
  27. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +1 -1
  28. package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +3 -2
  29. package/templates/beauty-salon/tier5-src/_shared/himi-store-collection.ts +1 -1
  30. package/templates/beauty-salon/tier5-src/_shared/home-service.ts +2 -1
  31. package/templates/beauty-salon/tier5-src/_shared/iap-presentation.ts +53 -0
  32. package/templates/beauty-salon/tier5-src/_shared/iap-worker.ts +42 -0
  33. package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  34. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +16 -2
  35. package/templates/beauty-salon/tier5-src/himi-branded-lite-order/index.ts +24 -3
  36. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  37. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +24 -3
  38. package/templates/beauty-salon/tier5-src/himi-store-saved/index.ts +8 -3
  39. package/templates/beauty-salon/tier5-src/himi-store-shop/index.ts +10 -0
  40. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  41. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +1 -1
  42. package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +3 -2
  43. package/templates/beauty-salon-owner/tier5-src/_shared/home-service.ts +2 -1
  44. package/templates/branded-lite/config.ts +1 -8
  45. package/templates/branded-lite/dev/index.ts +46 -134
  46. package/templates/branded-lite/himalaya.content.json +0 -3
  47. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +1 -1
  48. package/templates/branded-lite/tier5-src/_shared/collection-service.ts +3 -2
  49. package/templates/branded-lite/tier5-src/_shared/himi-store-collection.ts +1 -1
  50. package/templates/branded-lite/tier5-src/_shared/home-service.ts +2 -1
  51. package/templates/branded-lite/tier5-src/_shared/iap-presentation.ts +53 -0
  52. package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  53. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +16 -2
  54. package/templates/branded-lite/tier5-src/himi-branded-lite-order/index.ts +24 -3
  55. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  56. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +24 -3
  57. package/templates/branded-lite/tier5-src/himi-store-saved/index.ts +8 -3
  58. package/templates/branded-lite/tier5-src/himi-store-shop/index.ts +10 -0
  59. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  60. package/templates/community-club/config.ts +1 -8
  61. package/templates/community-club/dev/index.ts +3 -117
  62. package/templates/community-club/himalaya.content.json +0 -3
  63. package/templates/community-club/tier5-src/_shared/collection-contract.ts +1 -1
  64. package/templates/community-club/tier5-src/_shared/collection-service.ts +3 -2
  65. package/templates/community-club/tier5-src/_shared/home-service.ts +2 -1
  66. package/templates/community-club/tier5-src/_shared/iap-presentation.ts +53 -0
  67. package/templates/community-club/tier5-src/_shared/iap-worker.ts +42 -0
  68. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +16 -2
  69. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +1 -1
  70. package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +3 -2
  71. package/templates/community-club-owner/tier5-src/_shared/home-service.ts +2 -1
  72. package/templates/events-meetups/config.ts +1 -8
  73. package/templates/events-meetups/dev/index.ts +3 -117
  74. package/templates/events-meetups/himalaya.content.json +0 -3
  75. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +1 -1
  76. package/templates/events-meetups/tier5-src/_shared/collection-service.ts +3 -2
  77. package/templates/events-meetups/tier5-src/_shared/home-service.ts +2 -1
  78. package/templates/events-meetups/tier5-src/_shared/iap-presentation.ts +53 -0
  79. package/templates/events-meetups/tier5-src/_shared/iap-worker.ts +42 -0
  80. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +16 -2
  81. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +1 -1
  82. package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +3 -2
  83. package/templates/events-meetups-owner/tier5-src/_shared/home-service.ts +2 -1
  84. package/templates/gym-fitness/config.ts +1 -1
  85. package/templates/gym-fitness/dev/index.ts +46 -18
  86. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +1 -1
  87. package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +3 -2
  88. package/templates/gym-fitness/tier5-src/_shared/himi-store-collection.ts +1 -1
  89. package/templates/gym-fitness/tier5-src/_shared/home-service.ts +2 -1
  90. package/templates/gym-fitness/tier5-src/_shared/iap-presentation.ts +53 -0
  91. package/templates/gym-fitness/tier5-src/_shared/iap-worker.ts +42 -0
  92. package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  93. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +16 -2
  94. package/templates/gym-fitness/tier5-src/himi-branded-lite-order/index.ts +24 -3
  95. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  96. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +24 -3
  97. package/templates/gym-fitness/tier5-src/himi-store-saved/index.ts +8 -3
  98. package/templates/gym-fitness/tier5-src/himi-store-shop/index.ts +10 -0
  99. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  100. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +1 -1
  101. package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +3 -2
  102. package/templates/gym-fitness-owner/tier5-src/_shared/home-service.ts +2 -1
  103. package/templates/nonprofit-cause/config.ts +1 -8
  104. package/templates/nonprofit-cause/dev/index.ts +29 -129
  105. package/templates/nonprofit-cause/himalaya.content.json +0 -3
  106. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +1 -1
  107. package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +3 -2
  108. package/templates/nonprofit-cause/tier5-src/_shared/home-service.ts +2 -1
  109. package/templates/nonprofit-cause/tier5-src/_shared/iap-presentation.ts +53 -0
  110. package/templates/nonprofit-cause/tier5-src/_shared/iap-worker.ts +42 -0
  111. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +16 -2
  112. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  113. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  114. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +1 -1
  115. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +3 -2
  116. package/templates/nonprofit-cause-owner/tier5-src/_shared/home-service.ts +2 -1
  117. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +1 -1
  118. package/templates/owner-lite/tier5-src/_shared/collection-service.ts +3 -2
  119. package/templates/owner-lite/tier5-src/_shared/home-service.ts +2 -1
  120. package/templates/podcast-creator/config.ts +1 -1
  121. package/templates/podcast-creator/dev/index.ts +29 -13
  122. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +1 -1
  123. package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +3 -2
  124. package/templates/podcast-creator/tier5-src/_shared/home-service.ts +2 -1
  125. package/templates/podcast-creator/tier5-src/_shared/iap-presentation.ts +53 -0
  126. package/templates/podcast-creator/tier5-src/_shared/iap-worker.ts +42 -0
  127. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +16 -2
  128. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  129. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  130. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +1 -1
  131. package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +3 -2
  132. package/templates/podcast-creator-owner/tier5-src/_shared/home-service.ts +2 -1
  133. package/templates/portfolio-creative/config.ts +1 -1
  134. package/templates/portfolio-creative/dev/index.ts +20 -6
  135. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +1 -1
  136. package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +3 -2
  137. package/templates/portfolio-creative/tier5-src/_shared/himi-store-collection.ts +1 -1
  138. package/templates/portfolio-creative/tier5-src/_shared/home-service.ts +2 -1
  139. package/templates/portfolio-creative/tier5-src/_shared/iap-presentation.ts +53 -0
  140. package/templates/portfolio-creative/tier5-src/_shared/iap-worker.ts +42 -0
  141. package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  142. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +16 -2
  143. package/templates/portfolio-creative/tier5-src/himi-branded-lite-order/index.ts +24 -3
  144. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +24 -3
  145. package/templates/portfolio-creative/tier5-src/himi-store-saved/index.ts +8 -3
  146. package/templates/portfolio-creative/tier5-src/himi-store-shop/index.ts +10 -0
  147. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +1 -1
  148. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +3 -2
  149. package/templates/portfolio-creative-owner/tier5-src/_shared/home-service.ts +2 -1
  150. package/templates/restaurant/config.ts +1 -1
  151. package/templates/restaurant/dev/index.ts +3 -1
  152. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +1 -1
  153. package/templates/restaurant/tier5-src/_shared/collection-service.ts +3 -2
  154. package/templates/restaurant/tier5-src/_shared/home-service.ts +2 -1
  155. package/templates/restaurant/tier5-src/_shared/iap-presentation.ts +53 -0
  156. package/templates/restaurant/tier5-src/_shared/iap-worker.ts +42 -0
  157. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +16 -2
  158. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +1 -1
  159. package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +3 -2
  160. package/templates/restaurant-owner/tier5-src/_shared/home-service.ts +2 -1
  161. package/templates/retail-store/config.ts +1 -1
  162. package/templates/retail-store/dev/index.ts +20 -6
  163. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +1 -1
  164. package/templates/retail-store/tier5-src/_shared/collection-service.ts +3 -2
  165. package/templates/retail-store/tier5-src/_shared/himi-store-collection.ts +1 -1
  166. package/templates/retail-store/tier5-src/_shared/home-service.ts +2 -1
  167. package/templates/retail-store/tier5-src/_shared/iap-presentation.ts +53 -0
  168. package/templates/retail-store/tier5-src/_shared/iap-worker.ts +42 -0
  169. package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  170. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +16 -2
  171. package/templates/retail-store/tier5-src/himi-branded-lite-order/index.ts +24 -3
  172. package/templates/retail-store/tier5-src/himi-store-product/index.ts +24 -3
  173. package/templates/retail-store/tier5-src/himi-store-saved/index.ts +8 -3
  174. package/templates/retail-store/tier5-src/himi-store-shop/index.ts +10 -0
  175. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +1 -1
  176. package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +3 -2
  177. package/templates/retail-store-owner/tier5-src/_shared/home-service.ts +2 -1
  178. package/templates/yoga-studio/config.ts +1 -1
  179. package/templates/yoga-studio/dev/index.ts +29 -13
  180. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +1 -1
  181. package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +3 -2
  182. package/templates/yoga-studio/tier5-src/_shared/home-service.ts +2 -1
  183. package/templates/yoga-studio/tier5-src/_shared/iap-presentation.ts +53 -0
  184. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +16 -2
  185. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  186. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  187. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +1 -1
  188. package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +3 -2
  189. package/templates/yoga-studio-owner/tier5-src/_shared/home-service.ts +2 -1
  190. package/templates/branded-lite/tier5-src/wix-spaces-create-group/index.ts +0 -29
  191. package/templates/community-club/tier5-src/wix-spaces-create-group/index.ts +0 -29
  192. package/templates/events-meetups/tier5-src/wix-spaces-create-group/index.ts +0 -29
  193. package/templates/nonprofit-cause/tier5-src/wix-spaces-create-group/index.ts +0 -29
@@ -2005,7 +2005,9 @@ const SCREENS = runtimeSetting({
2005
2005
  "integrity": "",
2006
2006
  "contentType": "application/javascript+ts-bundle",
2007
2007
  "declaredCapabilities": [
2008
- "wix_api"
2008
+ "wix_api",
2009
+ "iap",
2010
+ "app_info"
2009
2011
  ],
2010
2012
  "minBinaryVersion": "0.2.0",
2011
2013
  "bundleVersion": "0.0.1"
@@ -12328,25 +12330,6 @@ const SCREENS = runtimeSetting({
12328
12330
  }
12329
12331
  }
12330
12332
  },
12331
- {
12332
- "id": "himi_groups.new_group",
12333
- "body": {
12334
- "package": "himalaya",
12335
- "type": "Button",
12336
- "props": {
12337
- "title": "New Group",
12338
- "style": "secondary",
12339
- "fullWidth": true,
12340
- "leadingIcon": "plus",
12341
- "action": {
12342
- "actionId": "himi.open",
12343
- "params": {
12344
- "screenId": "himi_create_group"
12345
- }
12346
- }
12347
- }
12348
- }
12349
- },
12350
12333
  {
12351
12334
  "id": "himi_groups.search",
12352
12335
  "body": {
@@ -14727,103 +14710,6 @@ const SCREENS = runtimeSetting({
14727
14710
  "bundleVersion": "0.0.1"
14728
14711
  }
14729
14712
  },
14730
- "himi_create_group": {
14731
- "screenId": "himi_create_group",
14732
- "renderer": "sdui_intent",
14733
- "title": "",
14734
- "schemaVersion": 1,
14735
- "cacheControl": {
14736
- "maxAge": 0,
14737
- "staleWhileRevalidate": 0
14738
- },
14739
- "payload": {
14740
- "id": "himi_create_group.root",
14741
- "body": {
14742
- "package": "himalaya",
14743
- "type": "VStack",
14744
- "props": {
14745
- "spacing": 16,
14746
- "alignment": "leading",
14747
- "children": [
14748
- {
14749
- "id": "himi_create_group.title",
14750
- "body": {
14751
- "package": "himalaya",
14752
- "type": "Text",
14753
- "props": {
14754
- "text": "New Group",
14755
- "style": "title1"
14756
- }
14757
- }
14758
- },
14759
- {
14760
- "id": "",
14761
- "body": {
14762
- "package": "himalaya",
14763
- "type": "Text",
14764
- "props": {
14765
- "text": "$service:spacesCreateGroup.hint",
14766
- "style": "footnote",
14767
- "color": "textSecondary",
14768
- "collapseWhenEmpty": true
14769
- }
14770
- }
14771
- },
14772
- {
14773
- "id": "himi_create_group.name_field",
14774
- "body": {
14775
- "package": "himalaya",
14776
- "type": "TextField",
14777
- "props": {
14778
- "placeholder": "Group name",
14779
- "value": "",
14780
- "onChange": {
14781
- "actionId": "tier5.spacesCreateGroup.setName"
14782
- }
14783
- }
14784
- }
14785
- },
14786
- {
14787
- "id": "himi_create_group.submit",
14788
- "body": {
14789
- "package": "himalaya",
14790
- "type": "Button",
14791
- "props": {
14792
- "title": "Create",
14793
- "style": "primary",
14794
- "fullWidth": true,
14795
- "action": {
14796
- "actionId": "tier5.spacesCreateGroup.submit"
14797
- }
14798
- }
14799
- }
14800
- }
14801
- ]
14802
- }
14803
- },
14804
- "modifiers": {
14805
- "background": {
14806
- "token": "surfacePrimary"
14807
- },
14808
- "padding": {
14809
- "leading": 16,
14810
- "trailing": 16,
14811
- "top": 18,
14812
- "bottom": 28
14813
- }
14814
- }
14815
- },
14816
- "logicBundle": {
14817
- "url": "http://localhost:8190/v1/bundles/wix-spaces-create-group?integrity=",
14818
- "integrity": "",
14819
- "contentType": "application/javascript+ts-bundle",
14820
- "declaredCapabilities": [
14821
- "wix_api"
14822
- ],
14823
- "minBinaryVersion": "0.1.0",
14824
- "bundleVersion": "0.0.1"
14825
- }
14826
- },
14827
14713
  "himi_cms": {
14828
14714
  "screenId": "himi_cms",
14829
14715
  "renderer": "sdui_intent",
@@ -28,9 +28,6 @@
28
28
  "himi_cms_collection": {
29
29
  "displayName": "CMS Collection"
30
30
  },
31
- "himi_create_group": {
32
- "displayName": "Create Group"
33
- },
34
31
  "himi_create_post": {
35
32
  "displayName": "Create Post"
36
33
  },
@@ -87,7 +87,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
87
87
  himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
88
88
  searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
89
89
  sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
90
- himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
90
+ himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3', 'joinNote'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
91
91
  himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
92
92
  himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
93
93
  himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
@@ -323,6 +323,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
323
323
  /** Domain-owned page source with shared query controls. Publishing uses a normal
324
324
  * reducer and reads the private controller snapshot, never an SDUI payload. */
325
325
  export function defineBackendCollection<S extends object, Row extends object>(name: string, options: {
326
+ register?: typeof register;
326
327
  /** Revalidate already opened pages on navigation return; explicit refresh resets. */
327
328
  restorePagesOnLoad?: boolean;
328
329
  /** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
@@ -342,7 +343,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
342
343
  controller: controller as unknown as BackendCollection['controller'],
343
344
  apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
344
345
  });
345
- const handle = register(name, definition);
346
+ const handle = (options.register ?? register)(name, definition);
346
347
  publish = () => handle.collectionDataChanged();
347
348
  return handle;
348
349
  }
@@ -350,7 +351,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
350
351
  /** Keep the domain service's existing TypeScript API. Extra collection verbs are
351
352
  * dispatched through SDUI and do not widen the native capability surface. */
352
353
  export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
353
- register(name, decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }))) as typeof register;
354
+ register(name, { ...def, ...decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }) } as ServiceDefinition<State>)) as typeof register;
354
355
 
355
356
  /** Domains can supply their existing saved-items store without putting domain
356
357
  * data or mutations in the shared collection layer. */
@@ -87,4 +87,5 @@ export function decorateHome(def: { state: State; actions?: Record<string, Actio
87
87
  actions.homeMoveDown = change(id => { const index = prefs.order.indexOf(id); if (index >= 0 && index < prefs.order.length - 1) [prefs.order[index], prefs.order[index + 1]] = [prefs.order[index + 1], prefs.order[index]]; });
88
88
  return { state: derive(def.state), actions };
89
89
  }
90
- export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) => register(name, decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }))) as typeof register;
90
+ export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
91
+ register(name, { ...def, ...decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }) })) as typeof register;
@@ -0,0 +1,53 @@
1
+ import { defineService, type ServiceDefinition } from "@himalaya/state";
2
+ import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
3
+ import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
4
+ import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
5
+
6
+ // Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
7
+ export function withIapPresentation<S extends object>(options: {
8
+ register?: typeof defineService;
9
+ onCreate?(commerce: IapCommerce): void;
10
+ onContent?(state: S): void;
11
+ items(state: S): IapCatalogItem[];
12
+ present(state: S, price: (id: string, commercePrice?: string) => string, allowed: (id: string) => boolean): S;
13
+ contentActions: string[];
14
+ transientKeys: (keyof S & string)[];
15
+ prefix: string;
16
+ }): typeof defineService {
17
+ return ((name: string, definition: ServiceDefinition<S>) => {
18
+ const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
19
+ options.onCreate?.(commerce);
20
+ let items: IapCatalogItem[] = [];
21
+ let contentVersion = 0;
22
+ const project = (state: S): S => {
23
+ const price = (id: string, commercePrice?: string) => {
24
+ const item = items.find(item => item.reference.id === id);
25
+ if (!item) return "";
26
+ const view = commerce.pricing.view(item.reference);
27
+ return view.route === "disabled" || view.route === "regular" ? commercePrice ?? item.commerceDisplayPrice ?? "" : iapPriceFields(view, iapPriceCopy(options.prefix)).priceText;
28
+ };
29
+ const allowed = (id: string) => commerce.pricing.checkoutGuard({ kind: "storesProduct", id }).allowed;
30
+ const poll = commerce.pollDirective(`${name}.iapPoll`);
31
+ return { ...options.present(state, price, allowed), ...(poll ? { _poll: poll } : {}) };
32
+ };
33
+ const actions = Object.fromEntries(Object.entries(definition.actions ?? {}).map(([action, reduce]) => [action, async (state: S, ...args: never[]) => {
34
+ const content = options.contentActions.includes(action);
35
+ const version = action === "load" ? ++contentVersion : contentVersion;
36
+ const next = await reduce(state, ...args);
37
+ if (!next || (content && version !== contentVersion)) return undefined;
38
+ if (content) {
39
+ options.onContent?.(next);
40
+ items = options.items(next);
41
+ void commerce.loadPrices(items);
42
+ }
43
+ return project(next);
44
+ }]));
45
+ const service = (options.register ?? defineService)(name, {
46
+ ...definition, notHydrated: [...(definition.notHydrated ?? []), ...options.transientKeys],
47
+ actions: { ...actions, iapPublish: project, iapPoll: async () => { await commerce.poll(); return undefined; },
48
+ iapRefresh: async () => { await commerce.loadPrices(items); return undefined; } },
49
+ });
50
+ resetUnhydratedKeysOnOlderRuntime(service, definition.state, options.transientKeys);
51
+ return service;
52
+ }) as unknown as typeof defineService;
53
+ }
@@ -0,0 +1,42 @@
1
+ // Worker-side glue for the shared IAP layer (`@himalaya/iap` catalog pricing and
2
+ // `@himalaya/iap-checkout` → createIapCommerce), shared by every screen that shows or sells an
3
+ // IAP-backed item. Mapping, Apple prices, payment guards, the host scope and the sale live in the
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";
8
+
9
+ /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
10
+ export function iapPriceCopy(prefix: string): IapPriceCopy {
11
+ return {
12
+ loading: copyParam(`${prefix}.priceLoadingText`, "Loading price…"),
13
+ unavailable: copyParam(`${prefix}.priceUnavailableText`, "Price unavailable"),
14
+ oneTimePurchase: copyParam(`${prefix}.oneTimePurchaseLabel`, "One-time purchase"),
15
+ purchaseUnavailable: copyParam(`${prefix}.purchaseUnavailableText`, "Purchasing is not available yet."),
16
+ };
17
+ }
18
+
19
+ /** App Store purchase labels from the screen's copy pack (`<prefix>.buyCta`, …), for
20
+ * `iapPurchaseFields`. Customer copy only: no backend names, ids or diagnostics. */
21
+ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
22
+ return {
23
+ buy: copyParam(`${prefix}.buyCta`, "Get access"),
24
+ processing: copyParam(`${prefix}.purchaseProcessingText`, "Processing your purchase…"),
25
+ awaitingApproval: copyParam(`${prefix}.purchaseAwaitingApprovalText`, "Waiting for approval"),
26
+ unavailable: copyParam(`${prefix}.purchaseTemporarilyUnavailableText`, "Purchasing is temporarily unavailable."),
27
+ signInRequired: copyParam(`${prefix}.purchaseSignInText`, "Sign in to buy."),
28
+ retry: copyParam(`${prefix}.purchaseRetryText`, "Something went wrong. Try again."),
29
+ priceChanged: copyParam(`${prefix}.purchasePriceChangedText`, "The price changed. Check it and try again."),
30
+ support: copyParam(`${prefix}.purchaseSupportText`, "We couldn't finish this purchase. Contact support."),
31
+ };
32
+ }
33
+
34
+ /** What `notHydrated` does, for a runtime that ignores the option (a binary older than it, still
35
+ * served new bundles): put the listed keys back to their declared values before any action runs,
36
+ * so a persisted price or an enabled purchase action never paints as this run's. */
37
+ export function resetUnhydratedKeysOnOlderRuntime<S extends object>(svc: { getState(): S }, declared: S, keys: readonly (keyof S & string)[]): void {
38
+ if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydrationOptOut === true) return;
39
+ const booted = svc.getState();
40
+ if (!booted || typeof booted !== "object") return;
41
+ for (const key of keys) booted[key] = declared[key];
42
+ }
@@ -1,6 +1,7 @@
1
1
  import { fetchMemberBookings, type MemberBookingRow } from "../_shared/member-bookings.js";
2
2
  import { memberAccess, assertMemberAccess } from "../_shared/member-access.js";
3
- import { defineService } from "../_shared/home-service.js";
3
+ import { defineService as registerHome } from "../_shared/home-service.js";
4
+ import { withIapPresentation } from "../_shared/iap-presentation.js";
4
5
  import { queryServices, type WixService } from "@himalaya/wix-bookings";
5
6
  import { getPost, queryPosts, type WixPost } from "@himalaya/wix-blog";
6
7
  import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
@@ -68,6 +69,7 @@ interface ProductFeature {
68
69
  /** Device-local catalog saves are useful to visitors too; they are never
69
70
  * presented as account-synced data or mixed with member-only bookings. */
70
71
  interface SavedProductCard {
72
+ commercePrice: string;
71
73
  id: string;
72
74
  name: string;
73
75
  price: string;
@@ -573,7 +575,7 @@ function postCard(post: WixPost): PostCard {
573
575
  function savedProductCard(product: SavedProduct): SavedProductCard {
574
576
  const name = cleanTitle(product.name) || copyParam("home.savedProductFallbackName", "Saved item");
575
577
  const image = product.image ? wixMediaUrl(product.image) : "";
576
- return { id: product.id, name, price: product.price, image, initial: initials(name), hasImage: !!image, noImage: !image, detailScreenId: "himi_product" };
578
+ return { id: product.id, name, price: product.price, commercePrice: product.price, image, initial: initials(name), hasImage: !!image, noImage: !image, detailScreenId: "himi_product" };
577
579
  }
578
580
 
579
581
  function savedPostCard(post: WixPost): SavedPostCard {
@@ -1111,6 +1113,18 @@ async function refresh(state: State, arg?: unknown): Promise<State | undefined>
1111
1113
  weeklyClassCount: current.weeklyClassCount, weeklyClassGoalFraction: current.weeklyClassGoalFraction, weeklyGoalLabel: current.weeklyGoalLabel });
1112
1114
  }
1113
1115
 
1116
+ const defineService = withIapPresentation<State>({
1117
+ register: registerHome, prefix: "home", transientKeys: ["featuredProducts", "savedProducts", "heroPrice"], contentActions: ["refresh"],
1118
+ items: state => [
1119
+ ...state.featuredProducts.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.price })),
1120
+ ...state.savedProducts.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.commercePrice })),
1121
+ ],
1122
+ present: (state, price) => derive({ ...state,
1123
+ featuredProducts: state.featuredProducts.map(row => ({ ...row, price: price(row.id) })),
1124
+ savedProducts: state.savedProducts.map(row => ({ ...row, price: price(row.id, row.commercePrice) })),
1125
+ }),
1126
+ });
1127
+
1114
1128
  const svc = defineService<State, {
1115
1129
  // `State | undefined`, matching the implementation: `refresh` returns undefined on a
1116
1130
  // STALE generation (`if (gen !== refreshGen) return undefined`) so the runtime keeps
@@ -87,7 +87,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
87
87
  himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
88
88
  searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
89
89
  sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
90
- himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
90
+ himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3', 'joinNote'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
91
91
  himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
92
92
  himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
93
93
  himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
@@ -323,6 +323,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
323
323
  /** Domain-owned page source with shared query controls. Publishing uses a normal
324
324
  * reducer and reads the private controller snapshot, never an SDUI payload. */
325
325
  export function defineBackendCollection<S extends object, Row extends object>(name: string, options: {
326
+ register?: typeof register;
326
327
  /** Revalidate already opened pages on navigation return; explicit refresh resets. */
327
328
  restorePagesOnLoad?: boolean;
328
329
  /** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
@@ -342,7 +343,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
342
343
  controller: controller as unknown as BackendCollection['controller'],
343
344
  apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
344
345
  });
345
- const handle = register(name, definition);
346
+ const handle = (options.register ?? register)(name, definition);
346
347
  publish = () => handle.collectionDataChanged();
347
348
  return handle;
348
349
  }
@@ -350,7 +351,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
350
351
  /** Keep the domain service's existing TypeScript API. Extra collection verbs are
351
352
  * dispatched through SDUI and do not widen the native capability surface. */
352
353
  export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
353
- register(name, decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }))) as typeof register;
354
+ register(name, { ...def, ...decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }) } as ServiceDefinition<State>)) as typeof register;
354
355
 
355
356
  /** Domains can supply their existing saved-items store without putting domain
356
357
  * data or mutations in the shared collection layer. */
@@ -87,4 +87,5 @@ export function decorateHome(def: { state: State; actions?: Record<string, Actio
87
87
  actions.homeMoveDown = change(id => { const index = prefs.order.indexOf(id); if (index >= 0 && index < prefs.order.length - 1) [prefs.order[index], prefs.order[index + 1]] = [prefs.order[index + 1], prefs.order[index]]; });
88
88
  return { state: derive(def.state), actions };
89
89
  }
90
- export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) => register(name, decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }))) as typeof register;
90
+ export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
91
+ register(name, { ...def, ...decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }) })) as typeof register;
@@ -37,7 +37,7 @@ export default defineApp({
37
37
  actions: "http://localhost:8190/v1/actions",
38
38
  sync: "http://localhost:8190/v1/sync",
39
39
  },
40
- capabilities: ["live_activity","push","share","secure_storage","open_url","calendar","local_database","wix_api","wix_platform","app_info","release_control","clipboard"],
40
+ capabilities: ["live_activity","push","share","secure_storage","open_url","calendar","local_database","wix_api","wix_platform","app_info","iap","release_control","clipboard"],
41
41
  designTokensPath: "./tokens.json",
42
42
  navigation: {
43
43
  "navKind": "responsive",
@@ -178,13 +178,6 @@ export default defineApp({
178
178
  "parentScreenId": "himi_groups",
179
179
  "tabId": "groups"
180
180
  },
181
- {
182
- "screenId": "himi_create_group",
183
- "isPublic": false,
184
- "paramKeys": [],
185
- "parentScreenId": "himi_groups",
186
- "tabId": "groups"
187
- },
188
181
  {
189
182
  "screenId": "himi_account",
190
183
  "isPublic": true,
@@ -2189,7 +2189,9 @@ const SCREENS = runtimeSetting({
2189
2189
  "integrity": "",
2190
2190
  "contentType": "application/javascript+ts-bundle",
2191
2191
  "declaredCapabilities": [
2192
- "wix_api"
2192
+ "wix_api",
2193
+ "iap",
2194
+ "app_info"
2193
2195
  ],
2194
2196
  "minBinaryVersion": "0.2.0",
2195
2197
  "bundleVersion": "0.0.1"
@@ -12512,25 +12514,6 @@ const SCREENS = runtimeSetting({
12512
12514
  }
12513
12515
  }
12514
12516
  },
12515
- {
12516
- "id": "himi_groups.new_group",
12517
- "body": {
12518
- "package": "himalaya",
12519
- "type": "Button",
12520
- "props": {
12521
- "title": "New Group",
12522
- "style": "secondary",
12523
- "fullWidth": true,
12524
- "leadingIcon": "plus",
12525
- "action": {
12526
- "actionId": "himi.open",
12527
- "params": {
12528
- "screenId": "himi_create_group"
12529
- }
12530
- }
12531
- }
12532
- }
12533
- },
12534
12517
  {
12535
12518
  "id": "himi_groups.search",
12536
12519
  "body": {
@@ -14911,103 +14894,6 @@ const SCREENS = runtimeSetting({
14911
14894
  "bundleVersion": "0.0.1"
14912
14895
  }
14913
14896
  },
14914
- "himi_create_group": {
14915
- "screenId": "himi_create_group",
14916
- "renderer": "sdui_intent",
14917
- "title": "",
14918
- "schemaVersion": 1,
14919
- "cacheControl": {
14920
- "maxAge": 0,
14921
- "staleWhileRevalidate": 0
14922
- },
14923
- "payload": {
14924
- "id": "himi_create_group.root",
14925
- "body": {
14926
- "package": "himalaya",
14927
- "type": "VStack",
14928
- "props": {
14929
- "spacing": 16,
14930
- "alignment": "leading",
14931
- "children": [
14932
- {
14933
- "id": "himi_create_group.title",
14934
- "body": {
14935
- "package": "himalaya",
14936
- "type": "Text",
14937
- "props": {
14938
- "text": "New Group",
14939
- "style": "title1"
14940
- }
14941
- }
14942
- },
14943
- {
14944
- "id": "",
14945
- "body": {
14946
- "package": "himalaya",
14947
- "type": "Text",
14948
- "props": {
14949
- "text": "$service:spacesCreateGroup.hint",
14950
- "style": "footnote",
14951
- "color": "textSecondary",
14952
- "collapseWhenEmpty": true
14953
- }
14954
- }
14955
- },
14956
- {
14957
- "id": "himi_create_group.name_field",
14958
- "body": {
14959
- "package": "himalaya",
14960
- "type": "TextField",
14961
- "props": {
14962
- "placeholder": "Group name",
14963
- "value": "",
14964
- "onChange": {
14965
- "actionId": "tier5.spacesCreateGroup.setName"
14966
- }
14967
- }
14968
- }
14969
- },
14970
- {
14971
- "id": "himi_create_group.submit",
14972
- "body": {
14973
- "package": "himalaya",
14974
- "type": "Button",
14975
- "props": {
14976
- "title": "Create",
14977
- "style": "primary",
14978
- "fullWidth": true,
14979
- "action": {
14980
- "actionId": "tier5.spacesCreateGroup.submit"
14981
- }
14982
- }
14983
- }
14984
- }
14985
- ]
14986
- }
14987
- },
14988
- "modifiers": {
14989
- "background": {
14990
- "token": "surfacePrimary"
14991
- },
14992
- "padding": {
14993
- "leading": 16,
14994
- "trailing": 16,
14995
- "top": 18,
14996
- "bottom": 28
14997
- }
14998
- }
14999
- },
15000
- "logicBundle": {
15001
- "url": "http://localhost:8190/v1/bundles/wix-spaces-create-group?integrity=",
15002
- "integrity": "",
15003
- "contentType": "application/javascript+ts-bundle",
15004
- "declaredCapabilities": [
15005
- "wix_api"
15006
- ],
15007
- "minBinaryVersion": "0.1.0",
15008
- "bundleVersion": "0.0.1"
15009
- }
15010
- },
15011
14897
  }) as Record<string, unknown>;
15012
14898
 
15013
14899
  // The language destination stays registered for deep links and future translations. Keep its
@@ -22,9 +22,6 @@
22
22
  "himi_branded_home": {
23
23
  "displayName": "Home"
24
24
  },
25
- "himi_create_group": {
26
- "displayName": "Create Group"
27
- },
28
25
  "himi_create_post": {
29
26
  "displayName": "Create Post"
30
27
  },
@@ -87,7 +87,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
87
87
  himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
88
88
  searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
89
89
  sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
90
- himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
90
+ himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3', 'joinNote'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
91
91
  himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
92
92
  himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
93
93
  himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
@@ -323,6 +323,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
323
323
  /** Domain-owned page source with shared query controls. Publishing uses a normal
324
324
  * reducer and reads the private controller snapshot, never an SDUI payload. */
325
325
  export function defineBackendCollection<S extends object, Row extends object>(name: string, options: {
326
+ register?: typeof register;
326
327
  /** Revalidate already opened pages on navigation return; explicit refresh resets. */
327
328
  restorePagesOnLoad?: boolean;
328
329
  /** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
@@ -342,7 +343,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
342
343
  controller: controller as unknown as BackendCollection['controller'],
343
344
  apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
344
345
  });
345
- const handle = register(name, definition);
346
+ const handle = (options.register ?? register)(name, definition);
346
347
  publish = () => handle.collectionDataChanged();
347
348
  return handle;
348
349
  }
@@ -350,7 +351,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
350
351
  /** Keep the domain service's existing TypeScript API. Extra collection verbs are
351
352
  * dispatched through SDUI and do not widen the native capability surface. */
352
353
  export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
353
- register(name, decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }))) as typeof register;
354
+ register(name, { ...def, ...decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }) } as ServiceDefinition<State>)) as typeof register;
354
355
 
355
356
  /** Domains can supply their existing saved-items store without putting domain
356
357
  * data or mutations in the shared collection layer. */
@@ -87,4 +87,5 @@ export function decorateHome(def: { state: State; actions?: Record<string, Actio
87
87
  actions.homeMoveDown = change(id => { const index = prefs.order.indexOf(id); if (index >= 0 && index < prefs.order.length - 1) [prefs.order[index], prefs.order[index + 1]] = [prefs.order[index + 1], prefs.order[index]]; });
88
88
  return { state: derive(def.state), actions };
89
89
  }
90
- export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) => register(name, decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }))) as typeof register;
90
+ export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
91
+ register(name, { ...def, ...decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }) })) as typeof register;