@wix/himalaya-cli 0.1392.0 → 0.1394.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 (286) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/bookings-v2/src/extensions.d.ts +7 -5
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-bookings.mjs +4 -4
  3. package/dist/cli.mjs +41 -27
  4. package/dist/preview-assets.generated.bin +0 -0
  5. package/guides/himi-json-output.md +1 -0
  6. package/guides/iap-products.md +5 -0
  7. package/guides/sdk-reference.json +4 -4
  8. package/package.json +2 -2
  9. package/templates/beauty-salon/tier5-src/_shared/owner-load-error.ts +115 -12
  10. package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +52 -28
  11. package/templates/beauty-salon-owner/dev/index.ts +545 -100
  12. package/templates/beauty-salon-owner/himalaya.content.json +6 -0
  13. package/templates/beauty-salon-owner/tier5-src/_shared/owner-account.ts +6 -2
  14. package/templates/beauty-salon-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  15. package/templates/beauty-salon-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  16. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  17. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  18. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  19. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  20. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  21. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  22. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  23. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  24. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  25. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  26. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  27. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  28. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  29. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
  30. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  31. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
  32. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  33. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  34. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  35. package/templates/branded-lite/tier5-src/_shared/owner-load-error.ts +115 -12
  36. package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +52 -28
  37. package/templates/branded-lite/tier5-src/himi-branded-lite-group/index.ts +24 -24
  38. package/templates/community-club/tier5-src/_shared/owner-load-error.ts +115 -12
  39. package/templates/community-club/tier5-src/himi-branded-lite-group/index.ts +24 -24
  40. package/templates/community-club-owner/dev/index.ts +317 -100
  41. package/templates/community-club-owner/himalaya.content.json +6 -0
  42. package/templates/community-club-owner/tier5-src/_shared/owner-account.ts +6 -2
  43. package/templates/community-club-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  44. package/templates/community-club-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  45. package/templates/community-club-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  46. package/templates/community-club-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  47. package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  48. package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  49. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  50. package/templates/community-club-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  51. package/templates/community-club-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  52. package/templates/community-club-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  53. package/templates/community-club-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  54. package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  55. package/templates/community-club-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  56. package/templates/community-club-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  57. package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  58. package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  59. package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  60. package/templates/community-club-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  61. package/templates/community-club-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  62. package/templates/community-club-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  63. package/templates/events-meetups/tier5-src/_shared/owner-load-error.ts +115 -12
  64. package/templates/events-meetups/tier5-src/himi-branded-lite-group/index.ts +24 -24
  65. package/templates/events-meetups-owner/dev/index.ts +275 -100
  66. package/templates/events-meetups-owner/himalaya.content.json +6 -0
  67. package/templates/events-meetups-owner/tier5-src/_shared/owner-account.ts +6 -2
  68. package/templates/events-meetups-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  69. package/templates/events-meetups-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  70. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  71. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  72. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  73. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  74. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  75. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  76. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  77. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  78. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  79. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  80. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  81. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  82. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  83. package/templates/gym-fitness/tier5-src/_shared/owner-load-error.ts +115 -12
  84. package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +52 -28
  85. package/templates/gym-fitness-owner/dev/index.ts +545 -100
  86. package/templates/gym-fitness-owner/himalaya.content.json +6 -0
  87. package/templates/gym-fitness-owner/tier5-src/_shared/owner-account.ts +6 -2
  88. package/templates/gym-fitness-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  89. package/templates/gym-fitness-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  90. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  91. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  92. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  93. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  94. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  95. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  96. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  97. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  98. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  99. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  100. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  101. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  102. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  103. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  104. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  105. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  106. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
  107. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  108. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
  109. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  110. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  111. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  112. package/templates/himi-chat/tier5-src/_shared/owner-load-error.ts +115 -12
  113. package/templates/nonprofit-cause/tier5-src/_shared/owner-load-error.ts +115 -12
  114. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group/index.ts +24 -24
  115. package/templates/nonprofit-cause-owner/dev/index.ts +503 -100
  116. package/templates/nonprofit-cause-owner/himalaya.content.json +6 -0
  117. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-account.ts +6 -2
  118. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  119. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  120. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  121. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  122. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  123. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  124. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  125. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  126. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  127. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  128. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  129. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  130. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  131. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
  132. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  133. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
  134. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  135. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  136. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  137. package/templates/owner-lite/dev/index.ts +545 -100
  138. package/templates/owner-lite/himalaya.content.json +6 -0
  139. package/templates/owner-lite/tier5-src/_shared/owner-account.ts +6 -2
  140. package/templates/owner-lite/tier5-src/_shared/owner-booking-respond.ts +82 -0
  141. package/templates/owner-lite/tier5-src/_shared/owner-load-error.ts +115 -12
  142. package/templates/owner-lite/tier5-src/himi-owner-lite-account/index.ts +7 -4
  143. package/templates/owner-lite/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  144. package/templates/owner-lite/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  145. package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +18 -20
  146. package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  147. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +2 -2
  148. package/templates/owner-lite/tier5-src/himi-owner-lite-home/index.ts +2 -2
  149. package/templates/owner-lite/tier5-src/himi-owner-lite-order/index.ts +3 -3
  150. package/templates/owner-lite/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  151. package/templates/owner-lite/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  152. package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +60 -43
  153. package/templates/owner-lite/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  154. package/templates/owner-lite/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  155. package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +31 -32
  156. package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  157. package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +2 -2
  158. package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +18 -20
  159. package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  160. package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +2 -2
  161. package/templates/owner-lite/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  162. package/templates/owner-lite/tier5-src/himi-owner-lite-today/index.ts +315 -0
  163. package/templates/owner-lite/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  164. package/templates/podcast-creator/tier5-src/_shared/owner-load-error.ts +115 -12
  165. package/templates/podcast-creator-owner/dev/index.ts +317 -100
  166. package/templates/podcast-creator-owner/himalaya.content.json +6 -0
  167. package/templates/podcast-creator-owner/tier5-src/_shared/owner-account.ts +6 -2
  168. package/templates/podcast-creator-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  169. package/templates/podcast-creator-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  170. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  171. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  172. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  173. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  174. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  175. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  176. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  177. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  178. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  179. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  180. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  181. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  182. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  183. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  184. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  185. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  186. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  187. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  188. package/templates/portfolio-creative/tier5-src/_shared/owner-load-error.ts +115 -12
  189. package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +52 -28
  190. package/templates/portfolio-creative-owner/dev/index.ts +545 -100
  191. package/templates/portfolio-creative-owner/himalaya.content.json +6 -0
  192. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-account.ts +6 -2
  193. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  194. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  195. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  196. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  197. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  198. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  199. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  200. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  201. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  202. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  203. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  204. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  205. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  206. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  207. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  208. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
  209. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  210. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
  211. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  212. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  213. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  214. package/templates/restaurant/tier5-src/_shared/owner-load-error.ts +115 -12
  215. package/templates/restaurant-owner/dev/index.ts +545 -100
  216. package/templates/restaurant-owner/himalaya.content.json +6 -0
  217. package/templates/restaurant-owner/tier5-src/_shared/owner-account.ts +6 -2
  218. package/templates/restaurant-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  219. package/templates/restaurant-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  220. package/templates/restaurant-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  221. package/templates/restaurant-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  222. package/templates/restaurant-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  223. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  224. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  225. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  226. package/templates/restaurant-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  227. package/templates/restaurant-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  228. package/templates/restaurant-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  229. package/templates/restaurant-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  230. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  231. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  232. package/templates/restaurant-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  233. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  234. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  235. package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  236. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
  237. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  238. package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
  239. package/templates/restaurant-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  240. package/templates/restaurant-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  241. package/templates/restaurant-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  242. package/templates/retail-store/tier5-src/_shared/owner-load-error.ts +115 -12
  243. package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +52 -28
  244. package/templates/retail-store-owner/dev/index.ts +317 -100
  245. package/templates/retail-store-owner/himalaya.content.json +6 -0
  246. package/templates/retail-store-owner/tier5-src/_shared/owner-account.ts +6 -2
  247. package/templates/retail-store-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  248. package/templates/retail-store-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  249. package/templates/retail-store-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  250. package/templates/retail-store-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  251. package/templates/retail-store-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  252. package/templates/retail-store-owner/tier5-src/himi-owner-lite-order/index.ts +3 -3
  253. package/templates/retail-store-owner/tier5-src/himi-owner-lite-orders/index.ts +2 -2
  254. package/templates/retail-store-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  255. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  256. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  257. package/templates/retail-store-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  258. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +31 -32
  259. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +23 -43
  260. package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +2 -2
  261. package/templates/retail-store-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  262. package/templates/retail-store-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  263. package/templates/retail-store-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
  264. package/templates/yoga-studio/tier5-src/_shared/owner-load-error.ts +115 -12
  265. package/templates/yoga-studio-owner/dev/index.ts +503 -100
  266. package/templates/yoga-studio-owner/himalaya.content.json +6 -0
  267. package/templates/yoga-studio-owner/tier5-src/_shared/owner-account.ts +6 -2
  268. package/templates/yoga-studio-owner/tier5-src/_shared/owner-booking-respond.ts +82 -0
  269. package/templates/yoga-studio-owner/tier5-src/_shared/owner-load-error.ts +115 -12
  270. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-account/index.ts +7 -4
  271. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-appointments/index.ts +2 -2
  272. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-ask/live.ts +3 -3
  273. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +18 -20
  274. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +20 -28
  275. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +2 -2
  276. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-home/index.ts +2 -2
  277. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-overview/index.ts +2 -2
  278. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +60 -43
  279. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post-form/index.ts +10 -23
  280. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-posts/index.ts +2 -2
  281. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +18 -20
  282. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +16 -29
  283. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +2 -2
  284. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-sites/index.ts +2 -2
  285. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-today/index.ts +315 -0
  286. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-wrist/index.ts +42 -15
@@ -7,24 +7,22 @@
7
7
  import { defineService } from "@himalaya/state";
8
8
  import { getProduct, updateProduct, deleteProduct, type WixProduct } from "@himalaya/wix-stores";
9
9
  import { plainText, productImage, wixMediaUrl, WIX_DETAIL_IMAGE, type StoreProduct } from "../_shared/himi-store.js";
10
- import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
10
+ import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
11
11
  import { DEMO_PRODUCTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
12
12
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
13
- import { get as dbGet, upsert as dbUpsert, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
13
+ import { get as dbGet, upsert as dbUpsert, remove as dbRemove, type DbEntity } from "../_shared/owner-local-db.js";
14
14
  import { blankFlags } from "../_shared/detail-facts.js";
15
15
 
16
- // Offline-first (data-offline): owner CRUD is WRITE-BEHIND against the encrypted
17
- // `owner_products` entity — the local store is updated OPTIMISTICALLY so the
18
- // detail/list repaint from cache immediately, then the Wix write is attempted; if
19
- // it can't reach the bridge/network the mutation is ENQUEUED for replay on
20
- // reconnect, and detail reads fall back to the last-synced local row. Every
21
- // local-db call is wrapped (try/catch) so a build that ships without the
22
- // `local_database` grant behaves exactly as before (online-only, no cache/queue).
16
+ // Offline-first (data-offline): detail reads fall back to the last-synced local row
17
+ // of the encrypted `owner_products` entity. Writes are SERVER-CONFIRMED: the local
18
+ // row changes only after Wix accepts the write; a failed one leaves it alone and
19
+ // tells the owner why. No host replays a queued write, so nothing is queued.
20
+ // Every local-db call is wrapped (try/catch) so a build that ships without the
21
+ // `local_database` grant behaves exactly as before (online-only, no cache).
23
22
  const ENTITY = "owner_products";
24
23
  async function cachePut(row: DbEntity): Promise<void> { try { await dbUpsert(ENTITY, row); } catch { /* capability not granted */ } }
25
24
  async function cacheDrop(id: string): Promise<void> { try { await dbRemove(ENTITY, id); } catch { /* capability not granted */ } }
26
25
  async function cacheGet(id: string): Promise<DbEntity | null> { try { return await dbGet(ENTITY, id); } catch { return null; } }
27
- async function queueWrite(actionId: string, params: Record<string, string>): Promise<void> { try { await dbEnqueue(actionId, params); } catch { /* capability not granted */ } }
28
26
 
29
27
  interface ProductState {
30
28
  id: string;
@@ -265,19 +263,21 @@ async function setVisibility(s: ProductState, visible: boolean, msg: string): Pr
265
263
  : copyParam("ownerProduct.alreadyHidden", "Already hidden."),
266
264
  });
267
265
  }
268
- // Optimistic local write — the detail/list repaint from cache immediately.
269
- const optimistic = shape({ ...s, visibleRaw: visible, statusLabel: visible ? "Visible in store" : "Hidden", subtitle: visible ? "Visible in store" : "Hidden from store", actionMessage: msg });
270
- await cachePut(cacheRow(optimistic));
271
266
  try {
272
267
  await updateProduct(s.id, { visible });
273
- return refresh(s, s.id, msg);
274
268
  } catch (e) {
275
- if (shouldQueueOwnerWrite(e)) {
276
- // Offline / no bridge: queue the visibility write to replay on reconnect.
277
- await queueWrite("tier5.himiOwnerLiteProduct." + (visible ? "show" : "hide"), { id: s.id });
278
- return shape({ ...optimistic, actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
279
- }
280
- return shape({ ...s, actionMessage: ownerLoadError(e) });
269
+ // Nothing replays a queued write: the record is as it was, and the owner is told why.
270
+ return shape({ ...s, actionMessage: ownerWriteError(e) });
271
+ }
272
+ // Wix confirmed it: the list repaints from cache at once, and a re-read that fails
273
+ // afterwards still shows the confirmed change rather than undoing it on screen.
274
+ const confirmed = shape({ ...s, visibleRaw: visible, statusLabel: visible ? "Visible in store" : "Hidden", subtitle: visible ? "Visible in store" : "Hidden from store", actionMessage: msg });
275
+ await cachePut(cacheRow(confirmed));
276
+ try {
277
+ const next = await refresh(s, s.id, msg);
278
+ return next.status === "error" ? confirmed : next;
279
+ } catch {
280
+ return confirmed;
281
281
  }
282
282
  }
283
283
 
@@ -321,9 +321,10 @@ const svc = defineService<ProductState, {
321
321
  return await refresh(shape({ ...s, id, status: "loading" }), id);
322
322
  } catch (e) {
323
323
  // Offline: fall back to the last-synced local row before the demo/error paths.
324
- const cached = await cacheGet(id);
324
+ // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
325
+ const cached = isNotFound(e) ? (await cacheDrop(id), null) : await cacheGet(id);
325
326
  if (cached) return cachedDetail(s, cached);
326
- if (isBridgeUnavailable(e)) {
327
+ if (ownerDemoFallback(e)) {
327
328
  const demo = demoDetail(s, id);
328
329
  if (demo) return demo;
329
330
  }
@@ -342,9 +343,10 @@ const svc = defineService<ProductState, {
342
343
  try {
343
344
  return await refresh(shape({ ...s, status: "loading" }), s.id);
344
345
  } catch (e) {
345
- const cached = await cacheGet(s.id);
346
+ // Wix says it is gone (e.g. a delete whose reply was lost): drop the row, never show it.
347
+ const cached = isNotFound(e) ? (await cacheDrop(s.id), null) : await cacheGet(s.id);
346
348
  if (cached) return cachedDetail(s, cached);
347
- if (isBridgeUnavailable(e)) {
349
+ if (ownerDemoFallback(e)) {
348
350
  const demo = demoDetail(s, s.id);
349
351
  if (demo) return demo;
350
352
  }
@@ -368,18 +370,15 @@ const svc = defineService<ProductState, {
368
370
  const s = disarm(s0);
369
371
  if (isOwnerPreviewDemo() || s.demoNote) return shape({ ...s, actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
370
372
  if (!s.id) return shape({ ...s, actionMessage: copyParam("ownerProduct.noIdMessage", "No product id.") });
371
- // Optimistic local delete — the list drops the row immediately.
372
- await cacheDrop(s.id);
373
373
  try {
374
374
  await deleteProduct(s.id);
375
- return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerProduct.deletedEmpty", "Product deleted."), actionMessage: "" });
376
375
  } catch (e) {
377
- if (shouldQueueOwnerWrite(e)) {
378
- await queueWrite("tier5.himiOwnerLiteProduct.delete", { id: s.id });
379
- return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerProduct.deletedEmpty", "Product deleted."), actionMessage: copyParam("ownerCommon.demoSaveNote", DEMO_SAVE_NOTE) });
380
- }
381
- return shape({ ...s, actionMessage: ownerLoadError(e) });
376
+ // Nothing replays a queued write: the record stays, and the owner is told why.
377
+ return shape({ ...s, actionMessage: ownerWriteError(e) });
382
378
  }
379
+ // Only a delete Wix confirmed leaves the local list.
380
+ await cacheDrop(s.id);
381
+ return shape({ ...s, status: "deleted", title: "", emptyText: copyParam("ownerProduct.deletedEmpty", "Product deleted."), actionMessage: "" });
383
382
  } finally {
384
383
  deleteInFlight = false;
385
384
  }
@@ -15,29 +15,28 @@ import {
15
15
  type WixProduct,
16
16
  type WixProductWrite,
17
17
  } from "@himalaya/wix-stores";
18
- import { get as dbGet, upsert as dbUpsert, remove as dbRemove, enqueue as dbEnqueue, type DbEntity } from "../_shared/owner-local-db.js";
18
+ import { get as dbGet, upsert as dbUpsert, remove as dbRemove, type DbEntity } from "../_shared/owner-local-db.js";
19
19
  import { htmlIsTrivial, htmlPreviewText } from "../_shared/owner-rich-guard.js";
20
20
  import { postBodyText, plainPostBody } from "../_shared/owner-post-content.js";
21
21
  import { editorSubmitValue, queuedDocument, editorChanged, cachedRichVerdict, editorRunKeys, resetHydratedEditorOnOlderRuntime, editorAfterSave, liveState, settleOverLive } from "../_shared/owner-editor-value.js";
22
22
  import { parseRicosJson } from "@himalaya/wix-rich-content";
23
23
  import { ricosUploadService } from "@himalaya/wix-rich-content-editor";
24
- import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo, shouldQueueOwnerWrite } from "../_shared/owner-load-error.js";
24
+ import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError } from "../_shared/owner-load-error.js";
25
25
  import { DEMO_PRODUCTS, DEMO_NOTE, DEMO_SAVE_NOTE, demoRefusalPatch, editablePrice } from "../_shared/owner-preview-demo.js";
26
26
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
27
27
 
28
28
  type Mode = "create" | "edit";
29
29
 
30
- // Offline-first (data-offline): owner CRUD is WRITE-BEHIND against the encrypted
31
- // `owner_products` entity — the local store is updated OPTIMISTICALLY so the list
32
- // repaints from cache immediately, then the Wix write is attempted; if it can't
33
- // reach the bridge/network the mutation is ENQUEUED for replay on reconnect. Every
34
- // local-db call is wrapped (try/catch) so a build that ships without the
35
- // `local_database` grant behaves exactly as before (online-only, no cache/queue).
30
+ // Offline-first (data-offline): writes to the encrypted `owner_products` entity are
31
+ // SERVER-CONFIRMED — the list's local row changes only after Wix accepts the save;
32
+ // a failed one leaves it alone, keeps the form, and tells the owner why. No host
33
+ // replays a queued write, so nothing is queued.
34
+ // Every local-db call is wrapped (try/catch) so a build that ships without the
35
+ // `local_database` grant behaves exactly as before (online-only, no cache).
36
36
  const ENTITY = "owner_products";
37
37
  async function cachePut(row: DbEntity): Promise<void> { try { await dbUpsert(ENTITY, row); } catch { /* capability not granted */ } }
38
38
  async function cacheDrop(id: string): Promise<void> { try { await dbRemove(ENTITY, id); } catch { /* capability not granted */ } }
39
39
  async function cacheGet(id: string): Promise<DbEntity | null> { try { return await dbGet(ENTITY, id); } catch { return null; } }
40
- async function queueWrite(actionId: string, params: Record<string, string>): Promise<void> { try { await dbEnqueue(actionId, params); } catch { /* capability not granted */ } }
41
40
 
42
41
  // The store's currency — the Apptestersite detail view shows € prices.
43
42
  const CURRENCY = "EUR";
@@ -268,7 +267,7 @@ const svc = defineService<ProductFormState, {
268
267
  status: "idle", error: "", savedMessage: "",
269
268
  });
270
269
  }
271
- if (isBridgeUnavailable(e)) {
270
+ if (ownerDemoFallback(e)) {
272
271
  const d = DEMO_PRODUCTS.find((p) => p.id === id);
273
272
  if (d) {
274
273
  return shape({
@@ -356,7 +355,7 @@ const svc = defineService<ProductFormState, {
356
355
  const knownPlain = s.descriptionMode === "plain" || (s.descriptionMode === "editor" && !s.descriptionPending && s.descriptionJson === "");
357
356
  const descriptionProtected = cachedRichVerdict(heldDoc as never, knownPlain);
358
357
 
359
- // Optimistic local write — the list repaints from cache immediately (offline or not).
358
+ // The row the cache gets once Wix accepts the save.
360
359
  // Replay starts with default worker state. An untouched description was omitted from the
361
360
  // queued PATCH, so retain its cached mirror/document/verdict when replacing the local row.
362
361
  const cached = replay && targetId ? await cacheGet(targetId) : null;
@@ -380,7 +379,6 @@ const svc = defineService<ProductFormState, {
380
379
  return { descriptionDirty: a.dirty, descriptionEdits: a.edits, ...(a.editedDuringSave ? { status: "idle" as const, savedMessage: "" } : {}) };
381
380
  };
382
381
  const localId = mode === "edit" && targetId ? targetId : `local-${Date.now()}`;
383
- await cachePut(localRow(localId));
384
382
  try {
385
383
  let id = targetId;
386
384
  if (mode === "edit" && targetId) {
@@ -388,9 +386,9 @@ const svc = defineService<ProductFormState, {
388
386
  } else {
389
387
  const r = await createProduct(write);
390
388
  id = r.product?.id || localId;
391
- // Reconcile the optimistic local id with the server-assigned id.
392
- if (id !== localId) { await cacheDrop(localId); await cachePut(localRow(id)); }
393
389
  }
390
+ // The local row follows only what Wix confirmed, under the id Wix assigned.
391
+ await cachePut(localRow(id));
394
392
  return shape({
395
393
  ...s,
396
394
  mode: "edit",
@@ -407,44 +405,26 @@ const svc = defineService<ProductFormState, {
407
405
  ...afterSaved(),
408
406
  });
409
407
  } catch (e) {
410
- if (shouldQueueOwnerWrite(e)) {
411
- // Demo / web-preview (no owner bridge; the record came from DEMO_PRODUCTS,
412
- // marked by demoNote): a queued write could never replay, so answer with
413
- // the demo save note instead of the real-offline write-behind.
414
- if (s.demoNote) return demoRefusal(s);
415
- // Real device offline: the optimistic cache row already shows; queue the
416
- // write to replay (WriteQueue, idempotent) when connectivity returns.
417
- // A document rides as `descriptionJson` with descriptionEdited "false", so a bundle rolled back
418
- // past this change leaves the description rather than flattening it to text.
419
- await queueWrite("tier5.himiOwnerLiteProductForm.submit", { id: targetId, name, price, sku, description, visible: String(visible), mode,
420
- ...(docRaw ? { descriptionJson: docRaw, descriptionEdited: "false" } : { descriptionEdited: String(descriptionEdited) }) });
421
- return shape({
422
- ...s, mode: "edit", id: localId, name, price, sku, ...savedDescription,
423
- visible: visible ? "true" : "false", status: "saved",
424
- savedMessage: copyParam("ownerProductForm.savedOffline", "Saved offline — will sync when you reconnect."),
425
- savedId: localId,
426
- ...afterSaved(),
427
- });
428
- }
429
- return shape({ ...s, status: "error", error: ownerLoadError(e), savedMessage: "" });
408
+ // Never "saved offline": nothing replays a queued write on any platform. The cache was
409
+ // not touched, and the typed values stay in the form (the description in its editor) so
410
+ // a retry is one tap.
411
+ // Demo / web-preview: the record came from DEMO_PRODUCTS (marked by demoNote).
412
+ if (s.demoNote) return demoRefusal(s);
413
+ return shape({ ...s, name, price, sku, ...(s.descriptionMode === "editor" ? {} : { description }), visible: visible ? "true" : "false", status: "error", error: ownerWriteError(e), savedMessage: "" });
430
414
  }
431
415
  })()),
432
416
  remove: async (s) => {
433
417
  if (isOwnerPreviewDemo() || s.demoNote) return demoRefusal(s);
434
418
  if (!s.id) return shape({ ...s, status: "error", error: copyParam("ownerProductForm.errorNoDelete", "No product to delete."), savedMessage: "" });
435
- // Optimistic local delete — the list drops the row immediately.
436
- await cacheDrop(s.id);
437
419
  try {
438
420
  await deleteProduct(s.id);
439
- return shape({ ...s, id: "", status: "saved", error: "", savedMessage: copyParam("ownerProductForm.deletedMessage", "Product deleted."), savedId: "" });
440
421
  } catch (e) {
441
- if (shouldQueueOwnerWrite(e)) {
442
- if (s.demoNote) return demoRefusal(s);
443
- await queueWrite("tier5.himiOwnerLiteProductForm.remove", { id: s.id });
444
- return shape({ ...s, id: "", status: "saved", error: "", savedMessage: copyParam("ownerProductForm.deletedOffline", "Deleted offline — will sync when you reconnect."), savedId: "" });
445
- }
446
- return shape({ ...s, status: "error", error: ownerLoadError(e), savedMessage: "" });
422
+ // Nothing replays a queued delete: the row stays, and the owner is told why.
423
+ return shape({ ...s, status: "error", error: ownerWriteError(e), savedMessage: "" });
447
424
  }
425
+ // Only a delete Wix confirmed leaves the local list.
426
+ await cacheDrop(s.id);
427
+ return shape({ ...s, id: "", status: "saved", error: "", savedMessage: copyParam("ownerProductForm.deletedMessage", "Product deleted."), savedId: "" });
448
428
  },
449
429
  },
450
430
  });
@@ -7,7 +7,7 @@
7
7
  import { compareText } from "../_shared/engine-intl.js";
8
8
  import { defineBackendCollection } from "../_shared/collection-service.js";
9
9
  import { searchStoreProducts, productImage, productPrice, plainText, type StoreProduct } from "../_shared/himi-store.js";
10
- import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo } from "../_shared/owner-load-error.js";
10
+ import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback } from "../_shared/owner-load-error.js";
11
11
  import { DEMO_PRODUCTS, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
12
12
  import { copyParam } from "../_shared/himi-copy.js";
13
13
  import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
@@ -191,7 +191,7 @@ const svc = defineBackendCollection("himiOwnerLiteProducts", {
191
191
  const items = (await readCachedRows()).filter(item => !!item.id);
192
192
  if (items.length) return { items, nextCursor: "", complete: false, metadata: { fromCache: true, demoNote: "" } };
193
193
  }
194
- if (!cursor && isBridgeUnavailable(error)) return demoPage();
194
+ if (!cursor && ownerDemoFallback(error)) return demoPage();
195
195
  throw error;
196
196
  }
197
197
  },
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { defineService } from "@himalaya/state";
10
10
  import { querySites, type WixSite } from "@himalaya/wix-site-list";
11
- import { ownerLoadError, isBridgeUnavailable, isOwnerPreviewDemo } from "../_shared/owner-load-error.js";
11
+ import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback } from "../_shared/owner-load-error.js";
12
12
  import { DEMO_SITES, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
13
13
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
14
14
  import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
@@ -165,7 +165,7 @@ async function loadList(s: SitesState): Promise<SitesState> {
165
165
  } catch (e) {
166
166
  // Offline / fetch failure: keep the last-synced cache BEFORE the demo/error fallbacks.
167
167
  if (repairedCache.length) return base;
168
- if (isBridgeUnavailable(e)) return demoList(s);
168
+ if (ownerDemoFallback(e)) return demoList(s);
169
169
  return shape({ ...s, status: "error", allSites: [], sites: [], error: ownerLoadError(e) });
170
170
  }
171
171
  }
@@ -0,0 +1,315 @@
1
+ // Tier-5 bundle: __HIMI_APP_NAME__ — TODAY. The worker behind the Home Screen widget.
2
+ //
3
+ // A WidgetKit extension (and a Glance receiver) runs no JS and holds no Wix session, and the OTA
4
+ // widget producer has no credential either, so neither can read an owner's business. The PHONE
5
+ // app runs this worker headlessly with its owner Wix bridge, resolves the widget descriptors'
6
+ // `$service:ownerToday.*` leaves to literals (CompanionDescriptorResolver, the same pattern the
7
+ // watch companion uses), and writes the frozen tree to the App Group for the widget to render.
8
+ //
9
+ // One owner glance: "what's next, and what is waiting for me" — the next booking, the booking
10
+ // requests waiting on the owner, and the orders that need attention. Read-only: a widget never
11
+ // writes to the business.
12
+ //
13
+ // Each source degrades on its own. A site without Bookings answers 428/404 on the bookings
14
+ // query, and that must not cost the order count (or the reverse).
15
+
16
+ import { defineService } from "@himalaya/state";
17
+ import { queryBookings } from "@himalaya/wix-bookings";
18
+ import { searchOrders } from "@himalaya/wix-ecom";
19
+ import { ownerOrderAttentionFilter } from "../_shared/owner-order-attention.js";
20
+ import { readSelectedSite } from "../_shared/owner-selected-site.js";
21
+
22
+ type Rec = Record<string, unknown>;
23
+
24
+ /** One booking as the widget shows it. Client FIRST name only: a Home Screen is visible to
25
+ * anyone near the phone. */
26
+ export interface TodayBooking {
27
+ bookingId: string;
28
+ name: string;
29
+ service: string;
30
+ timeLabel: string; // "10:30"
31
+ dayLabel: string; // "Today" / "Tomorrow" / "Thu 16 Oct"
32
+ /** "Today 10:30" — the absolute moment, never a countdown: a frozen "in 21 min" is wrong a
33
+ * minute later, and nothing re-renders the widget between app foregrounds. */
34
+ whenLabel: string;
35
+ }
36
+
37
+ export interface TodayState {
38
+ status: "loading" | "ready" | "error" | "signedOut";
39
+ siteName: string;
40
+ /** Header line: "Today · <site>", or "Today" when the site has no name yet. */
41
+ header: string;
42
+ next: TodayBooking;
43
+ hasNext: boolean;
44
+ /** Hide the next-booking block: nothing is booked, or Bookings cannot be read. */
45
+ hideNext: boolean;
46
+ /** "No upcoming bookings" — shown in place of the block when Bookings has nothing ahead. */
47
+ nextNote: string;
48
+ hideNextNote: boolean;
49
+ requestCount: number;
50
+ /** "2 requests" / "1 request" / "No requests". At the read cap: "50+ requests". */
51
+ requestLabel: string;
52
+ orderCount: number;
53
+ /** "3 orders to handle" / "1 order to handle" / "No orders waiting". */
54
+ orderLabel: string;
55
+ /** Compact counts for the small widget: "2 req · 3 orders". */
56
+ countsShort: string;
57
+ bookingsReady: boolean;
58
+ ordersReady: boolean;
59
+ hideRequests: boolean;
60
+ hideOrders: boolean;
61
+ /** One-line state copy for a degrade (signed out, read failed, nothing installed). */
62
+ message: string;
63
+ hideMessage: boolean;
64
+ /** Epoch SECONDS after which this frame's labels are wrong: the next local midnight ("Today" /
65
+ * "Tomorrow" shift) or the next booking's start (it is no longer "next"), whichever is first.
66
+ * The phone caps the snapshot's lifetime at it, so a frozen frame never outlives its words. */
67
+ validUntil: number;
68
+ }
69
+
70
+ /** Which verticals THIS build provisions — baked into the widget descriptors' `input`. A vertical
71
+ * the build does not show is never read: its count must not reach the compact line, and its
72
+ * transient failure must not withhold a frame about the sections the build does show. */
73
+ export interface Verticals {
74
+ bookings: boolean;
75
+ store: boolean;
76
+ }
77
+
78
+ export const COPY = {
79
+ signedOut: "Sign in to Owner to see today",
80
+ error: "Open Owner to refresh",
81
+ nothing: "Open Owner to see your business",
82
+ noneUpcoming: "No upcoming bookings",
83
+ } as const;
84
+
85
+ const PENDING = ["PENDING", "PENDING_APPROVAL", "WAITING_LIST"];
86
+ /** A glance, not a list: a count at the cap reads as "50+". */
87
+ export const LIMIT = 50;
88
+
89
+ const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
90
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
91
+
92
+ function str(r: unknown, ...paths: string[]): string {
93
+ for (const path of paths) {
94
+ let v: unknown = r;
95
+ for (const k of path.split(".")) v = v && typeof v === "object" ? (v as Rec)[k] : undefined;
96
+ if (typeof v === "string" && v) return v;
97
+ if (typeof v === "number") return String(v);
98
+ }
99
+ return "";
100
+ }
101
+
102
+ function hhmm(d: Date): string {
103
+ return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
104
+ }
105
+
106
+ function sameLocalDay(a: Date, b: Date): boolean {
107
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
108
+ }
109
+
110
+ /** The day a booking falls on, as an owner would say it. */
111
+ export function dayLabel(start: Date, now: Date): string {
112
+ if (sameLocalDay(start, now)) return "Today";
113
+ const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
114
+ if (sameLocalDay(start, tomorrow)) return "Tomorrow";
115
+ return `${WEEKDAYS[start.getDay()]} ${start.getDate()} ${MONTHS[start.getMonth()]}`;
116
+ }
117
+
118
+ function startOf(b: Rec): string {
119
+ return str(b, "startDate", "bookedEntity.slot.startDate");
120
+ }
121
+
122
+ function booking(b: Rec, now: Date): TodayBooking {
123
+ const start = new Date(startOf(b));
124
+ const day = dayLabel(start, now);
125
+ return {
126
+ bookingId: str(b, "id"),
127
+ name: str(b, "contactDetails.firstName") || "Client",
128
+ service: str(b, "bookedEntity.title", "bookedEntity.slot.serviceName") || "Appointment",
129
+ timeLabel: hhmm(start),
130
+ dayLabel: day,
131
+ whenLabel: `${day} ${hhmm(start)}`,
132
+ };
133
+ }
134
+
135
+ const EMPTY_BOOKING: TodayBooking = { bookingId: "", name: "", service: "", timeLabel: "", dayLabel: "", whenLabel: "" };
136
+
137
+ function counted(n: number, capped: boolean, one: string, many: string): string {
138
+ return `${capped ? `${LIMIT}+` : n} ${n === 1 && !capped ? one : many}`;
139
+ }
140
+
141
+ export interface Sources {
142
+ /** Upcoming CONFIRMED bookings, or null when Bookings could not be read. */
143
+ bookings: Rec[] | null;
144
+ /** Upcoming booking REQUESTS (pending statuses), read on their own: one page of every status
145
+ * would fill with a busy week's confirmed bookings and hide next week's request. */
146
+ requests: Rec[] | null;
147
+ /** Orders needing attention, or null when Store could not be read. */
148
+ orders: Rec[] | null;
149
+ }
150
+
151
+ function upcoming(rows: Rec[] | null, statuses: string[], now: Date): Rec[] {
152
+ return (rows ?? [])
153
+ .filter((b) => str(b, "id") && statuses.includes(str(b, "status").toUpperCase()))
154
+ .filter((b) => Date.parse(startOf(b)) >= now.getTime())
155
+ .sort((a, b) => Date.parse(startOf(a)) - Date.parse(startOf(b)) || str(a, "id").localeCompare(str(b, "id")));
156
+ }
157
+
158
+ /** Pure: shape what was read into the widget state. `now` is injectable for tests. */
159
+ export function shape(sources: Sources, now: Date, siteName: string): TodayState {
160
+ const bookingsReady = sources.bookings !== null && sources.requests !== null;
161
+ const ordersReady = sources.orders !== null;
162
+ // Re-filtered even though the queries filter: a server (or a fixture) that returns a
163
+ // cancelled or past row must not become an appointment the owner is told to expect.
164
+ const confirmed = bookingsReady ? upcoming(sources.bookings, ["CONFIRMED"], now) : [];
165
+ // Today's next CONFIRMED booking first: a request is not an appointment yet. With none left
166
+ // today, the first confirmed one after today, so "what's next" still has an answer.
167
+ const next = confirmed.find((b) => sameLocalDay(new Date(startOf(b)), now)) ?? confirmed[0];
168
+ const requestCount = bookingsReady ? upcoming(sources.requests, PENDING, now).length : 0;
169
+ const orderCount = (sources.orders ?? []).length;
170
+ const requestsCapped = (sources.requests ?? []).length >= LIMIT;
171
+ const ordersCapped = orderCount >= LIMIT;
172
+
173
+ const shortParts = [
174
+ ...(bookingsReady ? [`${requestsCapped ? `${LIMIT}+` : requestCount} req`] : []),
175
+ ...(ordersReady ? [counted(orderCount, ordersCapped, "order", "orders")] : []),
176
+ ];
177
+ const nothing = !bookingsReady && !ordersReady;
178
+ const nextNote = bookingsReady && !next ? COPY.noneUpcoming : "";
179
+ const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).getTime();
180
+ const nextStart = next ? Date.parse(startOf(next)) : Infinity;
181
+ return {
182
+ validUntil: Math.floor(Math.min(midnight, nextStart) / 1000),
183
+ status: "ready",
184
+ siteName,
185
+ header: siteName ? `Today · ${siteName}` : "Today",
186
+ next: next ? booking(next, now) : EMPTY_BOOKING,
187
+ hasNext: !!next,
188
+ hideNext: !next,
189
+ nextNote,
190
+ hideNextNote: !nextNote,
191
+ requestCount,
192
+ requestLabel: requestCount ? counted(requestCount, requestsCapped, "request", "requests") : "No requests",
193
+ orderCount,
194
+ orderLabel: orderCount ? `${counted(orderCount, ordersCapped, "order", "orders")} to handle` : "No orders waiting",
195
+ countsShort: shortParts.join(" · "),
196
+ bookingsReady,
197
+ ordersReady,
198
+ hideRequests: !bookingsReady,
199
+ hideOrders: !ordersReady,
200
+ message: nothing ? COPY.nothing : "",
201
+ hideMessage: !nothing,
202
+ };
203
+ }
204
+
205
+ /** A degrade frame: ONE line, no rows. After a sign-out or a business switch the last rows
206
+ * belong to someone else's clients, so none of them survive into it. */
207
+ export function degraded(status: "error" | "signedOut" | "loading", siteName: string): TodayState {
208
+ // A FIXED instant, not the clock: a degrade frame has no rows, so nothing in it depends on
209
+ // the time — and the initial state must be deterministic (template contract: no worker's
210
+ // initial state may read the clock).
211
+ const base = shape({ bookings: null, requests: null, orders: null }, new Date(0), siteName);
212
+ const message = status === "signedOut" ? COPY.signedOut : status === "error" ? COPY.error : "";
213
+ // validUntil pinned too: shape() derives it from LOCAL midnight, so even at a fixed instant it
214
+ // would vary with the build machine's timezone and change the baked services hash.
215
+ return { ...base, status, message, hideMessage: !message, validUntil: 0 };
216
+ }
217
+
218
+ /** No usable owner session behind the bridge (the bridge refusing, or a 401). A transient
219
+ * 5xx is an ordinary error, not a sign-out — and a 403 is a missing PERMISSION for one
220
+ * vertical (a collaborator whose role lacks Bookings), not a sign-out either. */
221
+ export function isSignedOut(e: unknown): boolean {
222
+ const text = String((e as Error)?.message ?? e);
223
+ return /^HTTP 401\b/.test(text) || /wixApi not enabled|session unavailable|not signed in|webwix_bridge/i.test(text);
224
+ }
225
+
226
+ /** This section cannot exist for this owner: the vertical is not installed on the site (its
227
+ * API answers 428, or 404 for the route), or the owner's role may not read it (403). Hides
228
+ * that one section; every other failure is an error for the whole frame.
229
+ *
230
+ * Read from the LEADING status only — the bridges format a failure as `HTTP <status>: <body>`
231
+ * or `HTTP <status> [<code>]: <message>` — never from anywhere in the message: a transient
232
+ * `HTTP 503: {"message":"upstream returned 404"}` must stay an error, not hide the section. */
233
+ export function isSectionUnavailable(e: unknown): boolean {
234
+ const text = String((e as Error)?.message ?? e);
235
+ return /^HTTP (?:403|404|428)\b/.test(text) || /^HTTP \d{3} \[(?:FAILED_PRECONDITION|PERMISSION_DENIED)\]/.test(text);
236
+ }
237
+
238
+ function siteName(): string {
239
+ return readSelectedSite()?.name ?? "";
240
+ }
241
+
242
+ type Read = { ok: true; value: unknown } | { ok: false; error: unknown };
243
+
244
+ async function attempt(fn: () => Promise<unknown>): Promise<Read> {
245
+ try {
246
+ return { ok: true, value: await fn() };
247
+ } catch (error) {
248
+ return { ok: false, error };
249
+ }
250
+ }
251
+
252
+ function rows(r: Read, key: string): Rec[] | null {
253
+ if (!r.ok) return null;
254
+ const list = (r.value as Rec | undefined)?.[key];
255
+ return Array.isArray(list) ? (list as Rec[]) : [];
256
+ }
257
+
258
+ /** The build's verticals from the descriptor's baked `input.verticals` ("bookings,store"). A
259
+ * host that passes none (an older descriptor) reads both, as before. */
260
+ export function buildVerticals(): Verticals {
261
+ const raw = (globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input?.verticals;
262
+ if (typeof raw !== "string") return { bookings: true, store: true };
263
+ const set = new Set(raw.split(",").map((v) => v.trim()));
264
+ return { bookings: set.has("bookings"), store: set.has("store") };
265
+ }
266
+
267
+ /** A section this build does not provision: never read, and indistinguishable from "not
268
+ * installed" downstream (hidden, out of the compact line, out of the error rule). */
269
+ const NOT_PROVISIONED: Read = { ok: false, error: new Error("HTTP 404: not provisioned in this build") };
270
+
271
+ async function read(): Promise<TodayState> {
272
+ const nowIso = new Date().toISOString();
273
+ const verticals = buildVerticals();
274
+ const sort = [{ fieldName: "startDate", order: "ASC" }, { fieldName: "id", order: "ASC" }];
275
+ const bookingsBy = (statuses: string[]) => verticals.bookings ? attempt(() => queryBookings({ query: {
276
+ filter: { startDate: { $gte: nowIso }, status: { $in: statuses } },
277
+ sort,
278
+ paging: { limit: LIMIT, offset: 0 },
279
+ } })) : Promise.resolve(NOT_PROVISIONED);
280
+ const reads = await Promise.all([
281
+ bookingsBy(["CONFIRMED"]),
282
+ bookingsBy(PENDING),
283
+ verticals.store ? attempt(() => searchOrders({ search: {
284
+ filter: ownerOrderAttentionFilter(),
285
+ sort: [{ fieldName: "createdDate", order: "DESC" }, { fieldName: "id", order: "ASC" }],
286
+ cursorPaging: { limit: LIMIT },
287
+ } })) : Promise.resolve(NOT_PROVISIONED),
288
+ ]);
289
+ const failures = reads.flatMap((r) => (r.ok ? [] : [r.error]));
290
+ if (failures.some(isSignedOut)) return degraded("signedOut", "");
291
+ // A section that cannot exist (not installed, no permission) is hidden. ANY other failure —
292
+ // a transient 5xx on one read included — makes this an error frame, which the phone does
293
+ // not publish: the last good frame stays, rather than a frame that silently drops a
294
+ // section the owner does have and reads as "nothing waiting".
295
+ if (failures.some((e) => !isSectionUnavailable(e))) return degraded("error", siteName());
296
+ const [bookings, requests, orders] = reads;
297
+ return shape({
298
+ bookings: rows(bookings, "bookings"),
299
+ requests: rows(requests, "bookings"),
300
+ orders: rows(orders, "orders"),
301
+ }, new Date(), siteName());
302
+ }
303
+
304
+ const svc = defineService<TodayState, {
305
+ load: (s: TodayState) => Promise<TodayState>;
306
+ refresh: (s: TodayState) => Promise<TodayState>;
307
+ }>("ownerToday", {
308
+ state: degraded("loading", ""),
309
+ actions: {
310
+ load: async () => read(),
311
+ refresh: async () => read(),
312
+ },
313
+ });
314
+
315
+ export { svc };