@aglyn/plugins-commerce 1.0.0-beta.143

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 (417) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +10 -0
  3. package/package.json +57 -0
  4. package/src/index.d.ts +38 -0
  5. package/src/index.js +38 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/account.d.ts +31 -0
  8. package/src/lib/components/account.js +645 -0
  9. package/src/lib/components/account.js.map +1 -0
  10. package/src/lib/components/cart.d.ts +38 -0
  11. package/src/lib/components/cart.js +851 -0
  12. package/src/lib/components/cart.js.map +1 -0
  13. package/src/lib/components/commerce-console-page.d.ts +32 -0
  14. package/src/lib/components/commerce-console-page.js +296 -0
  15. package/src/lib/components/commerce-console-page.js.map +1 -0
  16. package/src/lib/components/commerce-console-sections.d.ts +34 -0
  17. package/src/lib/components/commerce-console-sections.js +62 -0
  18. package/src/lib/components/commerce-console-sections.js.map +1 -0
  19. package/src/lib/components/console/catalog-organization-card.component.d.ts +14 -0
  20. package/src/lib/components/console/catalog-organization-card.component.js +1087 -0
  21. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -0
  22. package/src/lib/components/console/commerce-analytics-card.component.d.ts +27 -0
  23. package/src/lib/components/console/commerce-analytics-card.component.js +310 -0
  24. package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -0
  25. package/src/lib/components/console/commerce-glance-card.component.d.ts +20 -0
  26. package/src/lib/components/console/commerce-glance-card.component.js +340 -0
  27. package/src/lib/components/console/commerce-glance-card.component.js.map +1 -0
  28. package/src/lib/components/console/commerce-stat-tile.component.d.ts +37 -0
  29. package/src/lib/components/console/commerce-stat-tile.component.js +72 -0
  30. package/src/lib/components/console/commerce-stat-tile.component.js.map +1 -0
  31. package/src/lib/components/console/discounts-card.component.d.ts +14 -0
  32. package/src/lib/components/console/discounts-card.component.js +443 -0
  33. package/src/lib/components/console/discounts-card.component.js.map +1 -0
  34. package/src/lib/components/console/entitlement-gate.component.d.ts +76 -0
  35. package/src/lib/components/console/entitlement-gate.component.js +115 -0
  36. package/src/lib/components/console/entitlement-gate.component.js.map +1 -0
  37. package/src/lib/components/console/gift-cards-card.component.d.ts +29 -0
  38. package/src/lib/components/console/gift-cards-card.component.js +379 -0
  39. package/src/lib/components/console/gift-cards-card.component.js.map +1 -0
  40. package/src/lib/components/console/host-coupons-card.component.d.ts +14 -0
  41. package/src/lib/components/console/host-coupons-card.component.js +252 -0
  42. package/src/lib/components/console/host-coupons-card.component.js.map +1 -0
  43. package/src/lib/components/console/host-orders-card.component.d.ts +13 -0
  44. package/src/lib/components/console/host-orders-card.component.js +861 -0
  45. package/src/lib/components/console/host-orders-card.component.js.map +1 -0
  46. package/src/lib/components/console/locations-card.component.d.ts +14 -0
  47. package/src/lib/components/console/locations-card.component.js +256 -0
  48. package/src/lib/components/console/locations-card.component.js.map +1 -0
  49. package/src/lib/components/console/member-posts-card.component.d.ts +8 -0
  50. package/src/lib/components/console/member-posts-card.component.js +283 -0
  51. package/src/lib/components/console/member-posts-card.component.js.map +1 -0
  52. package/src/lib/components/console/newest-site-users-card.component.d.ts +36 -0
  53. package/src/lib/components/console/newest-site-users-card.component.js +126 -0
  54. package/src/lib/components/console/newest-site-users-card.component.js.map +1 -0
  55. package/src/lib/components/console/order-detail-dialog.component.d.ts +28 -0
  56. package/src/lib/components/console/order-detail-dialog.component.js +976 -0
  57. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -0
  58. package/src/lib/components/console/paid-media.d.ts +127 -0
  59. package/src/lib/components/console/paid-media.js +397 -0
  60. package/src/lib/components/console/paid-media.js.map +1 -0
  61. package/src/lib/components/console/payments-settings-card.component.d.ts +13 -0
  62. package/src/lib/components/console/payments-settings-card.component.js +295 -0
  63. package/src/lib/components/console/payments-settings-card.component.js.map +1 -0
  64. package/src/lib/components/console/pos-page.component.d.ts +13 -0
  65. package/src/lib/components/console/pos-page.component.js +916 -0
  66. package/src/lib/components/console/pos-page.component.js.map +1 -0
  67. package/src/lib/components/console/product-editor-dialog.component.d.ts +32 -0
  68. package/src/lib/components/console/product-editor-dialog.component.js +1374 -0
  69. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -0
  70. package/src/lib/components/console/products-hub-card.component.d.ts +16 -0
  71. package/src/lib/components/console/products-hub-card.component.js +1280 -0
  72. package/src/lib/components/console/products-hub-card.component.js.map +1 -0
  73. package/src/lib/components/console/products-hub-zone.component.d.ts +50 -0
  74. package/src/lib/components/console/products-hub-zone.component.js +200 -0
  75. package/src/lib/components/console/products-hub-zone.component.js.map +1 -0
  76. package/src/lib/components/console/recovery-queue-card.component.d.ts +39 -0
  77. package/src/lib/components/console/recovery-queue-card.component.js +262 -0
  78. package/src/lib/components/console/recovery-queue-card.component.js.map +1 -0
  79. package/src/lib/components/console/registers-card.component.d.ts +21 -0
  80. package/src/lib/components/console/registers-card.component.js +296 -0
  81. package/src/lib/components/console/registers-card.component.js.map +1 -0
  82. package/src/lib/components/console/reservations-card.component.d.ts +14 -0
  83. package/src/lib/components/console/reservations-card.component.js +650 -0
  84. package/src/lib/components/console/reservations-card.component.js.map +1 -0
  85. package/src/lib/components/console/reviews-moderation-card.component.d.ts +8 -0
  86. package/src/lib/components/console/reviews-moderation-card.component.js +235 -0
  87. package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -0
  88. package/src/lib/components/console/shipping-settings-card.component.d.ts +38 -0
  89. package/src/lib/components/console/shipping-settings-card.component.js +560 -0
  90. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -0
  91. package/src/lib/components/console/stock-movements-card.component.d.ts +31 -0
  92. package/src/lib/components/console/stock-movements-card.component.js +333 -0
  93. package/src/lib/components/console/stock-movements-card.component.js.map +1 -0
  94. package/src/lib/components/console/store-settings-card.component.d.ts +14 -0
  95. package/src/lib/components/console/store-settings-card.component.js +300 -0
  96. package/src/lib/components/console/store-settings-card.component.js.map +1 -0
  97. package/src/lib/components/console/storefront-tax-summary-card.component.d.ts +73 -0
  98. package/src/lib/components/console/storefront-tax-summary-card.component.js +408 -0
  99. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -0
  100. package/src/lib/components/console/suppliers-card.component.d.ts +8 -0
  101. package/src/lib/components/console/suppliers-card.component.js +328 -0
  102. package/src/lib/components/console/suppliers-card.component.js.map +1 -0
  103. package/src/lib/components/console/tax-settings-card.component.d.ts +14 -0
  104. package/src/lib/components/console/tax-settings-card.component.js +473 -0
  105. package/src/lib/components/console/tax-settings-card.component.js.map +1 -0
  106. package/src/lib/components/gate.d.ts +39 -0
  107. package/src/lib/components/gate.js +202 -0
  108. package/src/lib/components/gate.js.map +1 -0
  109. package/src/lib/components/gated-video.d.ts +40 -0
  110. package/src/lib/components/gated-video.js +366 -0
  111. package/src/lib/components/gated-video.js.map +1 -0
  112. package/src/lib/components/member-feed.d.ts +32 -0
  113. package/src/lib/components/member-feed.js +198 -0
  114. package/src/lib/components/member-feed.js.map +1 -0
  115. package/src/lib/components/member-recovery.d.ts +35 -0
  116. package/src/lib/components/member-recovery.js +313 -0
  117. package/src/lib/components/member-recovery.js.map +1 -0
  118. package/src/lib/components/member-signin.d.ts +35 -0
  119. package/src/lib/components/member-signin.js +257 -0
  120. package/src/lib/components/member-signin.js.map +1 -0
  121. package/src/lib/components/member-signup.d.ts +34 -0
  122. package/src/lib/components/member-signup.js +287 -0
  123. package/src/lib/components/member-signup.js.map +1 -0
  124. package/src/lib/components/newsletter-signup.d.ts +39 -0
  125. package/src/lib/components/newsletter-signup.js +229 -0
  126. package/src/lib/components/newsletter-signup.js.map +1 -0
  127. package/src/lib/components/product-detail.d.ts +38 -0
  128. package/src/lib/components/product-detail.js +1022 -0
  129. package/src/lib/components/product-detail.js.map +1 -0
  130. package/src/lib/components/product-grid.d.ts +67 -0
  131. package/src/lib/components/product-grid.js +909 -0
  132. package/src/lib/components/product-grid.js.map +1 -0
  133. package/src/lib/components/product-reviews.d.ts +32 -0
  134. package/src/lib/components/product-reviews.js +349 -0
  135. package/src/lib/components/product-reviews.js.map +1 -0
  136. package/src/lib/components/related-products.d.ts +33 -0
  137. package/src/lib/components/related-products.js +232 -0
  138. package/src/lib/components/related-products.js.map +1 -0
  139. package/src/lib/components/reservation-widget.d.ts +33 -0
  140. package/src/lib/components/reservation-widget.js +418 -0
  141. package/src/lib/components/reservation-widget.js.map +1 -0
  142. package/src/lib/components/storefront-payment-element-fallback.d.ts +18 -0
  143. package/src/lib/components/storefront-payment-element-fallback.js +51 -0
  144. package/src/lib/components/storefront-payment-element-fallback.js.map +1 -0
  145. package/src/lib/components/storefront-payment-element.d.ts +78 -0
  146. package/src/lib/components/storefront-payment-element.js +156 -0
  147. package/src/lib/components/storefront-payment-element.js.map +1 -0
  148. package/src/lib/components/wishlist.d.ts +46 -0
  149. package/src/lib/components/wishlist.js +304 -0
  150. package/src/lib/components/wishlist.js.map +1 -0
  151. package/src/lib/constants/bundle-common.d.ts +18 -0
  152. package/src/lib/constants/bundle-common.js +18 -0
  153. package/src/lib/constants/bundle-common.js.map +1 -0
  154. package/src/lib/constants/product-filters.d.ts +35 -0
  155. package/src/lib/constants/product-filters.js +109 -0
  156. package/src/lib/constants/product-filters.js.map +1 -0
  157. package/src/lib/model/commerce-cart.d.ts +52 -0
  158. package/src/lib/model/commerce-cart.js +102 -0
  159. package/src/lib/model/commerce-cart.js.map +1 -0
  160. package/src/lib/model/commerce-connect-transfer.d.ts +243 -0
  161. package/src/lib/model/commerce-connect-transfer.js +231 -0
  162. package/src/lib/model/commerce-connect-transfer.js.map +1 -0
  163. package/src/lib/model/commerce-discounts.d.ts +164 -0
  164. package/src/lib/model/commerce-discounts.js +231 -0
  165. package/src/lib/model/commerce-discounts.js.map +1 -0
  166. package/src/lib/model/commerce-dispute.d.ts +124 -0
  167. package/src/lib/model/commerce-dispute.js +225 -0
  168. package/src/lib/model/commerce-dispute.js.map +1 -0
  169. package/src/lib/model/commerce-gift-cards.d.ts +107 -0
  170. package/src/lib/model/commerce-gift-cards.js +121 -0
  171. package/src/lib/model/commerce-gift-cards.js.map +1 -0
  172. package/src/lib/model/commerce-io.d.ts +36 -0
  173. package/src/lib/model/commerce-io.js +245 -0
  174. package/src/lib/model/commerce-io.js.map +1 -0
  175. package/src/lib/model/commerce-orders.d.ts +1040 -0
  176. package/src/lib/model/commerce-orders.js +1022 -0
  177. package/src/lib/model/commerce-orders.js.map +1 -0
  178. package/src/lib/model/commerce-promotions.d.ts +188 -0
  179. package/src/lib/model/commerce-promotions.js +199 -0
  180. package/src/lib/model/commerce-promotions.js.map +1 -0
  181. package/src/lib/model/commerce-reservations.d.ts +175 -0
  182. package/src/lib/model/commerce-reservations.js +157 -0
  183. package/src/lib/model/commerce-reservations.js.map +1 -0
  184. package/src/lib/model/commerce-shipping.d.ts +316 -0
  185. package/src/lib/model/commerce-shipping.js +484 -0
  186. package/src/lib/model/commerce-shipping.js.map +1 -0
  187. package/src/lib/model/commerce-stock-holds.d.ts +199 -0
  188. package/src/lib/model/commerce-stock-holds.js +190 -0
  189. package/src/lib/model/commerce-stock-holds.js.map +1 -0
  190. package/src/lib/model/commerce-subscription-status.d.ts +67 -0
  191. package/src/lib/model/commerce-subscription-status.js +76 -0
  192. package/src/lib/model/commerce-subscription-status.js.map +1 -0
  193. package/src/lib/model/commerce-tax-decision.d.ts +222 -0
  194. package/src/lib/model/commerce-tax-decision.js +165 -0
  195. package/src/lib/model/commerce-tax-decision.js.map +1 -0
  196. package/src/lib/model/commerce-tax.d.ts +176 -0
  197. package/src/lib/model/commerce-tax.js +92 -0
  198. package/src/lib/model/commerce-tax.js.map +1 -0
  199. package/src/lib/model/commerce.d.ts +551 -0
  200. package/src/lib/model/commerce.js +739 -0
  201. package/src/lib/model/commerce.js.map +1 -0
  202. package/src/lib/model/index.d.ts +40 -0
  203. package/src/lib/model/index.js +40 -0
  204. package/src/lib/model/index.js.map +1 -0
  205. package/src/lib/model/order-figures.d.ts +48 -0
  206. package/src/lib/model/order-figures.js +74 -0
  207. package/src/lib/model/order-figures.js.map +1 -0
  208. package/src/lib/model/plugin-permissions.d.ts +23 -0
  209. package/src/lib/model/plugin-permissions.js +34 -0
  210. package/src/lib/model/plugin-permissions.js.map +1 -0
  211. package/src/lib/model/purchase-analytics.d.ts +186 -0
  212. package/src/lib/model/purchase-analytics.js +216 -0
  213. package/src/lib/model/purchase-analytics.js.map +1 -0
  214. package/src/lib/plugin-config.d.ts +62 -0
  215. package/src/lib/plugin-config.js +78 -0
  216. package/src/lib/plugin-config.js.map +1 -0
  217. package/src/lib/plugin.d.ts +28 -0
  218. package/src/lib/plugin.js +147 -0
  219. package/src/lib/plugin.js.map +1 -0
  220. package/src/lib/server/billing-webhook.d.ts +26 -0
  221. package/src/lib/server/billing-webhook.js +4011 -0
  222. package/src/lib/server/billing-webhook.js.map +1 -0
  223. package/src/lib/server/cancel-order.d.ts +87 -0
  224. package/src/lib/server/cancel-order.js +368 -0
  225. package/src/lib/server/cancel-order.js.map +1 -0
  226. package/src/lib/server/cart-checkout.d.ts +25 -0
  227. package/src/lib/server/cart-checkout.js +976 -0
  228. package/src/lib/server/cart-checkout.js.map +1 -0
  229. package/src/lib/server/cart-cookie.d.ts +32 -0
  230. package/src/lib/server/cart-cookie.js +77 -0
  231. package/src/lib/server/cart-cookie.js.map +1 -0
  232. package/src/lib/server/cart.d.ts +34 -0
  233. package/src/lib/server/cart.js +181 -0
  234. package/src/lib/server/cart.js.map +1 -0
  235. package/src/lib/server/catalog.d.ts +127 -0
  236. package/src/lib/server/catalog.js +277 -0
  237. package/src/lib/server/catalog.js.map +1 -0
  238. package/src/lib/server/checkout.d.ts +36 -0
  239. package/src/lib/server/checkout.js +915 -0
  240. package/src/lib/server/checkout.js.map +1 -0
  241. package/src/lib/server/connect.d.ts +26 -0
  242. package/src/lib/server/connect.js +238 -0
  243. package/src/lib/server/connect.js.map +1 -0
  244. package/src/lib/server/contact-refund.d.ts +117 -0
  245. package/src/lib/server/contact-refund.js +196 -0
  246. package/src/lib/server/contact-refund.js.map +1 -0
  247. package/src/lib/server/download.d.ts +39 -0
  248. package/src/lib/server/download.js +206 -0
  249. package/src/lib/server/download.js.map +1 -0
  250. package/src/lib/server/draft-order.d.ts +26 -0
  251. package/src/lib/server/draft-order.js +706 -0
  252. package/src/lib/server/draft-order.js.map +1 -0
  253. package/src/lib/server/feed.d.ts +24 -0
  254. package/src/lib/server/feed.js +59 -0
  255. package/src/lib/server/feed.js.map +1 -0
  256. package/src/lib/server/fulfill-order.d.ts +97 -0
  257. package/src/lib/server/fulfill-order.js +235 -0
  258. package/src/lib/server/fulfill-order.js.map +1 -0
  259. package/src/lib/server/gate.d.ts +36 -0
  260. package/src/lib/server/gate.js +112 -0
  261. package/src/lib/server/gate.js.map +1 -0
  262. package/src/lib/server/gift-cards.d.ts +42 -0
  263. package/src/lib/server/gift-cards.js +219 -0
  264. package/src/lib/server/gift-cards.js.map +1 -0
  265. package/src/lib/server/low-stock.d.ts +49 -0
  266. package/src/lib/server/low-stock.js +60 -0
  267. package/src/lib/server/low-stock.js.map +1 -0
  268. package/src/lib/server/manual-tax-rate.d.ts +82 -0
  269. package/src/lib/server/manual-tax-rate.js +113 -0
  270. package/src/lib/server/manual-tax-rate.js.map +1 -0
  271. package/src/lib/server/member-feed.d.ts +24 -0
  272. package/src/lib/server/member-feed.js +72 -0
  273. package/src/lib/server/member-feed.js.map +1 -0
  274. package/src/lib/server/member-name-search.d.ts +37 -0
  275. package/src/lib/server/member-name-search.js +42 -0
  276. package/src/lib/server/member-name-search.js.map +1 -0
  277. package/src/lib/server/member-post.d.ts +23 -0
  278. package/src/lib/server/member-post.js +166 -0
  279. package/src/lib/server/member-post.js.map +1 -0
  280. package/src/lib/server/membership-account.d.ts +37 -0
  281. package/src/lib/server/membership-account.js +148 -0
  282. package/src/lib/server/membership-account.js.map +1 -0
  283. package/src/lib/server/membership-admin-password.d.ts +33 -0
  284. package/src/lib/server/membership-admin-password.js +202 -0
  285. package/src/lib/server/membership-admin-password.js.map +1 -0
  286. package/src/lib/server/membership-content.d.ts +23 -0
  287. package/src/lib/server/membership-content.js +105 -0
  288. package/src/lib/server/membership-content.js.map +1 -0
  289. package/src/lib/server/membership-login.d.ts +19 -0
  290. package/src/lib/server/membership-login.js +137 -0
  291. package/src/lib/server/membership-login.js.map +1 -0
  292. package/src/lib/server/membership-logout.d.ts +19 -0
  293. package/src/lib/server/membership-logout.js +35 -0
  294. package/src/lib/server/membership-logout.js.map +1 -0
  295. package/src/lib/server/membership-recover.d.ts +60 -0
  296. package/src/lib/server/membership-recover.js +244 -0
  297. package/src/lib/server/membership-recover.js.map +1 -0
  298. package/src/lib/server/membership-register.d.ts +22 -0
  299. package/src/lib/server/membership-register.js +241 -0
  300. package/src/lib/server/membership-register.js.map +1 -0
  301. package/src/lib/server/membership-reset.d.ts +26 -0
  302. package/src/lib/server/membership-reset.js +88 -0
  303. package/src/lib/server/membership-reset.js.map +1 -0
  304. package/src/lib/server/membership-wishlist.d.ts +23 -0
  305. package/src/lib/server/membership-wishlist.js +89 -0
  306. package/src/lib/server/membership-wishlist.js.map +1 -0
  307. package/src/lib/server/membership.d.ts +96 -0
  308. package/src/lib/server/membership.js +222 -0
  309. package/src/lib/server/membership.js.map +1 -0
  310. package/src/lib/server/native-checkout.d.ts +170 -0
  311. package/src/lib/server/native-checkout.js +193 -0
  312. package/src/lib/server/native-checkout.js.map +1 -0
  313. package/src/lib/server/newsletter.d.ts +23 -0
  314. package/src/lib/server/newsletter.js +323 -0
  315. package/src/lib/server/newsletter.js.map +1 -0
  316. package/src/lib/server/notify-restock.d.ts +22 -0
  317. package/src/lib/server/notify-restock.js +84 -0
  318. package/src/lib/server/notify-restock.js.map +1 -0
  319. package/src/lib/server/order-analytics.d.ts +134 -0
  320. package/src/lib/server/order-analytics.js +263 -0
  321. package/src/lib/server/order-analytics.js.map +1 -0
  322. package/src/lib/server/order-figures.d.ts +33 -0
  323. package/src/lib/server/order-figures.js +220 -0
  324. package/src/lib/server/order-figures.js.map +1 -0
  325. package/src/lib/server/paid-media-delivery.d.ts +173 -0
  326. package/src/lib/server/paid-media-delivery.js +212 -0
  327. package/src/lib/server/paid-media-delivery.js.map +1 -0
  328. package/src/lib/server/pos-order.d.ts +54 -0
  329. package/src/lib/server/pos-order.js +1137 -0
  330. package/src/lib/server/pos-order.js.map +1 -0
  331. package/src/lib/server/process-abandoned.d.ts +54 -0
  332. package/src/lib/server/process-abandoned.js +222 -0
  333. package/src/lib/server/process-abandoned.js.map +1 -0
  334. package/src/lib/server/process-restock.d.ts +42 -0
  335. package/src/lib/server/process-restock.js +219 -0
  336. package/src/lib/server/process-restock.js.map +1 -0
  337. package/src/lib/server/product-card.d.ts +29 -0
  338. package/src/lib/server/product-card.js +54 -0
  339. package/src/lib/server/product-card.js.map +1 -0
  340. package/src/lib/server/product.d.ts +70 -0
  341. package/src/lib/server/product.js +105 -0
  342. package/src/lib/server/product.js.map +1 -0
  343. package/src/lib/server/promotion-hold.d.ts +144 -0
  344. package/src/lib/server/promotion-hold.js +174 -0
  345. package/src/lib/server/promotion-hold.js.map +1 -0
  346. package/src/lib/server/reconcile-stock.d.ts +106 -0
  347. package/src/lib/server/reconcile-stock.js +349 -0
  348. package/src/lib/server/reconcile-stock.js.map +1 -0
  349. package/src/lib/server/refund.d.ts +45 -0
  350. package/src/lib/server/refund.js +607 -0
  351. package/src/lib/server/refund.js.map +1 -0
  352. package/src/lib/server/related.d.ts +24 -0
  353. package/src/lib/server/related.js +104 -0
  354. package/src/lib/server/related.js.map +1 -0
  355. package/src/lib/server/reservation-availability.d.ts +23 -0
  356. package/src/lib/server/reservation-availability.js +111 -0
  357. package/src/lib/server/reservation-availability.js.map +1 -0
  358. package/src/lib/server/reserve-stock.d.ts +156 -0
  359. package/src/lib/server/reserve-stock.js +146 -0
  360. package/src/lib/server/reserve-stock.js.map +1 -0
  361. package/src/lib/server/reserve.d.ts +25 -0
  362. package/src/lib/server/reserve.js +426 -0
  363. package/src/lib/server/reserve.js.map +1 -0
  364. package/src/lib/server/restock-flag.d.ts +202 -0
  365. package/src/lib/server/restock-flag.js +449 -0
  366. package/src/lib/server/restock-flag.js.map +1 -0
  367. package/src/lib/server/reviews.d.ts +51 -0
  368. package/src/lib/server/reviews.js +132 -0
  369. package/src/lib/server/reviews.js.map +1 -0
  370. package/src/lib/server/site-page-enricher.d.ts +42 -0
  371. package/src/lib/server/site-page-enricher.js +149 -0
  372. package/src/lib/server/site-page-enricher.js.map +1 -0
  373. package/src/lib/server/site-page-resolver.d.ts +26 -0
  374. package/src/lib/server/site-page-resolver.js +223 -0
  375. package/src/lib/server/site-page-resolver.js.map +1 -0
  376. package/src/lib/server/stock-hold.d.ts +127 -0
  377. package/src/lib/server/stock-hold.js +258 -0
  378. package/src/lib/server/stock-hold.js.map +1 -0
  379. package/src/lib/server/storefront-tax-record.d.ts +22 -0
  380. package/src/lib/server/storefront-tax-record.js +218 -0
  381. package/src/lib/server/storefront-tax-record.js.map +1 -0
  382. package/src/lib/server/storefront-tax.d.ts +197 -0
  383. package/src/lib/server/storefront-tax.js +245 -0
  384. package/src/lib/server/storefront-tax.js.map +1 -0
  385. package/src/lib/server/stream.d.ts +25 -0
  386. package/src/lib/server/stream.js +191 -0
  387. package/src/lib/server/stream.js.map +1 -0
  388. package/src/lib/server/subscription-portal.d.ts +24 -0
  389. package/src/lib/server/subscription-portal.js +91 -0
  390. package/src/lib/server/subscription-portal.js.map +1 -0
  391. package/src/lib/server/supplier-outbox.d.ts +190 -0
  392. package/src/lib/server/supplier-outbox.js +342 -0
  393. package/src/lib/server/supplier-outbox.js.map +1 -0
  394. package/src/lib/server/supplier-update.d.ts +23 -0
  395. package/src/lib/server/supplier-update.js +289 -0
  396. package/src/lib/server/supplier-update.js.map +1 -0
  397. package/src/lib/server/tax-profile.d.ts +29 -0
  398. package/src/lib/server/tax-profile.js +40 -0
  399. package/src/lib/server/tax-profile.js.map +1 -0
  400. package/src/lib/server.d.ts +32 -0
  401. package/src/lib/server.js +311 -0
  402. package/src/lib/server.js.map +1 -0
  403. package/src/lib/site.d.ts +27 -0
  404. package/src/lib/site.js +134 -0
  405. package/src/lib/site.js.map +1 -0
  406. package/src/lib/utils/escape-html.d.ts +41 -0
  407. package/src/lib/utils/escape-html.js +41 -0
  408. package/src/lib/utils/escape-html.js.map +1 -0
  409. package/src/lib/utils/generate-preset-id.d.ts +19 -0
  410. package/src/lib/utils/generate-preset-id.js +25 -0
  411. package/src/lib/utils/generate-preset-id.js.map +1 -0
  412. package/src/lib/utils/member-continue.d.ts +45 -0
  413. package/src/lib/utils/member-continue.js +49 -0
  414. package/src/lib/utils/member-continue.js.map +1 -0
  415. package/src/lib/utils/use-storefront-purchase-event.d.ts +60 -0
  416. package/src/lib/utils/use-storefront-purchase-event.js +142 -0
  417. package/src/lib/utils/use-storefront-purchase-event.js.map +1 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { PluginApiHandler } from '@aglyn/aglyn/server';
18
+ /**
19
+ * Is the HOST's org on a plan that includes the content paywall (AGL-481)?
20
+ * `contentGating` is a Business+ feature, but the member paywall used to be
21
+ * enforced on the visitor's purchase alone — so any plan (or a lapsed
22
+ * Business org) could run members-only content. This gates the feature at
23
+ * the org level; a plan-less/lapsed org resolves as `free` and fails
24
+ * closed (gated content is refused, never served open). Re-resolved per
25
+ * request, so a downgrade takes effect within the revalidate window.
26
+ */
27
+ export declare function hostHasContentGating(hostId: string): Promise<boolean>;
28
+ /**
29
+ * Entitlement check (AGL-309): is the signed-in member entitled to
30
+ * gated content? Entitled = a paid (non-refunded) order containing the
31
+ * product, or a live subscription to it ('any' matches any live
32
+ * subscription). Server-enforced — the gated media endpoints (video
33
+ * AGL-315, downloads AGL-302) call the same logic.
34
+ */
35
+ export declare function checkMemberEntitlement(hostId: string, memberEmail: string, productId: string | 'any'): Promise<boolean>;
36
+ export declare const gateHandler: PluginApiHandler;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import * as Aglyn from "@aglyn/aglyn/server";
17
+ import * as CommerceModel from "../model/index.js";
18
+ import { firebaseAdmin, getOrgForHost } from "@aglyn/tenant-data-admin";
19
+ import { readActiveMemberSession, setMemberCookie } from "./membership.js";
20
+ // Subscription statuses that grant access (trialing pays later). The list
21
+ // moved to the model as `isTenantSubscriptionLive` (AGL-1849), where the
22
+ // checkout duplicate guard reads the same one rather than transcribing it a
23
+ // third time; the reasoning for keeping it apart from the org-side
24
+ // `isLiveSubscriptionStatus` travels with it.
25
+ /**
26
+ * Is the HOST's org on a plan that includes the content paywall (AGL-481)?
27
+ * `contentGating` is a Business+ feature, but the member paywall used to be
28
+ * enforced on the visitor's purchase alone — so any plan (or a lapsed
29
+ * Business org) could run members-only content. This gates the feature at
30
+ * the org level; a plan-less/lapsed org resolves as `free` and fails
31
+ * closed (gated content is refused, never served open). Re-resolved per
32
+ * request, so a downgrade takes effect within the revalidate window.
33
+ */ export async function hostHasContentGating(hostId) {
34
+ var _this;
35
+ const org = (_this = await getOrgForHost(hostId)) == null ? void 0 : _this.org;
36
+ return Aglyn.checkEntitlement(org, 'contentGating');
37
+ }
38
+ /**
39
+ * Entitlement check (AGL-309): is the signed-in member entitled to
40
+ * gated content? Entitled = a paid (non-refunded) order containing the
41
+ * product, or a live subscription to it ('any' matches any live
42
+ * subscription). Server-enforced — the gated media endpoints (video
43
+ * AGL-315, downloads AGL-302) call the same logic.
44
+ */ export async function checkMemberEntitlement(hostId, memberEmail, productId) {
45
+ // Org-level gate (AGL-481): the paywall feature itself is Business+. If
46
+ // the host's plan doesn't include it, no visitor is "entitled" — fail
47
+ // closed rather than serve gated content on a plan that didn't pay for
48
+ // gating.
49
+ if (!await hostHasContentGating(hostId)) return false;
50
+ const firestore = firebaseAdmin.app().firestore();
51
+ const hostRef = firestore.collection('hosts').doc(hostId);
52
+ const subscriptions = await hostRef.collection('subscriptions').where('customerEmail', '==', memberEmail).limit(10).get();
53
+ for (const docSnapshot of subscriptions.docs){
54
+ if (!CommerceModel.isTenantSubscriptionLive(docSnapshot.get('status'))) continue;
55
+ if (productId === 'any' || docSnapshot.get('productId') === productId) {
56
+ return true;
57
+ }
58
+ }
59
+ if (productId === 'any') return false;
60
+ const orders = await hostRef.collection('orders').where('customerEmail', '==', memberEmail).limit(50).get();
61
+ for (const docSnapshot of orders.docs){
62
+ const order = CommerceModel.liftLegacyOrder(docSnapshot.data());
63
+ // The shared entitlement test (AGL-2454), which is where the partial-refund
64
+ // case lives. The inline status literal this replaced only ever moved on a
65
+ // FULL refund, so a buyer refunded for the gated product kept their access.
66
+ if (CommerceModel.orderEntitlesProduct(order, productId)) return true;
67
+ }
68
+ return false;
69
+ }
70
+ export const gateHandler = async (req, res)=>{
71
+ var _req_query_hostId, _req_query_productId;
72
+ const hostId = String((_req_query_hostId = req.query.hostId) != null ? _req_query_hostId : '');
73
+ const productId = String((_req_query_productId = req.query.productId) != null ? _req_query_productId : 'any');
74
+ if (!hostId) return res.status(400).json({
75
+ error: 'Missing hostId'
76
+ });
77
+ try {
78
+ var _session_member_get;
79
+ // This probe answers 200 either way, so the suspension gate
80
+ // (AGL-546/550) maps a suspended member to the signed-out shape —
81
+ // never entitled — and drops the stale cookie so the signed-in
82
+ // shell unwinds on the next paint.
83
+ const session = await readActiveMemberSession(req, hostId);
84
+ if (session.status !== 'active') {
85
+ if (session.status === 'suspended') setMemberCookie(res, hostId, null);
86
+ return res.status(200).json({
87
+ entitled: false,
88
+ signedIn: false
89
+ });
90
+ }
91
+ const email = String((_session_member_get = session.member.get('email')) != null ? _session_member_get : '');
92
+ if (!email) {
93
+ return res.status(200).json({
94
+ entitled: false,
95
+ signedIn: false
96
+ });
97
+ }
98
+ const entitled = await checkMemberEntitlement(hostId, email, productId);
99
+ res.setHeader('Cache-Control', 'private, no-store');
100
+ return res.status(200).json({
101
+ entitled,
102
+ signedIn: true
103
+ });
104
+ } catch (error) {
105
+ console.error(error);
106
+ return res.status(500).json({
107
+ error: 'Check failed'
108
+ });
109
+ }
110
+ };
111
+
112
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/gate.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginApiHandler } from '@aglyn/aglyn/server'\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport * as CommerceModel from '../model'\nimport { firebaseAdmin, getOrgForHost } from '@aglyn/tenant-data-admin'\nimport { readActiveMemberSession, setMemberCookie } from './membership'\n\n// Subscription statuses that grant access (trialing pays later). The list\n// moved to the model as `isTenantSubscriptionLive` (AGL-1849), where the\n// checkout duplicate guard reads the same one rather than transcribing it a\n// third time; the reasoning for keeping it apart from the org-side\n// `isLiveSubscriptionStatus` travels with it.\n\n/**\n * Is the HOST's org on a plan that includes the content paywall (AGL-481)?\n * `contentGating` is a Business+ feature, but the member paywall used to be\n * enforced on the visitor's purchase alone — so any plan (or a lapsed\n * Business org) could run members-only content. This gates the feature at\n * the org level; a plan-less/lapsed org resolves as `free` and fails\n * closed (gated content is refused, never served open). Re-resolved per\n * request, so a downgrade takes effect within the revalidate window.\n */\nexport async function hostHasContentGating(hostId: string): Promise<boolean> {\n const org = (await getOrgForHost(hostId))?.org\n return Aglyn.checkEntitlement(org as any, 'contentGating')\n}\n\n/**\n * Entitlement check (AGL-309): is the signed-in member entitled to\n * gated content? Entitled = a paid (non-refunded) order containing the\n * product, or a live subscription to it ('any' matches any live\n * subscription). Server-enforced — the gated media endpoints (video\n * AGL-315, downloads AGL-302) call the same logic.\n */\nexport async function checkMemberEntitlement(\n hostId: string,\n memberEmail: string,\n productId: string | 'any',\n): Promise<boolean> {\n // Org-level gate (AGL-481): the paywall feature itself is Business+. If\n // the host's plan doesn't include it, no visitor is \"entitled\" — fail\n // closed rather than serve gated content on a plan that didn't pay for\n // gating.\n if (!(await hostHasContentGating(hostId))) return false\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const subscriptions = await hostRef\n .collection('subscriptions')\n .where('customerEmail', '==', memberEmail)\n .limit(10)\n .get()\n for (const docSnapshot of subscriptions.docs) {\n if (!CommerceModel.isTenantSubscriptionLive(docSnapshot.get('status')))\n continue\n if (productId === 'any' || docSnapshot.get('productId') === productId) {\n return true\n }\n }\n if (productId === 'any') return false\n const orders = await hostRef\n .collection('orders')\n .where('customerEmail', '==', memberEmail)\n .limit(50)\n .get()\n for (const docSnapshot of orders.docs) {\n const order = CommerceModel.liftLegacyOrder(docSnapshot.data() as any)\n // The shared entitlement test (AGL-2454), which is where the partial-refund\n // case lives. The inline status literal this replaced only ever moved on a\n // FULL refund, so a buyer refunded for the gated product kept their access.\n if (CommerceModel.orderEntitlesProduct(order, productId)) return true\n }\n return false\n}\n\nexport const gateHandler: PluginApiHandler = async (req, res) => {\n const hostId = String(req.query.hostId ?? '')\n const productId = String(req.query.productId ?? 'any')\n if (!hostId) return res.status(400).json({ error: 'Missing hostId' })\n try {\n // This probe answers 200 either way, so the suspension gate\n // (AGL-546/550) maps a suspended member to the signed-out shape —\n // never entitled — and drops the stale cookie so the signed-in\n // shell unwinds on the next paint.\n const session = await readActiveMemberSession(req, hostId)\n if (session.status !== 'active') {\n if (session.status === 'suspended') setMemberCookie(res, hostId, null)\n return res.status(200).json({ entitled: false, signedIn: false })\n }\n const email = String(session.member.get('email') ?? '')\n if (!email) {\n return res.status(200).json({ entitled: false, signedIn: false })\n }\n const entitled = await checkMemberEntitlement(hostId, email, productId)\n res.setHeader('Cache-Control', 'private, no-store')\n return res.status(200).json({ entitled, signedIn: true })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Check failed' })\n }\n}\n"],"names":["Aglyn","CommerceModel","firebaseAdmin","getOrgForHost","readActiveMemberSession","setMemberCookie","hostHasContentGating","hostId","org","checkEntitlement","checkMemberEntitlement","memberEmail","productId","firestore","app","hostRef","collection","doc","subscriptions","where","limit","get","docSnapshot","docs","isTenantSubscriptionLive","orders","order","liftLegacyOrder","data","orderEntitlesProduct","gateHandler","req","res","String","query","status","json","error","session","entitled","signedIn","email","member","setHeader","console"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type PluginApiHandler } from '@aglyn/aglyn/server';
18
+ /**
19
+ * Gift-card issue / void (AGL-2226).
20
+ *
21
+ * The `giftCards` subcollection has existed since AGL-322 — minted by
22
+ * `billing-webhook.ts` when a gift-card product is bought, decremented by
23
+ * `cart-checkout.ts` at redemption. Neither end had a console: grepping for
24
+ * that collection returned exactly those two server files, so a
25
+ * merchant on Business (the tier that carries `giftCards`) could sell store
26
+ * credit and then not see one cent of the liability they had taken on, help
27
+ * a customer who lost a code, or void a leaked one.
28
+ *
29
+ * WRITES ARE SERVER-SIDE, and that is not a style preference. The Firestore
30
+ * rules' host catch-all would happily grant an admin `create`/`update` on
31
+ * `giftCards` — it is not in any exclusion list. A client-writable
32
+ * `balanceCents` is a self-service discount machine: set it to any number,
33
+ * redeem it at checkout, and `cart-checkout.ts` applies it as amount-off
34
+ * against a Stripe coupon. The reads stay client-side (the catch-all's
35
+ * `allow read` already grants them, and the card wants live updates), so
36
+ * only the two operations that move money come through here.
37
+ *
38
+ * Re-checks the entitlement server-side for the same reason
39
+ * `billing-webhook.ts:2285` does: a card issued while the plan was live is
40
+ * honoured, but a lapsed plan must not mint new ones.
41
+ */
42
+ export declare const giftCardsHandler: PluginApiHandler;
@@ -0,0 +1,219 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import * as Aglyn from "@aglyn/aglyn/server";
18
+ import { randomBytes } from "crypto";
19
+ import { firebaseAdmin, getOrgForHost, hostSendingIdentity, meterHostEmail, renderHostEmailWithTokens } from "@aglyn/tenant-data-admin";
20
+ import { isEmailConfigured, sendEmail } from "@aglyn/shared-util-email";
21
+ /** A merchant may not mint more than this in one card. */ const MAX_ISSUE_CENTS = 100000;
22
+ /** `GC-` + 12 uppercase hex, the shape `billing-webhook.ts` already mints. */ function mintCode() {
23
+ return `GC-${randomBytes(6).toString('hex').toUpperCase()}`;
24
+ }
25
+ /**
26
+ * Gift-card issue / void (AGL-2226).
27
+ *
28
+ * The `giftCards` subcollection has existed since AGL-322 — minted by
29
+ * `billing-webhook.ts` when a gift-card product is bought, decremented by
30
+ * `cart-checkout.ts` at redemption. Neither end had a console: grepping for
31
+ * that collection returned exactly those two server files, so a
32
+ * merchant on Business (the tier that carries `giftCards`) could sell store
33
+ * credit and then not see one cent of the liability they had taken on, help
34
+ * a customer who lost a code, or void a leaked one.
35
+ *
36
+ * WRITES ARE SERVER-SIDE, and that is not a style preference. The Firestore
37
+ * rules' host catch-all would happily grant an admin `create`/`update` on
38
+ * `giftCards` — it is not in any exclusion list. A client-writable
39
+ * `balanceCents` is a self-service discount machine: set it to any number,
40
+ * redeem it at checkout, and `cart-checkout.ts` applies it as amount-off
41
+ * against a Stripe coupon. The reads stay client-side (the catch-all's
42
+ * `allow read` already grants them, and the card wants live updates), so
43
+ * only the two operations that move money come through here.
44
+ *
45
+ * Re-checks the entitlement server-side for the same reason
46
+ * `billing-webhook.ts:2285` does: a card issued while the plan was live is
47
+ * honoured, but a lapsed plan must not mint new ones.
48
+ */ export const giftCardsHandler = async (req, res)=>{
49
+ var _req_headers_authorization, _req_body, _body_hostId, _body_action;
50
+ if (req.method !== 'POST') {
51
+ return res.status(405).json({
52
+ error: 'Method not allowed'
53
+ });
54
+ }
55
+ const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
56
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
57
+ if (!idToken) return res.status(401).json({
58
+ error: 'Unauthenticated'
59
+ });
60
+ const body = typeof req.body === 'string' ? JSON.parse(req.body || '{}') : (_req_body = req.body) != null ? _req_body : {};
61
+ const hostId = String((_body_hostId = body.hostId) != null ? _body_hostId : '');
62
+ const action = String((_body_action = body.action) != null ? _body_action : '');
63
+ if (!hostId) return res.status(400).json({
64
+ error: 'Missing hostId'
65
+ });
66
+ if (action !== 'issue' && action !== 'void') {
67
+ return res.status(400).json({
68
+ error: 'Unknown action'
69
+ });
70
+ }
71
+ try {
72
+ var _hostSnapshot_get, _body_amountCents, _body_recipientEmail, _body_note;
73
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
74
+ const firestore = firebaseAdmin.app().firestore();
75
+ const hostRef = firestore.collection('hosts').doc(hostId);
76
+ const hostSnapshot = await hostRef.get();
77
+ if (!hostSnapshot.exists) {
78
+ return res.status(404).json({
79
+ error: 'Unknown site'
80
+ });
81
+ }
82
+ // AN ALLOWLIST (AGL-2372), not the `!role || role === 'viewer'` denylist
83
+ // this route used to carry — the same correction AGL-2262 made to
84
+ // `pos-order.ts` and `draft-order.ts` and did not reach here.
85
+ //
86
+ // A denylist silently WIDENS every time the role union grows. It did:
87
+ // `HostAccessRole` is `admin | editor | author | viewer` since AGL-2334,
88
+ // and `author` is grantable on any host through `/api/hosts/members`. So
89
+ // "not literally viewer" started admitting authors — a role that exists to
90
+ // edit content — to a route that mints and destroys store credit.
91
+ //
92
+ // `admin | editor` is the bar this route always meant (the comment here
93
+ // said "owner/admin/editor, not viewer"); the difference is that a role
94
+ // added tomorrow is now refused until someone decides otherwise, rather
95
+ // than admitted by silence. Absent stays refused: `undefined` is not in
96
+ // the set, which matters with `strictNullChecks` off.
97
+ const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
98
+ if (memberRole !== 'admin' && memberRole !== 'editor') {
99
+ return res.status(403).json({
100
+ error: 'Not permitted'
101
+ });
102
+ }
103
+ const owner = await getOrgForHost(hostId).catch(()=>null);
104
+ if (!Aglyn.checkEntitlement(owner == null ? void 0 : owner.org, 'giftCards')) {
105
+ return res.status(402).json({
106
+ error: 'Gift cards are not included on this plan'
107
+ });
108
+ }
109
+ if (action === 'void') {
110
+ var _body_code;
111
+ const code = String((_body_code = body.code) != null ? _body_code : '').trim();
112
+ if (!code) return res.status(400).json({
113
+ error: 'Missing code'
114
+ });
115
+ const cardRef = hostRef.collection('giftCards').doc(code);
116
+ // Zero the balance rather than delete the document. AGL-1767 is the
117
+ // reason: a deleted card that a checkout still had in flight was
118
+ // RESURRECTED by the webhook's `increment(-cents)` into a NEGATIVE
119
+ // balance, which then read as a card with money on it and understated
120
+ // the outstanding total. A zeroed card decrements to a negative number
121
+ // that no redemption path will ever accept, and the row stays
122
+ // auditable.
123
+ const existing = await cardRef.get();
124
+ if (!existing.exists) {
125
+ return res.status(404).json({
126
+ error: 'Unknown gift card'
127
+ });
128
+ }
129
+ await cardRef.set({
130
+ balanceCents: 0,
131
+ voidedAtMs: Date.now(),
132
+ voidedBy: decoded.uid
133
+ }, {
134
+ merge: true
135
+ });
136
+ return res.status(200).json({
137
+ ok: true,
138
+ code
139
+ });
140
+ }
141
+ const amountCents = Math.round(Number((_body_amountCents = body.amountCents) != null ? _body_amountCents : 0));
142
+ if (!Number.isFinite(amountCents) || amountCents <= 0) {
143
+ return res.status(400).json({
144
+ error: 'Amount must be above zero'
145
+ });
146
+ }
147
+ if (amountCents > MAX_ISSUE_CENTS) {
148
+ return res.status(400).json({
149
+ error: `Amount may not exceed $${MAX_ISSUE_CENTS / 100}`
150
+ });
151
+ }
152
+ const recipientEmail = String((_body_recipientEmail = body.recipientEmail) != null ? _body_recipientEmail : '').trim().slice(0, 200);
153
+ const note = String((_body_note = body.note) != null ? _body_note : '').trim().slice(0, 200);
154
+ const code = mintCode();
155
+ await hostRef.collection('giftCards').doc(code).set(_extends({
156
+ initialCents: amountCents,
157
+ balanceCents: amountCents,
158
+ recipientEmail: recipientEmail || null,
159
+ // Null rather than absent: the field is what tells a reader whether a
160
+ // card came from a sale or from the console, and `undefined` would
161
+ // make an issued-by-hand card indistinguishable from a legacy row.
162
+ orderId: null,
163
+ issuedBy: decoded.uid
164
+ }, note ? {
165
+ note
166
+ } : {}, {
167
+ createdAtMs: Date.now()
168
+ }));
169
+ let emailed = false;
170
+ if (recipientEmail && isEmailConfigured()) {
171
+ var _ref;
172
+ const value = `$${(amountCents / 100).toFixed(2)}`;
173
+ // The same site-designed template the purchase path uses (AGL-771), so
174
+ // a hand-issued card arrives looking like a bought one.
175
+ const designed = await renderHostEmailWithTokens(firestore, hostId, 'gift-card', {
176
+ 'giftcard.code': code,
177
+ 'giftcard.value': value
178
+ });
179
+ const sent = await sendEmail(_extends({
180
+ to: recipientEmail,
181
+ subject: (_ref = designed == null ? void 0 : designed.subject) != null ? _ref : 'Your gift card',
182
+ text: (designed == null ? void 0 : designed.text) || `Gift card code: ${code}\nValue: ${value}\n\n` + 'Enter it at checkout to apply the balance.'
183
+ }, (designed == null ? void 0 : designed.html) ? {
184
+ html: designed.html
185
+ } : {}, {
186
+ fromName: Aglyn.resolveBrandingProfile(owner == null ? void 0 : owner.org).fromName,
187
+ sendingIdentity: await hostSendingIdentity(hostId),
188
+ audience: 'tenant',
189
+ context: 'gift card'
190
+ })).then(()=>true).catch(()=>false);
191
+ if (sent) {
192
+ // Cost meter (AGL-1438). Transactional: the card has already been
193
+ // minted and the recipient is owed the code, so a quota may count
194
+ // this send but never refuse it. Metered on the SUCCESS path only,
195
+ // matching every other sender — a send the provider rejected costs
196
+ // nothing, and a meter that counted it would inflate the COGS figure
197
+ // exactly when mail was broken.
198
+ await meterHostEmail(hostId);
199
+ }
200
+ // The truth, not the intent: the console shows this back to whoever
201
+ // issued the card, and reporting a delivered email for a send that
202
+ // threw is how a customer ends up waiting for a code that is not
203
+ // coming.
204
+ emailed = sent;
205
+ }
206
+ return res.status(200).json({
207
+ ok: true,
208
+ code,
209
+ emailed
210
+ });
211
+ } catch (error) {
212
+ console.error(error);
213
+ return res.status(500).json({
214
+ error: 'Gift card operation failed'
215
+ });
216
+ }
217
+ };
218
+
219
+ //# sourceMappingURL=gift-cards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/gift-cards.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport { randomBytes } from 'crypto'\nimport {\n firebaseAdmin,\n getOrgForHost,\n hostSendingIdentity,\n meterHostEmail,\n renderHostEmailWithTokens,\n} from '@aglyn/tenant-data-admin'\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport { type PluginApiHandler } from '@aglyn/aglyn/server'\n\n/** A merchant may not mint more than this in one card. */\nconst MAX_ISSUE_CENTS = 100_000\n\n/** `GC-` + 12 uppercase hex, the shape `billing-webhook.ts` already mints. */\nfunction mintCode(): string {\n return `GC-${randomBytes(6).toString('hex').toUpperCase()}`\n}\n\n/**\n * Gift-card issue / void (AGL-2226).\n *\n * The `giftCards` subcollection has existed since AGL-322 — minted by\n * `billing-webhook.ts` when a gift-card product is bought, decremented by\n * `cart-checkout.ts` at redemption. Neither end had a console: grepping for\n * that collection returned exactly those two server files, so a\n * merchant on Business (the tier that carries `giftCards`) could sell store\n * credit and then not see one cent of the liability they had taken on, help\n * a customer who lost a code, or void a leaked one.\n *\n * WRITES ARE SERVER-SIDE, and that is not a style preference. The Firestore\n * rules' host catch-all would happily grant an admin `create`/`update` on\n * `giftCards` — it is not in any exclusion list. A client-writable\n * `balanceCents` is a self-service discount machine: set it to any number,\n * redeem it at checkout, and `cart-checkout.ts` applies it as amount-off\n * against a Stripe coupon. The reads stay client-side (the catch-all's\n * `allow read` already grants them, and the card wants live updates), so\n * only the two operations that move money come through here.\n *\n * Re-checks the entitlement server-side for the same reason\n * `billing-webhook.ts:2285` does: a card issued while the plan was live is\n * honoured, but a lapsed plan must not mint new ones.\n */\nexport const giftCardsHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n\n const body =\n typeof req.body === 'string' ? JSON.parse(req.body || '{}') : (req.body ?? {})\n const hostId = String(body.hostId ?? '')\n const action = String(body.action ?? '')\n if (!hostId) return res.status(400).json({ error: 'Missing hostId' })\n if (action !== 'issue' && action !== 'void') {\n return res.status(400).json({ error: 'Unknown action' })\n }\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n // AN ALLOWLIST (AGL-2372), not the `!role || role === 'viewer'` denylist\n // this route used to carry — the same correction AGL-2262 made to\n // `pos-order.ts` and `draft-order.ts` and did not reach here.\n //\n // A denylist silently WIDENS every time the role union grows. It did:\n // `HostAccessRole` is `admin | editor | author | viewer` since AGL-2334,\n // and `author` is grantable on any host through `/api/hosts/members`. So\n // \"not literally viewer\" started admitting authors — a role that exists to\n // edit content — to a route that mints and destroys store credit.\n //\n // `admin | editor` is the bar this route always meant (the comment here\n // said \"owner/admin/editor, not viewer\"); the difference is that a role\n // added tomorrow is now refused until someone decides otherwise, rather\n // than admitted by silence. Absent stays refused: `undefined` is not in\n // the set, which matters with `strictNullChecks` off.\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[decoded.uid]\n if (memberRole !== 'admin' && memberRole !== 'editor') {\n return res.status(403).json({ error: 'Not permitted' })\n }\n const owner = await getOrgForHost(hostId).catch(() => null)\n if (!Aglyn.checkEntitlement(owner?.org as never, 'giftCards')) {\n return res\n .status(402)\n .json({ error: 'Gift cards are not included on this plan' })\n }\n\n if (action === 'void') {\n const code = String(body.code ?? '').trim()\n if (!code) return res.status(400).json({ error: 'Missing code' })\n const cardRef = hostRef.collection('giftCards').doc(code)\n // Zero the balance rather than delete the document. AGL-1767 is the\n // reason: a deleted card that a checkout still had in flight was\n // RESURRECTED by the webhook's `increment(-cents)` into a NEGATIVE\n // balance, which then read as a card with money on it and understated\n // the outstanding total. A zeroed card decrements to a negative number\n // that no redemption path will ever accept, and the row stays\n // auditable.\n const existing = await cardRef.get()\n if (!existing.exists) {\n return res.status(404).json({ error: 'Unknown gift card' })\n }\n await cardRef.set(\n {\n balanceCents: 0,\n voidedAtMs: Date.now(),\n voidedBy: decoded.uid,\n },\n { merge: true },\n )\n return res.status(200).json({ ok: true, code })\n }\n\n const amountCents = Math.round(Number(body.amountCents ?? 0))\n if (!Number.isFinite(amountCents) || amountCents <= 0) {\n return res.status(400).json({ error: 'Amount must be above zero' })\n }\n if (amountCents > MAX_ISSUE_CENTS) {\n return res\n .status(400)\n .json({ error: `Amount may not exceed $${MAX_ISSUE_CENTS / 100}` })\n }\n const recipientEmail = String(body.recipientEmail ?? '').trim().slice(0, 200)\n const note = String(body.note ?? '').trim().slice(0, 200)\n const code = mintCode()\n await hostRef.collection('giftCards').doc(code).set({\n initialCents: amountCents,\n balanceCents: amountCents,\n recipientEmail: recipientEmail || null,\n // Null rather than absent: the field is what tells a reader whether a\n // card came from a sale or from the console, and `undefined` would\n // make an issued-by-hand card indistinguishable from a legacy row.\n orderId: null,\n issuedBy: decoded.uid,\n ...(note ? { note } : {}),\n createdAtMs: Date.now(),\n })\n\n let emailed = false\n if (recipientEmail && isEmailConfigured()) {\n const value = `$${(amountCents / 100).toFixed(2)}`\n // The same site-designed template the purchase path uses (AGL-771), so\n // a hand-issued card arrives looking like a bought one.\n const designed = await renderHostEmailWithTokens(\n firestore,\n hostId,\n 'gift-card',\n { 'giftcard.code': code, 'giftcard.value': value },\n )\n const sent = await sendEmail({\n to: recipientEmail,\n subject: designed?.subject ?? 'Your gift card',\n text:\n designed?.text ||\n `Gift card code: ${code}\\nValue: ${value}\\n\\n` +\n 'Enter it at checkout to apply the balance.',\n ...(designed?.html ? { html: designed.html } : {}),\n fromName: Aglyn.resolveBrandingProfile(owner?.org as never).fromName,\n sendingIdentity: await hostSendingIdentity(hostId),\n audience: 'tenant',\n context: 'gift card',\n })\n .then(() => true)\n .catch(() => false)\n if (sent) {\n // Cost meter (AGL-1438). Transactional: the card has already been\n // minted and the recipient is owed the code, so a quota may count\n // this send but never refuse it. Metered on the SUCCESS path only,\n // matching every other sender — a send the provider rejected costs\n // nothing, and a meter that counted it would inflate the COGS figure\n // exactly when mail was broken.\n await meterHostEmail(hostId)\n }\n // The truth, not the intent: the console shows this back to whoever\n // issued the card, and reporting a delivered email for a send that\n // threw is how a customer ends up waiting for a code that is not\n // coming.\n emailed = sent\n }\n\n return res.status(200).json({ ok: true, code, emailed })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Gift card operation failed' })\n }\n}\n"],"names":["Aglyn","randomBytes","firebaseAdmin","getOrgForHost","hostSendingIdentity","meterHostEmail","renderHostEmailWithTokens","isEmailConfigured","sendEmail","MAX_ISSUE_CENTS","mintCode","toString","toUpperCase","giftCardsHandler","req","res","body","method","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","undefined","JSON","parse","hostId","action","hostSnapshot","decoded","app","auth","verifyIdToken","firestore","hostRef","collection","doc","get","exists","memberRole","uid","owner","catch","checkEntitlement","org","code","trim","cardRef","existing","set","balanceCents","voidedAtMs","Date","now","voidedBy","merge","ok","amountCents","Math","round","Number","isFinite","recipientEmail","note","initialCents","orderId","issuedBy","createdAtMs","emailed","value","toFixed","designed","sent","to","subject","text","html","fromName","resolveBrandingProfile","sendingIdentity","audience","context","then","console"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import * as CommerceModel from '../model';
18
+ /**
19
+ * The AGL-281 low-stock crossing alert, extracted so every stock decrement
20
+ * fires it (AGL-1826). It used to live inline on the buy-now branch only, so
21
+ * whether a merchant was told they were running out depended on which door the
22
+ * sale came through: the single-product storefront button alerted, while the
23
+ * cart, a merchant-sent draft link and the register crossed the same threshold
24
+ * silently.
25
+ *
26
+ * SEMANTICS ARE THE BUY-NOW BRANCH'S, UNCHANGED:
27
+ *
28
+ * - The threshold is the product's own `lowStockThreshold`, compared against
29
+ * the flat total across TRACKED variants (`isLowStock` /
30
+ * `productInventory`). No threshold configured means no alert, ever.
31
+ * - It fires on the CROSSING adjustment only — low after, not low before — so
32
+ * managers get one nudge per threshold breach, not one per order after it.
33
+ * - Redelivery safety is the CALLER's replay guard, not re-checked here:
34
+ * every call site sits behind its branch's own gate (the cart and buy-now
35
+ * `created` transactions, the draft branch's pending-to-paid flip, the
36
+ * POS handler's `claimAttempt`), so a redelivered event never recomputes the
37
+ * pair this compares.
38
+ *
39
+ * Takes the PRE- and POST-decrement product states the caller just computed —
40
+ * never a re-read, which could see its own write (no crossing observed) or a
41
+ * concurrent one (somebody else's crossing attributed here). Callers looping
42
+ * over lines must hand in the compounded pair (AGL-1828/AGL-1825): each line's
43
+ * `lifted` is the previous line's `updated`, so two lines of one product cross
44
+ * exactly once, on the line that actually breaches.
45
+ *
46
+ * Fire-and-forget like the sibling notifications on every branch —
47
+ * `notifyHostManagers` never throws, and an alert must never fail a webhook.
48
+ */
49
+ export declare function alertLowStockCrossing(hostId: string, lifted: Pick<CommerceModel.HostProduct, 'name' | 'variants' | 'lowStockThreshold'>, updated: Pick<CommerceModel.HostProduct, 'name' | 'variants' | 'lowStockThreshold'>): void;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { notifyHostManagers } from "@aglyn/tenant-data-admin";
17
+ import * as CommerceModel from "../model/index.js";
18
+ /**
19
+ * The AGL-281 low-stock crossing alert, extracted so every stock decrement
20
+ * fires it (AGL-1826). It used to live inline on the buy-now branch only, so
21
+ * whether a merchant was told they were running out depended on which door the
22
+ * sale came through: the single-product storefront button alerted, while the
23
+ * cart, a merchant-sent draft link and the register crossed the same threshold
24
+ * silently.
25
+ *
26
+ * SEMANTICS ARE THE BUY-NOW BRANCH'S, UNCHANGED:
27
+ *
28
+ * - The threshold is the product's own `lowStockThreshold`, compared against
29
+ * the flat total across TRACKED variants (`isLowStock` /
30
+ * `productInventory`). No threshold configured means no alert, ever.
31
+ * - It fires on the CROSSING adjustment only — low after, not low before — so
32
+ * managers get one nudge per threshold breach, not one per order after it.
33
+ * - Redelivery safety is the CALLER's replay guard, not re-checked here:
34
+ * every call site sits behind its branch's own gate (the cart and buy-now
35
+ * `created` transactions, the draft branch's pending-to-paid flip, the
36
+ * POS handler's `claimAttempt`), so a redelivered event never recomputes the
37
+ * pair this compares.
38
+ *
39
+ * Takes the PRE- and POST-decrement product states the caller just computed —
40
+ * never a re-read, which could see its own write (no crossing observed) or a
41
+ * concurrent one (somebody else's crossing attributed here). Callers looping
42
+ * over lines must hand in the compounded pair (AGL-1828/AGL-1825): each line's
43
+ * `lifted` is the previous line's `updated`, so two lines of one product cross
44
+ * exactly once, on the line that actually breaches.
45
+ *
46
+ * Fire-and-forget like the sibling notifications on every branch —
47
+ * `notifyHostManagers` never throws, and an alert must never fail a webhook.
48
+ */ export function alertLowStockCrossing(hostId, lifted, updated) {
49
+ if (CommerceModel.isLowStock(updated) && !CommerceModel.isLowStock(lifted)) {
50
+ var _CommerceModel_productInventory;
51
+ void notifyHostManagers(hostId, {
52
+ type: 'content.lowStock',
53
+ title: `Low stock — ${updated.name}`,
54
+ body: `${(_CommerceModel_productInventory = CommerceModel.productInventory(updated)) != null ? _CommerceModel_productInventory : 0} left across tracked variants`,
55
+ link: `/${hostId}/products`
56
+ });
57
+ }
58
+ }
59
+
60
+ //# sourceMappingURL=low-stock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/low-stock.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { notifyHostManagers } from '@aglyn/tenant-data-admin'\nimport * as CommerceModel from '../model'\n\n/**\n * The AGL-281 low-stock crossing alert, extracted so every stock decrement\n * fires it (AGL-1826). It used to live inline on the buy-now branch only, so\n * whether a merchant was told they were running out depended on which door the\n * sale came through: the single-product storefront button alerted, while the\n * cart, a merchant-sent draft link and the register crossed the same threshold\n * silently.\n *\n * SEMANTICS ARE THE BUY-NOW BRANCH'S, UNCHANGED:\n *\n * - The threshold is the product's own `lowStockThreshold`, compared against\n * the flat total across TRACKED variants (`isLowStock` /\n * `productInventory`). No threshold configured means no alert, ever.\n * - It fires on the CROSSING adjustment only — low after, not low before — so\n * managers get one nudge per threshold breach, not one per order after it.\n * - Redelivery safety is the CALLER's replay guard, not re-checked here:\n * every call site sits behind its branch's own gate (the cart and buy-now\n * `created` transactions, the draft branch's pending-to-paid flip, the\n * POS handler's `claimAttempt`), so a redelivered event never recomputes the\n * pair this compares.\n *\n * Takes the PRE- and POST-decrement product states the caller just computed —\n * never a re-read, which could see its own write (no crossing observed) or a\n * concurrent one (somebody else's crossing attributed here). Callers looping\n * over lines must hand in the compounded pair (AGL-1828/AGL-1825): each line's\n * `lifted` is the previous line's `updated`, so two lines of one product cross\n * exactly once, on the line that actually breaches.\n *\n * Fire-and-forget like the sibling notifications on every branch —\n * `notifyHostManagers` never throws, and an alert must never fail a webhook.\n */\nexport function alertLowStockCrossing(\n hostId: string,\n lifted: Pick<\n CommerceModel.HostProduct,\n 'name' | 'variants' | 'lowStockThreshold'\n >,\n updated: Pick<\n CommerceModel.HostProduct,\n 'name' | 'variants' | 'lowStockThreshold'\n >,\n): void {\n if (\n CommerceModel.isLowStock(updated) &&\n !CommerceModel.isLowStock(lifted)\n ) {\n void notifyHostManagers(hostId, {\n type: 'content.lowStock',\n title: `Low stock — ${updated.name}`,\n body: `${CommerceModel.productInventory(updated) ?? 0} left across tracked variants`,\n link: `/${hostId}/products`,\n })\n }\n}\n"],"names":["notifyHostManagers","CommerceModel","alertLowStockCrossing","hostId","lifted","updated","isLowStock","type","title","name","body","productInventory","link"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,kBAAkB,QAAQ,2BAA0B;AAC7D,YAAYC,mBAAmB,oBAAU;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BC,GACD,OAAO,SAASC,sBACdC,MAAc,EACdC,MAGC,EACDC,OAGC;IAED,IACEJ,cAAcK,UAAU,CAACD,YACzB,CAACJ,cAAcK,UAAU,CAACF,SAC1B;YAIWH;QAHX,KAAKD,mBAAmBG,QAAQ;YAC9BI,MAAM;YACNC,OAAO,CAAC,YAAY,EAAEH,QAAQI,IAAI,EAAE;YACpCC,MAAM,IAAGT,kCAAAA,cAAcU,gBAAgB,CAACN,oBAA/BJ,kCAA2C,EAAE,6BAA6B,CAAC;YACpFW,MAAM,CAAC,CAAC,EAAET,OAAO,SAAS,CAAC;QAC7B;IACF;AACF"}