@wix/himalaya-cli 0.1583.0 → 0.1585.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 (313) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-module.d.ts +7 -4
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/bindings.d.ts +7 -6
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/extensions.d.ts +2 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +2 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +3 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +15 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/extensions.d.ts +13 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +19 -4
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +2 -2
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +8 -2
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +3 -0
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +4 -2
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +5 -0
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +4 -0
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +10 -3
  16. package/dist/cli.mjs +2201 -1716
  17. package/dist/preview-assets.generated.bin +0 -0
  18. package/fixtures/wix-canned.json +25 -0
  19. package/guides/branded-lite-site-binding.md +7 -1
  20. package/guides/designing-himi-screens.md +47 -7
  21. package/guides/himi-skill.md +16 -0
  22. package/guides/index.json +1 -0
  23. package/guides/one-app-to-himi-app.md +52 -1
  24. package/guides/sdk-reference.json +37 -9
  25. package/guides/tier-5-add-a-screen.md +9 -0
  26. package/guides/wix-api-usage.md +9 -0
  27. package/package.json +2 -2
  28. package/templates/beauty-salon/config.ts +7 -0
  29. package/templates/beauty-salon/dev/index.ts +758 -352
  30. package/templates/beauty-salon/himalaya.content.json +3 -0
  31. package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  32. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +19 -3
  33. package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +2 -1
  34. package/templates/beauty-salon/tier5-src/_shared/owner-editor-value.ts +164 -0
  35. package/templates/beauty-salon/tier5-src/_shared/store-product-info.ts +50 -0
  36. package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +21 -8
  37. package/templates/beauty-salon/tier5-src/himi-branded-lite-category/index.ts +3 -2
  38. package/templates/beauty-salon/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  39. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  40. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +14 -3
  41. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  42. package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  43. package/templates/beauty-salon-owner/dev/index.ts +129 -66
  44. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +19 -3
  45. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  46. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  47. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  48. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  49. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  50. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  51. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  52. package/templates/branded-lite/config.ts +9 -0
  53. package/templates/branded-lite/dev/index.ts +4988 -3304
  54. package/templates/branded-lite/himalaya.content.json +3 -0
  55. package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  56. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +19 -3
  57. package/templates/branded-lite/tier5-src/_shared/event-registration.ts +86 -0
  58. package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +2 -1
  59. package/templates/branded-lite/tier5-src/_shared/owner-editor-value.ts +164 -0
  60. package/templates/branded-lite/tier5-src/_shared/store-product-info.ts +50 -0
  61. package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +21 -8
  62. package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +2 -1
  63. package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -2
  64. package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  65. package/templates/branded-lite/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  66. package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +115 -49
  67. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  68. package/templates/branded-lite/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  69. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  70. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  71. package/templates/branded-lite/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  72. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -11
  73. package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +2 -1
  74. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +12 -86
  75. package/templates/branded-lite/tier5-src/himi-events-list/index.ts +47 -6
  76. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +13 -3
  77. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +3 -2
  78. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +53 -37
  79. package/templates/branded-lite/tier5-src/wix-spaces-members/index.ts +100 -34
  80. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  81. package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  82. package/templates/community-club/config.ts +16 -0
  83. package/templates/community-club/dev/index.ts +1930 -401
  84. package/templates/community-club/himalaya.content.json +6 -0
  85. package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  86. package/templates/community-club/tier5-src/_shared/collection-contract.ts +19 -3
  87. package/templates/community-club/tier5-src/_shared/event-registration.ts +86 -0
  88. package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +2 -1
  89. package/templates/community-club/tier5-src/himi-blog-post/index.ts +21 -8
  90. package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  91. package/templates/community-club/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  92. package/templates/community-club/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  93. package/templates/community-club/tier5-src/himi-events-detail/index.ts +12 -86
  94. package/templates/community-club/tier5-src/himi-events-list/index.ts +47 -6
  95. package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  96. package/templates/community-club-owner/dev/index.ts +53 -1
  97. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +19 -3
  98. package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  99. package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  100. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  101. package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  102. package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  103. package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  104. package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  105. package/templates/events-meetups/config.ts +16 -0
  106. package/templates/events-meetups/dev/index.ts +1930 -401
  107. package/templates/events-meetups/himalaya.content.json +6 -0
  108. package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  109. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +19 -3
  110. package/templates/events-meetups/tier5-src/_shared/event-registration.ts +86 -0
  111. package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +2 -1
  112. package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +21 -8
  113. package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  114. package/templates/events-meetups/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  115. package/templates/events-meetups/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  116. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +12 -86
  117. package/templates/events-meetups/tier5-src/himi-events-list/index.ts +47 -6
  118. package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  119. package/templates/events-meetups-owner/dev/index.ts +6 -0
  120. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +19 -3
  121. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  122. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  123. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  124. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  125. package/templates/gym-fitness/config.ts +7 -0
  126. package/templates/gym-fitness/dev/index.ts +1115 -278
  127. package/templates/gym-fitness/himalaya.content.json +3 -0
  128. package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  129. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +19 -3
  130. package/templates/gym-fitness/tier5-src/_shared/event-registration.ts +86 -0
  131. package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +2 -1
  132. package/templates/gym-fitness/tier5-src/_shared/owner-editor-value.ts +164 -0
  133. package/templates/gym-fitness/tier5-src/_shared/store-product-info.ts +50 -0
  134. package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +21 -8
  135. package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -2
  136. package/templates/gym-fitness/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  137. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  138. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +12 -86
  139. package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +47 -6
  140. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +14 -3
  141. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  142. package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  143. package/templates/gym-fitness-owner/dev/index.ts +132 -66
  144. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +19 -3
  145. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  146. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  147. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  148. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  149. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  150. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  151. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  152. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  153. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  154. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  155. package/templates/nonprofit-cause/config.ts +16 -0
  156. package/templates/nonprofit-cause/dev/index.ts +1925 -396
  157. package/templates/nonprofit-cause/himalaya.content.json +6 -0
  158. package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  159. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +19 -3
  160. package/templates/nonprofit-cause/tier5-src/_shared/event-registration.ts +86 -0
  161. package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +2 -1
  162. package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +21 -8
  163. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  164. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  165. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  166. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  167. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +12 -86
  168. package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +47 -6
  169. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  170. package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  171. package/templates/nonprofit-cause-owner/dev/index.ts +85 -65
  172. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +19 -3
  173. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  174. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  175. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  176. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  177. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  178. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  179. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  180. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  181. package/templates/owner-lite/dev/index.ts +132 -66
  182. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +19 -3
  183. package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +16 -8
  184. package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  185. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +4 -2
  186. package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +7 -0
  187. package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +39 -6
  188. package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  189. package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +7 -4
  190. package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +38 -7
  191. package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  192. package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +4 -2
  193. package/templates/podcast-creator/config.ts +7 -0
  194. package/templates/podcast-creator/dev/index.ts +1051 -158
  195. package/templates/podcast-creator/himalaya.content.json +3 -0
  196. package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  197. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +19 -3
  198. package/templates/podcast-creator/tier5-src/_shared/event-registration.ts +86 -0
  199. package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +2 -1
  200. package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +21 -8
  201. package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  202. package/templates/podcast-creator/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  203. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  204. package/templates/podcast-creator/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  205. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +12 -86
  206. package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +47 -6
  207. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  208. package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  209. package/templates/podcast-creator-owner/dev/index.ts +53 -1
  210. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +19 -3
  211. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  212. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  213. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  214. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  215. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  216. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  217. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  218. package/templates/portfolio-creative/config.ts +7 -0
  219. package/templates/portfolio-creative/dev/index.ts +1305 -468
  220. package/templates/portfolio-creative/himalaya.content.json +3 -0
  221. package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  222. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +19 -3
  223. package/templates/portfolio-creative/tier5-src/_shared/event-registration.ts +86 -0
  224. package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +2 -1
  225. package/templates/portfolio-creative/tier5-src/_shared/owner-editor-value.ts +164 -0
  226. package/templates/portfolio-creative/tier5-src/_shared/store-product-info.ts +50 -0
  227. package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +21 -8
  228. package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -2
  229. package/templates/portfolio-creative/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  230. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  231. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  232. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +12 -86
  233. package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +47 -6
  234. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +14 -3
  235. package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  236. package/templates/portfolio-creative-owner/dev/index.ts +129 -66
  237. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +19 -3
  238. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  239. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  240. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  241. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  242. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  243. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  244. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  245. package/templates/restaurant/config.ts +7 -0
  246. package/templates/restaurant/dev/index.ts +1051 -158
  247. package/templates/restaurant/himalaya.content.json +3 -0
  248. package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  249. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +19 -3
  250. package/templates/restaurant/tier5-src/_shared/event-registration.ts +86 -0
  251. package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +2 -1
  252. package/templates/restaurant/tier5-src/himi-blog-post/index.ts +21 -8
  253. package/templates/restaurant/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  254. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +12 -86
  255. package/templates/restaurant/tier5-src/himi-events-list/index.ts +47 -6
  256. package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  257. package/templates/restaurant-owner/dev/index.ts +132 -66
  258. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +19 -3
  259. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  260. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  261. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  262. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  263. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  264. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  265. package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  266. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  267. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  268. package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  269. package/templates/retail-store/config.ts +7 -0
  270. package/templates/retail-store/dev/index.ts +597 -191
  271. package/templates/retail-store/himalaya.content.json +3 -0
  272. package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  273. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +19 -3
  274. package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +2 -1
  275. package/templates/retail-store/tier5-src/_shared/owner-editor-value.ts +164 -0
  276. package/templates/retail-store/tier5-src/_shared/store-product-info.ts +50 -0
  277. package/templates/retail-store/tier5-src/himi-blog-post/index.ts +21 -8
  278. package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -2
  279. package/templates/retail-store/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  280. package/templates/retail-store/tier5-src/himi-store-product/index.ts +13 -3
  281. package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  282. package/templates/retail-store-owner/dev/index.ts +50 -1
  283. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +19 -3
  284. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  285. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  286. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  287. package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  288. package/templates/yoga-studio/config.ts +7 -0
  289. package/templates/yoga-studio/dev/index.ts +1107 -110
  290. package/templates/yoga-studio/himalaya.content.json +3 -0
  291. package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  292. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +19 -3
  293. package/templates/yoga-studio/tier5-src/_shared/event-registration.ts +86 -0
  294. package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +2 -1
  295. package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +21 -8
  296. package/templates/yoga-studio/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  297. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  298. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  299. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -11
  300. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +12 -86
  301. package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +47 -6
  302. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  303. package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  304. package/templates/yoga-studio-owner/dev/index.ts +85 -65
  305. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +19 -3
  306. package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  307. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  308. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  309. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  310. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  311. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  312. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  313. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
@@ -35,8 +35,10 @@ import {
35
35
  type EventRow,
36
36
  } from "../_shared/himi-events";
37
37
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
38
+ import { eventPageUrlOf, eventStatus, registrationKindOf, registrationUi, registrationUrlOf, type RegistrationKind } from "../_shared/event-registration.js";
39
+ export { eventPageUrlOf, registrationKindOf, registrationUrlOf, type RegistrationKind };
38
40
  import { openRichLink } from "../_shared/rich-content-link.js";
39
- import { richBody } from "../_shared/rich-body.js";
41
+ import { descriptionBody } from "../_shared/rich-body.js";
40
42
  import { mayUseRegularTicketCheckout } from "../_shared/iap-attendance.js";
41
43
  import { ATTENDANCE_INITIAL, ATTENDANCE_NOT_HYDRATED, createAttendancePurchase, type AttendancePurchaseFields } from "../_shared/iap-attendance-purchase.js";
42
44
  import { resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
@@ -88,9 +90,6 @@ interface TicketTierRow {
88
90
  notAvailable?: boolean;
89
91
  }
90
92
 
91
- /** Registration CTA kind derived from the event's registration/eventType. */
92
- export type RegistrationKind = "rsvp" | "tickets" | "external" | "none";
93
-
94
93
  // Offline-first (data-offline): the event detail is a read-through single-item read —
95
94
  // it shares the `events` entity written by the list, and also write-throughs its own
96
95
  // fetched row so a deep-linked detail is cached for the next offline open. On a failed
@@ -302,7 +301,7 @@ function loadedTiers(defs: WixAvailableTicket[]): { rows: TicketTierRow[]; ticke
302
301
  const ticket = tickets.get(row.ticketDefinitionId ?? row.id);
303
302
  if (!ticket) return { ...row, quantity: 0, description: "", feeNote: "", policy: "", noPolicy: true, notAvailable: !row.available, notGuestPrice: !row.isGuestPrice, maxQuantity: 0 };
304
303
  const policy = (ticket as unknown as { policy?: string; policyText?: string }).policyText || (ticket as unknown as { policy?: string }).policy || "";
305
- return { ...row, quantity: 0, description: ticket.description || "", feeNote: ((ticket as unknown as { wixFeeConfig?: { type?: string }; feeType?: string }).wixFeeConfig?.type ?? (ticket as unknown as { feeType?: string }).feeType) === "FEE_ADDED_AT_CHECKOUT" ? detailCopy("event.serviceFee", "+ ticket service fee") : "", policy, noPolicy: !policy, notAvailable: !row.available, notGuestPrice: !row.isGuestPrice, priceAmount: row.priceAmount ?? Number(ticket.price?.amount ?? 0), rowLimit: row.available ? ticket.limitPerCheckout ?? 99 : 0, maxQuantity: row.available ? ticket.limitPerCheckout ?? 99 : 0 };
304
+ return { ...row, quantity: 0, description: descriptionBody(ticket.description).bodyText, feeNote: ((ticket as unknown as { wixFeeConfig?: { type?: string }; feeType?: string }).wixFeeConfig?.type ?? (ticket as unknown as { feeType?: string }).feeType) === "FEE_ADDED_AT_CHECKOUT" ? detailCopy("event.serviceFee", "+ ticket service fee") : "", policy, noPolicy: !policy, notAvailable: !row.available, notGuestPrice: !row.isGuestPrice, priceAmount: row.priceAmount ?? Number(ticket.price?.amount ?? 0), rowLimit: row.available ? ticket.limitPerCheckout ?? 99 : 0, maxQuantity: row.available ? ticket.limitPerCheckout ?? 99 : 0 };
306
305
  });
307
306
  return { rows, tickets };
308
307
  }
@@ -451,29 +450,6 @@ function calendarFallbackUrl(s: S, end: number): string {
451
450
  * shortDescription: REGISTRATION fills eventType, which skips the query enrichment that used to supply them. */
452
451
  export const EVENT_DETAIL_FIELDSETS: WixEventFieldset[] = ["DETAILS", "TEXTS", "REGISTRATION", "URLS", "FORM", "AGENDA"];
453
452
 
454
- /** Map registration status and decoded eventType onto the CTA variant. The decoded
455
- * `eventType` already folds registration.type / registration.initialType /
456
- * the legacy flat `type` (decodeEvent, #478). Without a recognized type or
457
- * status there is no registration action to offer. */
458
- export function registrationKindOf(e: WixEvent): RegistrationKind {
459
- const raw = e as unknown as { registration?: { type?: string; initialType?: string; status?: string } };
460
- const value = (raw.registration?.type ?? e.eventType ?? raw.registration?.initialType ?? "").toUpperCase();
461
- if (value === "NONE" || value.includes("NO_REGISTRATION")) return "none";
462
- // Open statuses select the footer action, so they must also select the
463
- // inventory read and action guards when type metadata is absent or stale.
464
- switch (raw.registration?.status?.toUpperCase()) {
465
- case "OPEN_TICKETS": return "tickets";
466
- case "OPEN_EXTERNAL": return "external";
467
- case "OPEN_RSVP":
468
- case "OPEN_RSVP_WAITLIST_ONLY":
469
- case "SCHEDULED_RSVP": return "rsvp";
470
- }
471
- if (value.includes("EXTERNAL")) return "external";
472
- if (value.includes("TICKET")) return "tickets";
473
- if (value.includes("RSVP")) return "rsvp";
474
- return "none";
475
- }
476
-
477
453
  function detailCopy(key: string, fallback: string): string { return copyParam(key, fallback); }
478
454
 
479
455
  function detailDate(e: WixEvent): { line: string; zone: string; start: number; end: number } {
@@ -536,69 +512,17 @@ function detailPlace(e: WixEvent): { value: string; mapUrl: string; hideMapTap:
536
512
  return { value, mapUrl: mapped, hideMapTap: !mapped, icon: "location" };
537
513
  }
538
514
 
539
- function eventStatus(e: WixEvent): string {
540
- const raw = e as unknown as { status?: string };
541
- return (raw.status ?? e.eventStatus ?? "").toUpperCase();
542
- }
543
-
544
- function registrationUi(e: WixEvent, now: number): { state: string; label: string; action: string; caption: string; notice: string } {
545
- const raw = e as unknown as { registration?: { status?: string; registrationPaused?: boolean; registrationDisabled?: boolean; allowedGuestTypes?: string; rsvp?: { startDate?: string }; tickets?: { soldOut?: boolean }; external?: { url?: string } }; form?: { messages?: { registrationClosed?: { message?: string; exploreEventsActionLabel?: string }; ticketsUnavailable?: { message?: string; exploreEventsActionLabel?: string } } } };
546
- const r = raw.registration ?? {};
547
- const status = eventStatus(e);
548
- const type = registrationKindOf(e);
549
- if (status === "CANCELED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.canceledNotice", "This event was canceled.") };
550
- if (status === "ENDED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.endedNotice", "This event has ended.") };
551
- if (type === "none" || r.registrationDisabled) return { state: "none", label: "", action: "", caption: "", notice: "" };
552
- const ownerClosed = raw.form?.messages?.registrationClosed?.message;
553
- const ownerSold = raw.form?.messages?.ticketsUnavailable?.message;
554
- if (["CLOSED_AUTOMATICALLY", "CLOSED_MANUALLY"].includes(r.status || "") || r.registrationPaused) return { state: "notice", label: "", action: "", caption: "", notice: ownerClosed || detailCopy("event.closedNotice", "Registration is closed.") };
555
- if (type === "tickets" && r.tickets?.soldOut) return { state: "notice", label: "", action: "", caption: "", notice: ownerSold || detailCopy("event.soldOutNotice", "Tickets are sold out.") };
556
- if (r.status === "SCHEDULED_RSVP") return { state: "scheduled", label: detailCopy("event.rsvpCta", "RSVP"), action: "", caption: detailCopy("event.opensCaption", "RSVP opens {date}.").replace("{date}", r.rsvp?.startDate ? formatWeekdayDateTime(new Date(r.rsvp.startDate)) : ""), notice: "" };
557
- if (r.status === "OPEN_RSVP_WAITLIST_ONLY") return { state: "waitlist", label: detailCopy("event.waitlistCta", "Join Waitlist"), action: "himi.open", caption: detailCopy("event.fullCaption", "This event is full."), notice: "" };
558
- if (r.status === "OPEN_TICKETS" || type === "tickets") return { state: "tickets", label: detailCopy("event.ticketsCta", "Buy Tickets"), action: "openTickets", caption: "", notice: "" };
559
- if (r.status === "OPEN_EXTERNAL" || type === "external") return registrationUrlOf(e)
560
- ? { state: "external", label: detailCopy("event.externalCta", "Register"), action: "openRegistration", caption: "", notice: "" }
561
- : { state: "none", label: "", action: "", caption: "", notice: "" };
562
- if (type === "rsvp" && r.status === "OPEN_RSVP") return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
563
- if (r.status === "OPEN_RSVP" || (type === "rsvp" && !r.status)) return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
564
- void now;
565
- return { state: "none", label: "", action: "", caption: "", notice: "" };
566
- }
567
-
568
- /** External registration URL / event page URL from the raw payload ("" when absent). */
569
- export function registrationUrlOf(e: WixEvent): string {
570
- const raw = e as unknown as {
571
- registration?: { external?: { registration?: string; url?: string } };
572
- eventPageUrl?: { base?: string; path?: string } | string;
573
- };
574
- const external = raw.registration?.external?.registration ?? raw.registration?.external?.url;
575
- if (typeof external === "string" && /^https?:\/\//i.test(external)) return external;
576
- return eventPageUrlOf(e);
577
- }
578
-
579
515
  /** Event slug for createRedirectSession({ eventsCheckout }) — "" when absent. */
580
516
  export function eventSlugOf(e: WixEvent): string {
581
517
  const slug = e.slug ?? (e as unknown as { slug?: string }).slug;
582
518
  return typeof slug === "string" ? slug.trim() : "";
583
519
  }
584
520
 
585
- /** The event's public page URL when the payload carries one ("" otherwise). */
586
- export function eventPageUrlOf(e: WixEvent): string {
587
- const raw = e as unknown as { eventPageUrl?: { base?: string; path?: string } | string };
588
- const page = raw.eventPageUrl;
589
- if (typeof page === "string" && /^https?:\/\//i.test(page)) return page;
590
- if (page && typeof page === "object" && typeof page.base === "string" && page.base) {
591
- const path = typeof page.path === "string" ? page.path : "";
592
- return `${page.base.replace(/\/$/, "")}${path && !path.startsWith("/") ? "/" : ""}${path}`;
593
- }
594
- return "";
595
- }
596
-
597
521
  /** The description pair: the v3 Ricos `description` for RichContent, and its plain text. A string description
598
522
  * (or none) keeps `eventDescription`'s legacy read, with no document. */
599
523
  function eventBody(e: WixEvent): { description: string; descriptionJson: string } {
600
- const legacy = eventDescription(e);
601
- const { bodyJson, bodyText } = richBody((e as unknown as { description?: unknown }).description, legacy);
524
+ const legacy = eventDescription({ ...e, description: undefined });
525
+ const { bodyJson, bodyText } = descriptionBody((e as unknown as { description?: unknown }).description, descriptionBody(legacy).bodyText);
602
526
  return { description: bodyText, descriptionJson: bodyJson };
603
527
  }
604
528
 
@@ -1132,6 +1056,8 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1132
1056
  }).sort((a, b) => (eventStartMillis(a) ?? 0) - (eventStartMillis(b) ?? 0)).map((item) => ({ id: item.id ?? "", date: eventWhen(item) })).filter((item) => !!item.id);
1133
1057
  } catch { moreDates = []; optionalReadFailed = true; }
1134
1058
  }
1059
+ const body = eventBody(e);
1060
+ const shortDescription = descriptionBody(rawEvent.shortDescription).bodyText;
1135
1061
  const readyState = derive({
1136
1062
  ...s,
1137
1063
  status: "ready",
@@ -1153,9 +1079,9 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1153
1079
  price,
1154
1080
  zone: dates.zone,
1155
1081
  mapUrl: place.mapUrl,
1156
- shortDescription: rawEvent.shortDescription || "",
1157
- noShortDescription: !rawEvent.shortDescription || rawEvent.shortDescription === eventBody(e).description,
1158
- noDescription: !eventBody(e).description && !eventBody(e).descriptionJson,
1082
+ shortDescription,
1083
+ noShortDescription: !shortDescription || shortDescription === body.description,
1084
+ noDescription: !body.description && !body.descriptionJson,
1159
1085
  noDate: !dates.line,
1160
1086
  registrationState: registration.state,
1161
1087
  footerLabel: registration.label,
@@ -1177,7 +1103,7 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1177
1103
  noMoreDates: moreDates.length === 0,
1178
1104
  // The Ricos description when the event has one (its own text is the plain copy); the legacy
1179
1105
  // string fields otherwise.
1180
- ...eventBody(e),
1106
+ ...body,
1181
1107
  image,
1182
1108
  hasImage: !!image,
1183
1109
  noImage: !image,
@@ -5,6 +5,8 @@ import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
5
5
  import { normalizeEvent, type EventRow } from "../_shared/himi-events";
6
6
  import { offsetPage } from "../_shared/offset-page.js";
7
7
  import { copyParam } from "../_shared/himi-copy.js";
8
+ import { eventPageUrlOf, registrationUi } from "../_shared/event-registration.js";
9
+ import { share as shareSheet } from "@himalaya/share";
8
10
 
9
11
  // Offline-first (data-offline): the public events list is a read-through catalog —
10
12
  // paint the last-synced rows from the on-device store first (instant, works offline),
@@ -13,8 +15,37 @@ import { copyParam } from "../_shared/himi-copy.js";
13
15
  // (+ declared the `events` entity in its dataSchema) just falls back to today's
14
16
  // online-only path — the try/catch is the seam that keeps this shared bundle safe.
15
17
  const ENTITY = "events";
16
- async function readCached(): Promise<EventRow[]> {
17
- try { return (await getAll(ENTITY)) as unknown as EventRow[]; } catch { return []; }
18
+ interface ListEventRow extends EventRow {
19
+ dateBadge: string; noDateBadge: boolean;
20
+ /** This card's verb (Register / RSVP / Buy Tickets / Join Waitlist) from the SAME rule the detail uses; hidden when none applies. */
21
+ actionLabel: string; noAction: boolean;
22
+ /** The event page URL (when the payload carries one) for Share. */
23
+ shareUrl: string;
24
+ }
25
+
26
+ // The day monogram is a title initial for undated events. Only an actual calendar
27
+ // date becomes a badge; derive it for old cached rows too, without a cache migration.
28
+ // The month must be the three-letter abbreviation: rows cached by an older worker on an
29
+ // Intl-less engine can hold a whole "11/10/2030, 12:00:00 PM" there.
30
+ function withDateBadge(row: EventRow): ListEventRow {
31
+ const dated = typeof row.month === "string" && /^[A-Z]{3}$/.test(row.month)
32
+ && /^(?:[1-9]|[12]\d|3[01])$/.test(row.day);
33
+ const known = row as Partial<ListEventRow>;
34
+ // Rows cached by an older build carry no registration data: they show no verb until a refresh, never a wrong one.
35
+ return { ...row, dateBadge: dated ? `${row.day}\n${row.month}` : "", noDateBadge: !dated,
36
+ actionLabel: known.actionLabel ?? "", noAction: known.noAction ?? true, shareUrl: known.shareUrl ?? "" };
37
+ }
38
+
39
+ /** The card's action, from the event's own registration (REGISTRATION + URLS fieldsets). Cancelled, ended,
40
+ * closed, sold-out and registration-less events offer none, so a card never promises "Register" the detail refuses. */
41
+ function withRegistration(raw: WixEvent, row: ListEventRow): ListEventRow {
42
+ const ui = registrationUi(raw, Date.now());
43
+ const offers = ui.state !== "notice" && ui.state !== "none" && ui.state !== "scheduled" && !!ui.label;
44
+ return { ...row, actionLabel: offers ? ui.label : "", noAction: !offers, shareUrl: eventPageUrlOf(raw) };
45
+ }
46
+
47
+ async function readCached(): Promise<ListEventRow[]> {
48
+ try { return ((await getAll(ENTITY)) as unknown as EventRow[]).map(withDateBadge); } catch { return []; }
18
49
  }
19
50
  async function writeCached(rows: EventRow[]): Promise<void> {
20
51
  try { for (const r of rows) await upsert(ENTITY, r as unknown as DbEntity); } catch { /* capability not granted → online-only */ }
@@ -22,12 +53,12 @@ async function writeCached(rows: EventRow[]): Promise<void> {
22
53
 
23
54
  interface S {
24
55
  status: "loading" | "ready";
25
- query: string; all: EventRow[]; results: EventRow[]; emptyText: string; countLabel: string;
56
+ query: string; all: ListEventRow[]; results: ListEventRow[]; emptyText: string; countLabel: string;
26
57
  errored: boolean; notErrored: boolean; isLoading: boolean; notLoading: boolean; noEmpty: boolean;
27
58
  nextCursor?: string; complete?: boolean; total?: number; loadingMore?: boolean; pageError?: boolean; fromCache?: boolean;
28
59
  }
29
60
  let queryNow = new Date().toISOString();
30
- const svc = defineBackendCollection<S, EventRow>("himiEvents", {
61
+ const svc = defineBackendCollection<S, ListEventRow>("himiEvents", {
31
62
  restorePagesOnLoad: true,
32
63
  state: { status: "loading", query: "", all: [], results: [], emptyText: "", countLabel: "", errored: false, notErrored: true,
33
64
  isLoading: true, notLoading: false, noEmpty: true },
@@ -62,10 +93,10 @@ const svc = defineBackendCollection<S, EventRow>("himiEvents", {
62
93
  ];
63
94
  }
64
95
  if (criteria.filter === "value:Past") filter["dateAndTimeSettings.startDate"] = { $lt: queryNow };
65
- const res = await queryEvents({ limit: 50, offset: Number(cursor || 0), query: { filter,
96
+ const res = await queryEvents({ limit: 50, offset: Number(cursor || 0), fields: ["DETAILS", "REGISTRATION", "URLS"], query: { filter,
66
97
  sort: [{ fieldName: criteria.sort === "default" ? "dateAndTimeSettings.startDate" : "title", order: criteria.sort === "za" ? "DESC" : "ASC" }, { fieldName: "id", order: "ASC" }] } });
67
98
  const raw = res.events ?? [];
68
- const items = raw.filter(row => !!row.id).map(normalizeEvent);
99
+ const items = raw.filter(row => !!row.id).map(row => withRegistration(row, withDateBadge(normalizeEvent(row))));
69
100
  await writeCached(items);
70
101
  const metadata = (res as { pagingMetadata?: { total?: number } }).pagingMetadata;
71
102
  return { items, ...offsetPage(cursor, raw.length, 50, metadata), metadata: { fromCache: false } };
@@ -78,6 +109,16 @@ const svc = defineBackendCollection<S, EventRow>("himiEvents", {
78
109
  fromCache: page.metadata?.fromCache === true, errored, notErrored: !errored, isLoading: loading, notLoading: !loading,
79
110
  noEmpty: true, countLabel: "", emptyText: errored ? copyParam("events.errorText", "Events could not be loaded.") : "" };
80
111
  },
112
+ actions: {
113
+ // Share a card's event: its title and date line, plus the event page when the payload carried one.
114
+ share: async (s, arg) => {
115
+ const id = typeof arg === "string" ? arg : String((arg as { id?: unknown } | undefined)?.id ?? "");
116
+ const row = s.results.find(item => item.id === id);
117
+ if (!row) return undefined;
118
+ try { await shareSheet({ subject: row.title, text: [row.title, row.dateLine].filter(Boolean).join(" — "), ...(row.shareUrl ? { url: row.shareUrl } : {}) }); } catch { /* the sheet is best-effort: nothing to undo */ }
119
+ return undefined;
120
+ },
121
+ },
81
122
  });
82
123
  svc.load();
83
124
  export { svc };
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // Tier-5 bundle: Wix Spaces clone — Pricing plan detail (ON-DEVICE, site-scoped).
2
3
  // himalaya.input.id carries the plan id.
3
4
  //
@@ -53,7 +54,7 @@ const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesPlanDetail",
53
54
  const p = await readPlan(id);
54
55
  if (!p) return { ...s, status: "ready", ...presence("missing") };
55
56
  const perks = (p.perks?.values ?? []).map((v) => `• ${v}`).join("\n");
56
- return { status: "ready", name: p.name || "Plan", price: priceOf(p), description: p.description || "", perks, ...presence("found") };
57
+ return { status: "ready", name: p.name || "Plan", price: priceOf(p), description: descriptionBody(p.description).bodyText, perks, ...presence("found") };
57
58
  } catch (e) { return { ...s, status: "ready", ...presenceAfterError(e) }; }
58
59
  },
59
60
  },
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // Tier-5 bundle: Wix Spaces clone — What's New (ON-DEVICE, account-level).
2
3
  import { defineService } from "@himalaya/state";
3
4
  import { withEmptyFlags } from "../_shared/empty-flags.js";
@@ -8,7 +9,7 @@ interface S { status: "loading" | "ready"; items: Row[]; emptyText: string; /**
8
9
 
9
10
  function norm(a: WixAnnouncement, i: number): Row {
10
11
  const aa = a as unknown as { id?: string; title?: string; bodyText?: string; description?: string };
11
- return { id: aa.id || `a${i}`, name: aa.title || "Announcement", subtitle: aa.description || aa.bodyText || "" };
12
+ return { id: aa.id || `a${i}`, name: aa.title || "Announcement", subtitle: descriptionBody(aa.description, descriptionBody(aa.bodyText).bodyText).bodyText };
12
13
  }
13
14
 
14
15
  const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesWhatsNew", withEmptyFlags({
@@ -81718,6 +81718,51 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
81718
81718
  }
81719
81719
  }
81720
81720
  },
81721
+ {
81722
+ "id": "product_body",
81723
+ "body": {
81724
+ "package": "himalaya",
81725
+ "type": "RichContent",
81726
+ "props": {
81727
+ "documentJsonPath": "$service:himiOwnerLiteProduct.bodyJson",
81728
+ "onLink": {
81729
+ "actionId": "tier5.himiOwnerLiteProduct.openLink"
81730
+ },
81731
+ "fallback": {
81732
+ "id": "",
81733
+ "body": {
81734
+ "package": "himalaya",
81735
+ "type": "Text",
81736
+ "props": {
81737
+ "text": "$service:himiOwnerLiteProduct.bodyText",
81738
+ "style": "body",
81739
+ "color": "textPrimary",
81740
+ "collapseWhenEmpty": true
81741
+ }
81742
+ }
81743
+ }
81744
+ }
81745
+ },
81746
+ "modifiers": {
81747
+ "hiddenPath": "$service:himiOwnerLiteProduct.noBodyJson"
81748
+ }
81749
+ },
81750
+ {
81751
+ "id": "product_body_text",
81752
+ "body": {
81753
+ "package": "himalaya",
81754
+ "type": "Text",
81755
+ "props": {
81756
+ "text": "$service:himiOwnerLiteProduct.bodyText",
81757
+ "style": "body",
81758
+ "color": "textPrimary",
81759
+ "collapseWhenEmpty": true
81760
+ }
81761
+ },
81762
+ "modifiers": {
81763
+ "hiddenPath": "$service:himiOwnerLiteProduct.hasBodyJson"
81764
+ }
81765
+ },
81721
81766
  {
81722
81767
  "id": "product_info_card",
81723
81768
  "body": {
@@ -82140,7 +82185,8 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
82140
82185
  "contentType": "application/javascript+ts-bundle",
82141
82186
  "declaredCapabilities": [
82142
82187
  "wix_api",
82143
- "local_database"
82188
+ "local_database",
82189
+ "open_url"
82144
82190
  ],
82145
82191
  "minBinaryVersion": "0.1.0",
82146
82192
  "bundleVersion": "0.0.1"
@@ -82744,6 +82790,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
82744
82790
  }
82745
82791
  }
82746
82792
  }
82793
+ },
82794
+ "modifiers": {
82795
+ "hiddenPath": "$service:himiOwnerLiteEvent.noBodyJson"
82747
82796
  }
82748
82797
  },
82749
82798
  {
@@ -83753,6 +83802,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
83753
83802
  }
83754
83803
  }
83755
83804
  }
83805
+ },
83806
+ "modifiers": {
83807
+ "hiddenPath": "$service:himiOwnerLitePost.noBodyJson"
83756
83808
  }
83757
83809
  },
83758
83810
  {
@@ -19,6 +19,8 @@ export interface CollectionConfig {
19
19
  /** Numeric 0–5 row field rendered as a Rating (accessible "N out of 5") instead of
20
20
  * folding a "★★★★☆" glyph string into the metadata line, so screen readers get a value. */
21
21
  rating?: string;
22
+ /** Domain-provided calendar text and its unavailable flag, kept separate from media. */
23
+ dateBadge?: { text: string; hidden: string };
22
24
  sectioned?: boolean;
23
25
  view: 'list' | 'grid';
24
26
  backendQuery?: boolean;
@@ -38,7 +40,13 @@ export interface CollectionConfig {
38
40
  /** The card's primary verb, shown as a visible button beside the overflow.
39
41
  * `screenId` opens that screen with the row id; without it the verb reuses the
40
42
  * row's own tap action (the detail screen that owns the verb). */
41
- primaryVerb?: { label: string; screenId?: string };
43
+ primaryVerb?: { label: string; screenId?: string;
44
+ /** A row field holding THIS row's verb label (Register, RSVP, Buy Tickets…); `label` stays the fallback copy. */
45
+ labelPath?: string;
46
+ /** A row boolean that hides the verb on rows that offer no action (an event with no registration). */
47
+ hiddenPath?: string };
48
+ /** The worker action that shares a row (`tier5.<service>.<name>` with `{ id }`): adds Share to the card's overflow menu. */
49
+ shareAction?: string;
42
50
  /** A TIME-BOUND entity (an event, a booking, a reservation): the boolean row field the
43
51
  * DOMAIN sets once the record has finished — computed by the same rule as the row's own
44
52
  * "Past" label, in the same place, so the two cannot disagree. A finished
@@ -73,7 +81,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
73
81
  himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
74
82
  defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
75
83
  sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
76
- himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' }, endedWhen: 'isPast' },
84
+ himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', dateBadge: { text: 'dateBadge', hidden: 'noDateBadge' }, gridColumns: 1, view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register', labelPath: 'actionLabel', hiddenPath: 'noAction' }, shareAction: 'share', endedWhen: 'isPast' },
77
85
  himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
78
86
  himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
79
87
  himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
@@ -208,7 +216,15 @@ export function projectCollection(source: CollectionRow[], config: CollectionCon
208
216
  ...(config.endedWhen ? (() => {
209
217
  const ended = row[config.endedWhen] === true || (config.endedWhen === 'isPast' && row.statusLabel === 'Past');
210
218
  return { collectionEnded: ended, collectionNotEnded: !ended };
211
- })() : {}) })),
219
+ })() : {}),
220
+ // A row restored from state saved by an older build has no badge flag. An unset hidden flag
221
+ // fails open and would paint an empty chip, so it reads as "no badge" until a refresh.
222
+ ...(config.dateBadge && typeof row[config.dateBadge.hidden] !== 'boolean'
223
+ ? { [config.dateBadge.text]: '', [config.dateBadge.hidden]: true } : {}),
224
+ // Same for a per-row verb: an unset hidden flag fails open and would show a verb with an unresolved
225
+ // label, so a row without the flag offers no action until a refresh establishes one.
226
+ ...(config.primaryVerb?.hiddenPath && typeof row[config.primaryVerb.hiddenPath] !== 'boolean'
227
+ ? { ...(config.primaryVerb.labelPath ? { [config.primaryVerb.labelPath]: '' } : {}), [config.primaryVerb.hiddenPath]: true } : {}) })),
212
228
  filters, loaded: rows.length,
213
229
  };
214
230
  }
@@ -12,7 +12,7 @@ import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError,
12
12
  import { DEMO_EVENTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
13
13
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
14
14
  import { openRichLink } from "../_shared/rich-content-link.js";
15
- import { richBody } from "../_shared/rich-body.js";
15
+ import { descriptionBody } from "../_shared/rich-body.js";
16
16
  import { get as dbGet, upsert as dbUpsert, remove as dbRemove, type DbEntity } from "../_shared/owner-local-db.js";
17
17
  import { blankFlags } from "../_shared/detail-facts.js";
18
18
 
@@ -41,6 +41,7 @@ interface EventState {
41
41
  bodyJson: string;
42
42
  /** bodyJson !== "" — gates the plain text, because RichContent draws nothing for "". */
43
43
  hasBodyJson: boolean;
44
+ noBodyJson: boolean;
44
45
  /** The description's plain text (the document's own text, else the legacy description). */
45
46
  bodyText: string;
46
47
  initial: string;
@@ -68,15 +69,15 @@ interface EventState {
68
69
  }
69
70
 
70
71
  /* eslint-disable @typescript-eslint/no-explicit-any */
71
- /** [rico] #3499 — keep the description's plain text with the event's cached row, so the offline detail shows it.
72
+ /** [rico] #3499 — keep the description pair with the event's cached row, so the offline detail shows it.
72
73
  * Merged into the row the events list (or the form) wrote rather than replacing it; with no row yet, the
73
74
  * loaded detail's own fields become one. */
74
75
  async function cacheBodyText(s: EventState): Promise<void> {
75
76
  if (s.status !== "ready" || !s.id) return;
76
77
  const prev = await cacheGet(s.id);
77
- await cachePut(prev ? { ...prev, bodyText: s.bodyText } : {
78
+ await cachePut(prev ? { ...prev, bodyText: s.bodyText, bodyJson: s.bodyJson } : {
78
79
  id: s.id, title: s.title, subtitle: s.subtitle, image: s.image, dateLine: s.dateLine, location: s.location,
79
- statusLabel: s.statusLabel, registration: s.registration, bodyText: s.bodyText,
80
+ statusLabel: s.statusLabel, registration: s.registration, bodyText: s.bodyText, bodyJson: s.bodyJson,
80
81
  });
81
82
  }
82
83
  function idFrom(arg: unknown, fallback = ""): string {
@@ -132,6 +133,7 @@ function shapeFields(s: Partial<EventState>): EventState {
132
133
  summary: s.summary ?? "",
133
134
  bodyJson: s.bodyJson ?? "",
134
135
  hasBodyJson: !!s.bodyJson,
136
+ noBodyJson: !s.bodyJson,
135
137
  bodyText: s.bodyText ?? "",
136
138
  initial: s.initial ?? "?",
137
139
  image,
@@ -186,6 +188,7 @@ function cachedDetail(s: EventState, row: DbEntity): EventState {
186
188
  const title = String(row.title ?? row.name ?? "");
187
189
  const dateLine = String(row.dateLine ?? row.when ?? "");
188
190
  const location = String(row.location ?? "");
191
+ const body = descriptionBody(row.bodyJson || row.description, descriptionBody(row.bodyText ?? row.summary).bodyText);
189
192
  return shape({
190
193
  ...s,
191
194
  id: String(row.id),
@@ -193,8 +196,8 @@ function cachedDetail(s: EventState, row: DbEntity): EventState {
193
196
  title,
194
197
  subtitle: String(row.subtitle ?? [dateLine, location].filter((x) => x && x !== "—").join(" · ")),
195
198
  summary: "",
196
- bodyJson: "",
197
- bodyText: String(row.bodyText ?? row.summary ?? row.description ?? ""),
199
+ bodyJson: body.bodyJson,
200
+ bodyText: body.bodyText,
198
201
  initial: String(row.initial ?? (title[0] || "?")).slice(0, 1).toUpperCase(),
199
202
  image: String(row.image ?? ""),
200
203
  dateLine: dateLine || "—",
@@ -216,9 +219,9 @@ async function refresh(prev: EventState, id: string): Promise<EventState> {
216
219
  }
217
220
  const row = normalizeEvent(ev); // reuses date/location/status normalization
218
221
  const image = /^https?:\/\//.test(row.image) ? row.image : "";
219
- const { bodyJson, bodyText } = richBody((ev as unknown as { description?: unknown }).description, (eventDescription(ev) || "").trim());
222
+ const { bodyJson, bodyText } = descriptionBody((ev as unknown as { description?: unknown }).description, descriptionBody(eventDescription({ ...ev, description: undefined })).bodyText);
220
223
  // The teaser stays above the body only when it says something the body does not.
221
- const teaser = String((ev as any).shortDescription ?? "").trim();
224
+ const teaser = descriptionBody((ev as any).shortDescription).bodyText;
222
225
  return shape({
223
226
  ...prev,
224
227
  id,
@@ -358,4 +361,9 @@ if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydra
358
361
  if (booted && typeof booted === "object") Object.assign(booted, { deletePending: false, noDeleteConfirmation: true });
359
362
  }
360
363
 
364
+ // Hydration shallow-merges older snapshots over defaults; derive both body gates from
365
+ // the restored document before the first paint, without waiting for a load action.
366
+ const bootedBody = svc.getState();
367
+ Object.assign(bootedBody, { hasBodyJson: !!bootedBody.bodyJson, noBodyJson: !bootedBody.bodyJson });
368
+
361
369
  export { svc };
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // Tier-5 bundle: __HIMI_APP_NAME__ — Events event EDIT form (the CRUD "U" only).
2
3
  // Modeled on himi-blog-owner-editor. Opened from the event detail ("Edit").
3
4
  //
@@ -222,12 +223,16 @@ const svc = defineService<EventFormState, {
222
223
  // this form never writes, so showing them here would invite an edit that lands elsewhere.
223
224
  ...(() => {
224
225
  const raw = (ev as unknown as { description?: unknown }).description;
225
- const doc = raw && typeof raw === "object" && Array.isArray((raw as { nodes?: unknown }).nodes) ? raw as Parameters<typeof postBodyText>[0] : null;
226
- const text = doc ? postBodyText(doc) : typeof raw === "string" ? raw.trim() : "";
226
+ const doc = parseRicosJson(raw);
227
+ const text = descriptionBody(raw).bodyText;
227
228
  // Edited in the RichContentEditor, seeded with the event's own document byte for byte (never
228
229
  // re-encoded); a legacy string seeds as paragraphs, and none seeds an empty editor.
229
- return { ...DESCRIPTION_LOADED, description: text, originalDescription: text, descriptionMode: "editor" as const,
230
- descriptionJson: doc ? JSON.stringify(raw) : text ? docJson(plainPostBody(text)) : "" };
230
+ // A description that looks like a serialized document but does not read as one (malformed JSON,
231
+ // or an object that is not a document) is locked, as on the Product form: an empty editor would
232
+ // let a save replace it. A blank or tag-only string stays editable so the owner can write the first one.
233
+ const unreadable = !doc && !text && raw != null && (typeof raw !== "string" || /^\s*[{[]/.test(raw));
234
+ return { ...DESCRIPTION_LOADED, description: text, originalDescription: text, descriptionMode: unreadable ? "locked" as const : "editor" as const,
235
+ descriptionJson: unreadable ? "" : doc ? JSON.stringify(doc) : text ? docJson(plainPostBody(text)) : "" };
231
236
  })(),
232
237
  when: row.dateLine || "—",
233
238
  location: row.location || "—",
@@ -239,14 +244,18 @@ const svc = defineService<EventFormState, {
239
244
  // Offline: prefill from the last-synced local cache before the demo/error fallbacks.
240
245
  const cached = await cacheGet(id);
241
246
  if (cached) {
247
+ // Only `descriptionJson` is the document as Wix returned it. The detail's `bodyJson` is the encoder's
248
+ // DISPLAY copy (media resized to urls), so it feeds the preview text and never seeds the editor.
249
+ const doc = parseRicosJson(cached.descriptionJson);
250
+ const description = descriptionBody(doc ?? cached.bodyJson, descriptionBody(cached.description ?? cached.bodyText ?? cached.summary).bodyText).bodyText;
242
251
  return shape({
243
252
  ...s, mode, id, heading: "Edit event",
244
253
  title: String(cached.title ?? cached.name ?? ""),
245
- description: String(cached.description ?? ""),
246
- originalDescription: String(cached.description ?? ""), ...DESCRIPTION_LOADED,
254
+ description,
255
+ originalDescription: description, ...DESCRIPTION_LOADED,
247
256
  // A row cached with its document reopens in the editor. An older row has none, so its verdict
248
257
  // decides: plain when it was plain, else locked (it carries no verdict either: fail closed).
249
- ...(cached.descriptionJson ? { descriptionMode: "editor" as const, descriptionJson: String(cached.descriptionJson) }
258
+ ...(doc ? { descriptionMode: "editor" as const, descriptionJson: JSON.stringify(doc) }
250
259
  : { descriptionMode: cached.descriptionProtected === "false" ? "plain" as const : "locked" as const, descriptionJson: "" }),
251
260
  when: String(cached.when ?? cached.dateLine ?? "—"),
252
261
  location: String(cached.location ?? "—"),
@@ -9,7 +9,7 @@ import { normalizeEvent, type EventRow } from "../_shared/himi-events.js";
9
9
  import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback } from "../_shared/owner-load-error.js";
10
10
  import { DEMO_EVENTS, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
11
11
  import { copyParam } from "../_shared/himi-copy.js";
12
- import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
12
+ import { get, getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
13
13
 
14
14
  // Last-synced rows are an explicitly incomplete fallback for an unfiltered
15
15
  // request during an outage. Filtered pages never claim this cache is complete.
@@ -19,7 +19,9 @@ async function readCachedRows(): Promise<EventRow[]> {
19
19
  try { return (await getAll(ENTITY)) as unknown as EventRow[]; } catch { return []; }
20
20
  }
21
21
  async function writeCachedRows(rows: EventRow[]): Promise<void> {
22
- try { for (const r of rows) await upsert(ENTITY, r as unknown as DbEntity); } catch { /* capability not granted → online-only */ }
22
+ try {
23
+ for (const r of rows) await upsert(ENTITY, { ...await get(ENTITY, r.id), ...r } as unknown as DbEntity);
24
+ } catch { /* capability not granted → online-only */ }
23
25
  }
24
26
 
25
27
  interface EventsState {
@@ -55,6 +55,7 @@ interface PostState {
55
55
  bodyJson: string;
56
56
  /** bodyJson !== "" — gates the plain text, because RichContent draws nothing for "". */
57
57
  hasBodyJson: boolean;
58
+ noBodyJson: boolean;
58
59
  /** Plain text of the body: RichContent's `fallback`, and the offline copy's body. */
59
60
  bodyText: string;
60
61
  // Info rows
@@ -151,6 +152,7 @@ function shapeFields(s: Partial<PostState>): PostState {
151
152
  image,
152
153
  bodyJson: s.bodyJson ?? "",
153
154
  hasBodyJson: !!s.bodyJson,
155
+ noBodyJson: !s.bodyJson,
154
156
  bodyText: s.bodyText ?? "",
155
157
  postStatus: s.postStatus ?? statusLabel(raw),
156
158
  category: s.category ?? "—",
@@ -485,4 +487,9 @@ if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydra
485
487
  if (booted && typeof booted === "object") Object.assign(booted, { deletePending: false, noDeleteConfirmation: true });
486
488
  }
487
489
 
490
+ // Hydration shallow-merges older snapshots over defaults; derive both body gates from
491
+ // the restored document before the first paint, without waiting for a load action.
492
+ const bootedBody = svc.getState();
493
+ Object.assign(bootedBody, { hasBodyJson: !!bootedBody.bodyJson, noBodyJson: !bootedBody.bodyJson });
494
+
488
495
  export { svc };