@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,199 @@
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 { HostProduct } from './commerce';
18
+ /**
19
+ * RESERVED STOCK: the arithmetic half of the checkout hold (AGL-2356).
20
+ *
21
+ * ## The defect this closes
22
+ *
23
+ * AGL-2320 made every stock decrement atomic, so a decrement is never LOST and
24
+ * the count never shows phantom stock. It could not make the storefront REFUSE
25
+ * an oversell, and said so: the gate and the decrement are minutes apart with
26
+ * money moving in between.
27
+ *
28
+ * - `checkout.ts` / `cart-checkout.ts` called `canPurchase` against a plain
29
+ * `.get()` when the Stripe Checkout Session was created. Nothing was
30
+ * written, so there was no document to contend on — N concurrent shoppers
31
+ * all read `inventory: 1`, all passed, and no locking discipline anywhere
32
+ * could change that, because a read races nothing.
33
+ * - the decrement happened in `billing-webhook.ts` when the session
34
+ * completed. By then the shopper has paid, so refusing there would take
35
+ * money and withhold goods — worse than the oversell.
36
+ *
37
+ * The only correct place to refuse is session creation, and refusing there
38
+ * requires WRITING. That is what a hold is: a unit spoken for by a live
39
+ * checkout, subtracted from what the next shopper is offered.
40
+ *
41
+ * ## Why the hold does not touch `inventory`
42
+ *
43
+ * `inventory` means UNITS ON THE SHELF and half the product reads it that way —
44
+ * the low-stock crossing alert, the restock queue, `reconcile-stock.ts`, the
45
+ * ledger's `appliedDelta` cap, the console's own count. A hold is not a unit
46
+ * leaving the shelf; it is a unit promised to someone who has not paid yet.
47
+ * Decrementing at hold time and "converting" at settlement would make every one
48
+ * of those readers wrong for the duration of a checkout, and would make a
49
+ * released hold indistinguishable from a restock in the ledger.
50
+ *
51
+ * So the shelf count is untouched and AVAILABILITY becomes a derived figure:
52
+ * `inventory` minus the live holds. `decrementVariantStock` is unchanged, which
53
+ * is the point — settlement is still the same single atomic decrement it was
54
+ * before this issue, and the hold is simply dropped afterwards.
55
+ *
56
+ * ## Why a lapsed hold is a release
57
+ *
58
+ * Every read prunes (`pruneStockHolds`), so a hold whose release never arrived
59
+ * lapses on its own the next time anybody asks. That is what makes a leaked
60
+ * hold a bounded inconvenience rather than the unbounded, invisible UNDER-sell
61
+ * this issue was held back for: stock is never stranded waiting on a webhook.
62
+ * The explicit release paths exist so the merchant and the shopper see the unit
63
+ * come back promptly, not because correctness depends on them.
64
+ */
65
+ /** One live reservation on a product, keyed by checkout attempt. */
66
+ export interface StockHold {
67
+ /** Wall-clock ms after which this hold is void. */
68
+ expiresAtMs: number;
69
+ /**
70
+ * Variant id → units held. A map and not a single number because one cart
71
+ * can hold two variants of one product, and a product document is the thing
72
+ * the transaction contends on — collapsing them would make a two-variant
73
+ * cart reserve the wrong shelf.
74
+ */
75
+ units?: Record<string, number>;
76
+ }
77
+ /**
78
+ * The product fields this file reads. Structural on purpose: `HostProduct`
79
+ * carries forty fields and the hold arithmetic must not be able to depend on
80
+ * any of them but these.
81
+ */
82
+ export interface HeldStockProduct extends Pick<HostProduct, 'variants' | 'oversellPolicy'> {
83
+ /** Hold key → hold. Absent on every product written before AGL-2356. */
84
+ stockHolds?: Record<string, StockHold>;
85
+ }
86
+ /**
87
+ * HOW LONG A UNIT STAYS SPOKEN FOR.
88
+ *
89
+ * This number is not free to choose. A hold that lapses while its Checkout
90
+ * Session can still be PAID reopens the exact window this file closes: the
91
+ * shopper pays, the webhook decrements, and the unit was sold to someone else
92
+ * in between. So the floor is the session's own lifetime.
93
+ *
94
+ * Stripe's default session lifetime is 24 HOURS, and a 24-hour stock hold is
95
+ * precisely the trade this issue was blocked on — one abandoned cart standing
96
+ * on a merchant's last unit for a day. The resolution is not to shorten the
97
+ * hold below the session; it is to shorten the SESSION. `checkout.ts` and
98
+ * `cart-checkout.ts` now send `expires_at` at `CHECKOUT_SESSION_TTL_MS`, which
99
+ * is Stripe's own minimum and the same half-hour the bookings plugin has always
100
+ * given a held room.
101
+ *
102
+ * The hold then outlives the session by `STOCK_HOLD_GRACE_MS`, so the ordering
103
+ * is always: session dies → `checkout.session.expired` releases → the hold was
104
+ * still standing when it did. The grace is what keeps clock skew between Stripe
105
+ * and this process from lapsing a hold under a session that is still payable.
106
+ */
107
+ /**
108
+ * 31 minutes, not 30. Stripe REJECTS an `expires_at` less than 30 minutes in
109
+ * the future, and it measures against its own clock at the moment the request
110
+ * lands — so a value computed here at exactly the floor fails whenever network
111
+ * latency or clock skew eats a second. The extra minute is the margin; it is
112
+ * not a product opinion, and the product opinion is "the shortest session
113
+ * Stripe will sell us".
114
+ */
115
+ export declare const CHECKOUT_SESSION_TTL_MS: number;
116
+ /** Cushion between the session expiring and its hold lapsing. */
117
+ export declare const STOCK_HOLD_GRACE_MS: number;
118
+ export declare const STOCK_HOLD_TTL_MS: number;
119
+ /**
120
+ * What a shopper is told when the units exist but are spoken for.
121
+ *
122
+ * Deliberately NOT the bare "Sold out" the empty-shelf refusal uses. The
123
+ * merchant has stock; someone else is in checkout with it. Naming the wait is
124
+ * what stops a shopper concluding the store is empty and leaving — the hold
125
+ * lapses within the half hour and the unit comes back.
126
+ */
127
+ export declare const STOCK_HELD_MESSAGE: string;
128
+ /**
129
+ * The product's holds with the lapsed ones dropped.
130
+ *
131
+ * Total, not a filter over a trusted shape: a hold whose `expiresAtMs` is
132
+ * absent or non-numeric is treated as EXPIRED rather than eternal. A merchant
133
+ * losing a unit forever to one corrupt map entry is the only direction with no
134
+ * recovery inside the product, and it is exactly the failure mode that kept
135
+ * this issue out of the launch build.
136
+ */
137
+ export declare function pruneStockHolds(holds: Record<string, StockHold> | undefined, nowMs: number): Record<string, StockHold>;
138
+ /**
139
+ * Units of one variant spoken for by live checkouts right now.
140
+ *
141
+ * `exceptHoldKey` is how a retry of one attempt does not stand in its own way:
142
+ * the shopper presses the same button twice, the second press re-derives the
143
+ * same hold key, and without this exclusion it would be refused the unit it is
144
+ * already holding.
145
+ */
146
+ export declare function heldVariantUnits(product: HeldStockProduct | undefined, variantId: string | undefined, nowMs: number, exceptHoldKey?: string): number;
147
+ /** Units held across every variant of a product — the console's figure. */
148
+ export declare function heldProductUnits(product: HeldStockProduct | undefined, nowMs: number): number;
149
+ /**
150
+ * Units a NEW checkout could take: the shelf minus the live holds.
151
+ *
152
+ * `null` when there is no number to answer with — an untracked variant, a
153
+ * backorder product (whose merchant chose to sell past zero, so availability is
154
+ * unbounded by construction), or a variant that does not exist. `null` rather
155
+ * than `Infinity` so a caller cannot do arithmetic on "no limit" and get a
156
+ * number, and never negative: a shelf that went short under a `backorder`
157
+ * policy reads as zero available, not as a debt.
158
+ *
159
+ * NOTE FOR THIS REPO: `strictNullChecks` is off, so `if (!available)` does NOT
160
+ * distinguish `null` from `0` here — and `0` is a legitimate, load-bearing
161
+ * answer meaning "tracked, and none left". Every caller must test `== null`.
162
+ */
163
+ export declare function availableVariantUnits(product: HeldStockProduct | undefined, variantId: string | undefined, nowMs: number, exceptHoldKey?: string): number | null;
164
+ /**
165
+ * Is there a hold to take at all?
166
+ *
167
+ * False for the cases `availableVariantUnits` answers `null` for — an untracked
168
+ * or backorder variant has no finite shelf to reserve against, and writing a
169
+ * hold for one would put a growing map on a document whose availability it can
170
+ * never change. The same shape as `holdPromotionSlot` skipping an UNCAPPED
171
+ * promotion: the unlimited case stays off the write path entirely.
172
+ */
173
+ export declare function stockIsReservable(product: HeldStockProduct | undefined, variantId: string | undefined, nowMs: number): boolean;
174
+ /**
175
+ * Can this attempt reserve `quantity` units?
176
+ *
177
+ * The hold-aware twin of `canPurchase`, and it must agree with it everywhere
178
+ * `canPurchase` says NO: an untracked variant is always purchasable, a
179
+ * backorder product always is, a missing variant never is. The single
180
+ * difference is that a tracked deny-policy variant is measured against the
181
+ * shelf MINUS what live checkouts are already holding.
182
+ *
183
+ * `canPurchase` is still the first test rather than re-derived arithmetic, so
184
+ * the two cannot drift on the cases they share.
185
+ */
186
+ export declare function canReserveStock(product: HeldStockProduct | undefined, variantId: string | undefined, quantity?: number, nowMs?: number, exceptHoldKey?: string): boolean;
187
+ /**
188
+ * What the console prints beside a stock count (AGL-2356).
189
+ *
190
+ * The merchant-facing half of the decision, and the reason a hold is not just
191
+ * an internal optimisation. `inventory` deliberately does not move while a unit
192
+ * is held, so a merchant looking at "3 in stock" while the storefront refuses
193
+ * the third sale has no way to reason about it — the number is right and the
194
+ * behaviour looks wrong. This is what names the gap.
195
+ *
196
+ * Empty string when nothing is held, so the row stays exactly as it reads today
197
+ * on the overwhelming majority of products.
198
+ */
199
+ export declare function stockHoldLabel(product: HeldStockProduct | undefined, nowMs?: number): string;
@@ -0,0 +1,190 @@
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 { canPurchase } from "./commerce.js";
17
+ /**
18
+ * HOW LONG A UNIT STAYS SPOKEN FOR.
19
+ *
20
+ * This number is not free to choose. A hold that lapses while its Checkout
21
+ * Session can still be PAID reopens the exact window this file closes: the
22
+ * shopper pays, the webhook decrements, and the unit was sold to someone else
23
+ * in between. So the floor is the session's own lifetime.
24
+ *
25
+ * Stripe's default session lifetime is 24 HOURS, and a 24-hour stock hold is
26
+ * precisely the trade this issue was blocked on — one abandoned cart standing
27
+ * on a merchant's last unit for a day. The resolution is not to shorten the
28
+ * hold below the session; it is to shorten the SESSION. `checkout.ts` and
29
+ * `cart-checkout.ts` now send `expires_at` at `CHECKOUT_SESSION_TTL_MS`, which
30
+ * is Stripe's own minimum and the same half-hour the bookings plugin has always
31
+ * given a held room.
32
+ *
33
+ * The hold then outlives the session by `STOCK_HOLD_GRACE_MS`, so the ordering
34
+ * is always: session dies → `checkout.session.expired` releases → the hold was
35
+ * still standing when it did. The grace is what keeps clock skew between Stripe
36
+ * and this process from lapsing a hold under a session that is still payable.
37
+ */ /**
38
+ * 31 minutes, not 30. Stripe REJECTS an `expires_at` less than 30 minutes in
39
+ * the future, and it measures against its own clock at the moment the request
40
+ * lands — so a value computed here at exactly the floor fails whenever network
41
+ * latency or clock skew eats a second. The extra minute is the margin; it is
42
+ * not a product opinion, and the product opinion is "the shortest session
43
+ * Stripe will sell us".
44
+ */ export const CHECKOUT_SESSION_TTL_MS = 31 * 60 * 1000;
45
+ /** Cushion between the session expiring and its hold lapsing. */ export const STOCK_HOLD_GRACE_MS = 10 * 60 * 1000;
46
+ export const STOCK_HOLD_TTL_MS = CHECKOUT_SESSION_TTL_MS + STOCK_HOLD_GRACE_MS;
47
+ /**
48
+ * What a shopper is told when the units exist but are spoken for.
49
+ *
50
+ * Deliberately NOT the bare "Sold out" the empty-shelf refusal uses. The
51
+ * merchant has stock; someone else is in checkout with it. Naming the wait is
52
+ * what stops a shopper concluding the store is empty and leaving — the hold
53
+ * lapses within the half hour and the unit comes back.
54
+ */ export const STOCK_HELD_MESSAGE = 'The last of these is in someone else’s checkout. ' + 'Try again in a few minutes.';
55
+ /**
56
+ * The product's holds with the lapsed ones dropped.
57
+ *
58
+ * Total, not a filter over a trusted shape: a hold whose `expiresAtMs` is
59
+ * absent or non-numeric is treated as EXPIRED rather than eternal. A merchant
60
+ * losing a unit forever to one corrupt map entry is the only direction with no
61
+ * recovery inside the product, and it is exactly the failure mode that kept
62
+ * this issue out of the launch build.
63
+ */ export function pruneStockHolds(holds, nowMs) {
64
+ const live = {};
65
+ for (const [key, hold] of Object.entries(holds != null ? holds : {})){
66
+ var _ref;
67
+ const expiresAtMs = Number(hold == null ? void 0 : hold.expiresAtMs);
68
+ if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) continue;
69
+ const units = {};
70
+ for (const [variantId, raw] of Object.entries((_ref = hold == null ? void 0 : hold.units) != null ? _ref : {})){
71
+ const count = Math.round(Number(raw));
72
+ // A non-numeric or non-positive count holds nothing. Coerced rather than
73
+ // trusted for the same reason as the expiry: a corrupt entry must not be
74
+ // able to reserve `NaN` units and make every comparison below false.
75
+ if (Number.isFinite(count) && count > 0) units[variantId] = count;
76
+ }
77
+ live[key] = {
78
+ expiresAtMs,
79
+ units
80
+ };
81
+ }
82
+ return live;
83
+ }
84
+ /**
85
+ * Units of one variant spoken for by live checkouts right now.
86
+ *
87
+ * `exceptHoldKey` is how a retry of one attempt does not stand in its own way:
88
+ * the shopper presses the same button twice, the second press re-derives the
89
+ * same hold key, and without this exclusion it would be refused the unit it is
90
+ * already holding.
91
+ */ export function heldVariantUnits(product, variantId, nowMs, exceptHoldKey) {
92
+ if (!variantId) return 0;
93
+ const live = pruneStockHolds(product == null ? void 0 : product.stockHolds, nowMs);
94
+ if (exceptHoldKey) delete live[exceptHoldKey];
95
+ let held = 0;
96
+ for (const hold of Object.values(live)){
97
+ var _ref;
98
+ var _hold_units;
99
+ held += Number((_ref = (_hold_units = hold.units) == null ? void 0 : _hold_units[variantId]) != null ? _ref : 0);
100
+ }
101
+ return held;
102
+ }
103
+ /** Units held across every variant of a product — the console's figure. */ export function heldProductUnits(product, nowMs) {
104
+ let held = 0;
105
+ for (const hold of Object.values(pruneStockHolds(product == null ? void 0 : product.stockHolds, nowMs))){
106
+ var _hold_units;
107
+ for (const units of Object.values((_hold_units = hold.units) != null ? _hold_units : {}))held += Number(units);
108
+ }
109
+ return held;
110
+ }
111
+ /**
112
+ * Resolve the variant a purchase refers to, EXACTLY as `canPurchase` does.
113
+ *
114
+ * Shared rather than re-derived, because the pre-filter and the in-transaction
115
+ * re-check disagreeing about which variant they are talking about is a defect
116
+ * with no visible symptom until the wrong shelf goes negative.
117
+ */ function resolveVariant(product, variantId) {
118
+ var _product_variants, _product_variants1;
119
+ return variantId ? product == null ? void 0 : (_product_variants = product.variants) == null ? void 0 : _product_variants.find((item)=>item.id === variantId) : product == null ? void 0 : (_product_variants1 = product.variants) == null ? void 0 : _product_variants1[0];
120
+ }
121
+ /**
122
+ * Units a NEW checkout could take: the shelf minus the live holds.
123
+ *
124
+ * `null` when there is no number to answer with — an untracked variant, a
125
+ * backorder product (whose merchant chose to sell past zero, so availability is
126
+ * unbounded by construction), or a variant that does not exist. `null` rather
127
+ * than `Infinity` so a caller cannot do arithmetic on "no limit" and get a
128
+ * number, and never negative: a shelf that went short under a `backorder`
129
+ * policy reads as zero available, not as a debt.
130
+ *
131
+ * NOTE FOR THIS REPO: `strictNullChecks` is off, so `if (!available)` does NOT
132
+ * distinguish `null` from `0` here — and `0` is a legitimate, load-bearing
133
+ * answer meaning "tracked, and none left". Every caller must test `== null`.
134
+ */ export function availableVariantUnits(product, variantId, nowMs, exceptHoldKey) {
135
+ const variant = resolveVariant(product, variantId);
136
+ if (!variant) return null;
137
+ if (variant.inventory == null) return null;
138
+ if ((product == null ? void 0 : product.oversellPolicy) === 'backorder') return null;
139
+ const onShelf = Math.round(Number(variant.inventory));
140
+ if (!Number.isFinite(onShelf)) return null;
141
+ return Math.max(0, onShelf - heldVariantUnits(product, variant.id, nowMs, exceptHoldKey));
142
+ }
143
+ /**
144
+ * Is there a hold to take at all?
145
+ *
146
+ * False for the cases `availableVariantUnits` answers `null` for — an untracked
147
+ * or backorder variant has no finite shelf to reserve against, and writing a
148
+ * hold for one would put a growing map on a document whose availability it can
149
+ * never change. The same shape as `holdPromotionSlot` skipping an UNCAPPED
150
+ * promotion: the unlimited case stays off the write path entirely.
151
+ */ export function stockIsReservable(product, variantId, nowMs) {
152
+ return availableVariantUnits(product, variantId, nowMs) != null;
153
+ }
154
+ /**
155
+ * Can this attempt reserve `quantity` units?
156
+ *
157
+ * The hold-aware twin of `canPurchase`, and it must agree with it everywhere
158
+ * `canPurchase` says NO: an untracked variant is always purchasable, a
159
+ * backorder product always is, a missing variant never is. The single
160
+ * difference is that a tracked deny-policy variant is measured against the
161
+ * shelf MINUS what live checkouts are already holding.
162
+ *
163
+ * `canPurchase` is still the first test rather than re-derived arithmetic, so
164
+ * the two cannot drift on the cases they share.
165
+ */ export function canReserveStock(product, variantId, quantity = 1, nowMs = Date.now(), exceptHoldKey) {
166
+ if (!product) return false;
167
+ if (!canPurchase(product, variantId, quantity)) return false;
168
+ const available = availableVariantUnits(product, variantId, nowMs, exceptHoldKey);
169
+ // `== null`, never `!available` — see the note on `availableVariantUnits`.
170
+ if (available == null) return true;
171
+ return available >= Math.max(1, Math.round(Number(quantity) || 1));
172
+ }
173
+ /**
174
+ * What the console prints beside a stock count (AGL-2356).
175
+ *
176
+ * The merchant-facing half of the decision, and the reason a hold is not just
177
+ * an internal optimisation. `inventory` deliberately does not move while a unit
178
+ * is held, so a merchant looking at "3 in stock" while the storefront refuses
179
+ * the third sale has no way to reason about it — the number is right and the
180
+ * behaviour looks wrong. This is what names the gap.
181
+ *
182
+ * Empty string when nothing is held, so the row stays exactly as it reads today
183
+ * on the overwhelming majority of products.
184
+ */ export function stockHoldLabel(product, nowMs = Date.now()) {
185
+ const held = heldProductUnits(product, nowMs);
186
+ if (held <= 0) return '';
187
+ return `${held} reserved in checkout`;
188
+ }
189
+
190
+ //# sourceMappingURL=commerce-stock-holds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-stock-holds.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 { HostProduct } from './commerce'\nimport { canPurchase } from './commerce'\n\n/**\n * RESERVED STOCK: the arithmetic half of the checkout hold (AGL-2356).\n *\n * ## The defect this closes\n *\n * AGL-2320 made every stock decrement atomic, so a decrement is never LOST and\n * the count never shows phantom stock. It could not make the storefront REFUSE\n * an oversell, and said so: the gate and the decrement are minutes apart with\n * money moving in between.\n *\n * - `checkout.ts` / `cart-checkout.ts` called `canPurchase` against a plain\n * `.get()` when the Stripe Checkout Session was created. Nothing was\n * written, so there was no document to contend on — N concurrent shoppers\n * all read `inventory: 1`, all passed, and no locking discipline anywhere\n * could change that, because a read races nothing.\n * - the decrement happened in `billing-webhook.ts` when the session\n * completed. By then the shopper has paid, so refusing there would take\n * money and withhold goods — worse than the oversell.\n *\n * The only correct place to refuse is session creation, and refusing there\n * requires WRITING. That is what a hold is: a unit spoken for by a live\n * checkout, subtracted from what the next shopper is offered.\n *\n * ## Why the hold does not touch `inventory`\n *\n * `inventory` means UNITS ON THE SHELF and half the product reads it that way —\n * the low-stock crossing alert, the restock queue, `reconcile-stock.ts`, the\n * ledger's `appliedDelta` cap, the console's own count. A hold is not a unit\n * leaving the shelf; it is a unit promised to someone who has not paid yet.\n * Decrementing at hold time and \"converting\" at settlement would make every one\n * of those readers wrong for the duration of a checkout, and would make a\n * released hold indistinguishable from a restock in the ledger.\n *\n * So the shelf count is untouched and AVAILABILITY becomes a derived figure:\n * `inventory` minus the live holds. `decrementVariantStock` is unchanged, which\n * is the point — settlement is still the same single atomic decrement it was\n * before this issue, and the hold is simply dropped afterwards.\n *\n * ## Why a lapsed hold is a release\n *\n * Every read prunes (`pruneStockHolds`), so a hold whose release never arrived\n * lapses on its own the next time anybody asks. That is what makes a leaked\n * hold a bounded inconvenience rather than the unbounded, invisible UNDER-sell\n * this issue was held back for: stock is never stranded waiting on a webhook.\n * The explicit release paths exist so the merchant and the shopper see the unit\n * come back promptly, not because correctness depends on them.\n */\n\n/** One live reservation on a product, keyed by checkout attempt. */\nexport interface StockHold {\n /** Wall-clock ms after which this hold is void. */\n expiresAtMs: number\n /**\n * Variant id → units held. A map and not a single number because one cart\n * can hold two variants of one product, and a product document is the thing\n * the transaction contends on — collapsing them would make a two-variant\n * cart reserve the wrong shelf.\n */\n units?: Record<string, number>\n}\n\n/**\n * The product fields this file reads. Structural on purpose: `HostProduct`\n * carries forty fields and the hold arithmetic must not be able to depend on\n * any of them but these.\n */\nexport interface HeldStockProduct extends Pick<\n HostProduct,\n 'variants' | 'oversellPolicy'\n> {\n /** Hold key → hold. Absent on every product written before AGL-2356. */\n stockHolds?: Record<string, StockHold>\n}\n\n/**\n * HOW LONG A UNIT STAYS SPOKEN FOR.\n *\n * This number is not free to choose. A hold that lapses while its Checkout\n * Session can still be PAID reopens the exact window this file closes: the\n * shopper pays, the webhook decrements, and the unit was sold to someone else\n * in between. So the floor is the session's own lifetime.\n *\n * Stripe's default session lifetime is 24 HOURS, and a 24-hour stock hold is\n * precisely the trade this issue was blocked on — one abandoned cart standing\n * on a merchant's last unit for a day. The resolution is not to shorten the\n * hold below the session; it is to shorten the SESSION. `checkout.ts` and\n * `cart-checkout.ts` now send `expires_at` at `CHECKOUT_SESSION_TTL_MS`, which\n * is Stripe's own minimum and the same half-hour the bookings plugin has always\n * given a held room.\n *\n * The hold then outlives the session by `STOCK_HOLD_GRACE_MS`, so the ordering\n * is always: session dies → `checkout.session.expired` releases → the hold was\n * still standing when it did. The grace is what keeps clock skew between Stripe\n * and this process from lapsing a hold under a session that is still payable.\n */\n/**\n * 31 minutes, not 30. Stripe REJECTS an `expires_at` less than 30 minutes in\n * the future, and it measures against its own clock at the moment the request\n * lands — so a value computed here at exactly the floor fails whenever network\n * latency or clock skew eats a second. The extra minute is the margin; it is\n * not a product opinion, and the product opinion is \"the shortest session\n * Stripe will sell us\".\n */\nexport const CHECKOUT_SESSION_TTL_MS = 31 * 60 * 1000\n\n/** Cushion between the session expiring and its hold lapsing. */\nexport const STOCK_HOLD_GRACE_MS = 10 * 60 * 1000\n\nexport const STOCK_HOLD_TTL_MS = CHECKOUT_SESSION_TTL_MS + STOCK_HOLD_GRACE_MS\n\n/**\n * What a shopper is told when the units exist but are spoken for.\n *\n * Deliberately NOT the bare \"Sold out\" the empty-shelf refusal uses. The\n * merchant has stock; someone else is in checkout with it. Naming the wait is\n * what stops a shopper concluding the store is empty and leaving — the hold\n * lapses within the half hour and the unit comes back.\n */\nexport const STOCK_HELD_MESSAGE =\n 'The last of these is in someone else’s checkout. ' +\n 'Try again in a few minutes.'\n\n/**\n * The product's holds with the lapsed ones dropped.\n *\n * Total, not a filter over a trusted shape: a hold whose `expiresAtMs` is\n * absent or non-numeric is treated as EXPIRED rather than eternal. A merchant\n * losing a unit forever to one corrupt map entry is the only direction with no\n * recovery inside the product, and it is exactly the failure mode that kept\n * this issue out of the launch build.\n */\nexport function pruneStockHolds(\n holds: Record<string, StockHold> | undefined,\n nowMs: number,\n): Record<string, StockHold> {\n const live: Record<string, StockHold> = {}\n for (const [key, hold] of Object.entries(holds ?? {})) {\n const expiresAtMs = Number(hold?.expiresAtMs)\n if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) continue\n const units: Record<string, number> = {}\n for (const [variantId, raw] of Object.entries(hold?.units ?? {})) {\n const count = Math.round(Number(raw))\n // A non-numeric or non-positive count holds nothing. Coerced rather than\n // trusted for the same reason as the expiry: a corrupt entry must not be\n // able to reserve `NaN` units and make every comparison below false.\n if (Number.isFinite(count) && count > 0) units[variantId] = count\n }\n live[key] = { expiresAtMs, units }\n }\n return live\n}\n\n/**\n * Units of one variant spoken for by live checkouts right now.\n *\n * `exceptHoldKey` is how a retry of one attempt does not stand in its own way:\n * the shopper presses the same button twice, the second press re-derives the\n * same hold key, and without this exclusion it would be refused the unit it is\n * already holding.\n */\nexport function heldVariantUnits(\n product: HeldStockProduct | undefined,\n variantId: string | undefined,\n nowMs: number,\n exceptHoldKey?: string,\n): number {\n if (!variantId) return 0\n const live = pruneStockHolds(product?.stockHolds, nowMs)\n if (exceptHoldKey) delete live[exceptHoldKey]\n let held = 0\n for (const hold of Object.values(live)) {\n held += Number(hold.units?.[variantId] ?? 0)\n }\n return held\n}\n\n/** Units held across every variant of a product — the console's figure. */\nexport function heldProductUnits(\n product: HeldStockProduct | undefined,\n nowMs: number,\n): number {\n let held = 0\n for (const hold of Object.values(\n pruneStockHolds(product?.stockHolds, nowMs),\n )) {\n for (const units of Object.values(hold.units ?? {})) held += Number(units)\n }\n return held\n}\n\n/**\n * Resolve the variant a purchase refers to, EXACTLY as `canPurchase` does.\n *\n * Shared rather than re-derived, because the pre-filter and the in-transaction\n * re-check disagreeing about which variant they are talking about is a defect\n * with no visible symptom until the wrong shelf goes negative.\n */\nfunction resolveVariant(\n product: HeldStockProduct | undefined,\n variantId: string | undefined,\n) {\n return variantId\n ? product?.variants?.find((item) => item.id === variantId)\n : product?.variants?.[0]\n}\n\n/**\n * Units a NEW checkout could take: the shelf minus the live holds.\n *\n * `null` when there is no number to answer with — an untracked variant, a\n * backorder product (whose merchant chose to sell past zero, so availability is\n * unbounded by construction), or a variant that does not exist. `null` rather\n * than `Infinity` so a caller cannot do arithmetic on \"no limit\" and get a\n * number, and never negative: a shelf that went short under a `backorder`\n * policy reads as zero available, not as a debt.\n *\n * NOTE FOR THIS REPO: `strictNullChecks` is off, so `if (!available)` does NOT\n * distinguish `null` from `0` here — and `0` is a legitimate, load-bearing\n * answer meaning \"tracked, and none left\". Every caller must test `== null`.\n */\nexport function availableVariantUnits(\n product: HeldStockProduct | undefined,\n variantId: string | undefined,\n nowMs: number,\n exceptHoldKey?: string,\n): number | null {\n const variant = resolveVariant(product, variantId)\n if (!variant) return null\n if (variant.inventory == null) return null\n if (product?.oversellPolicy === 'backorder') return null\n const onShelf = Math.round(Number(variant.inventory))\n if (!Number.isFinite(onShelf)) return null\n return Math.max(\n 0,\n onShelf - heldVariantUnits(product, variant.id, nowMs, exceptHoldKey),\n )\n}\n\n/**\n * Is there a hold to take at all?\n *\n * False for the cases `availableVariantUnits` answers `null` for — an untracked\n * or backorder variant has no finite shelf to reserve against, and writing a\n * hold for one would put a growing map on a document whose availability it can\n * never change. The same shape as `holdPromotionSlot` skipping an UNCAPPED\n * promotion: the unlimited case stays off the write path entirely.\n */\nexport function stockIsReservable(\n product: HeldStockProduct | undefined,\n variantId: string | undefined,\n nowMs: number,\n): boolean {\n return availableVariantUnits(product, variantId, nowMs) != null\n}\n\n/**\n * Can this attempt reserve `quantity` units?\n *\n * The hold-aware twin of `canPurchase`, and it must agree with it everywhere\n * `canPurchase` says NO: an untracked variant is always purchasable, a\n * backorder product always is, a missing variant never is. The single\n * difference is that a tracked deny-policy variant is measured against the\n * shelf MINUS what live checkouts are already holding.\n *\n * `canPurchase` is still the first test rather than re-derived arithmetic, so\n * the two cannot drift on the cases they share.\n */\nexport function canReserveStock(\n product: HeldStockProduct | undefined,\n variantId: string | undefined,\n quantity = 1,\n nowMs: number = Date.now(),\n exceptHoldKey?: string,\n): boolean {\n if (!product) return false\n if (!canPurchase(product as HostProduct, variantId, quantity)) return false\n const available = availableVariantUnits(\n product,\n variantId,\n nowMs,\n exceptHoldKey,\n )\n // `== null`, never `!available` — see the note on `availableVariantUnits`.\n if (available == null) return true\n return available >= Math.max(1, Math.round(Number(quantity) || 1))\n}\n\n/**\n * What the console prints beside a stock count (AGL-2356).\n *\n * The merchant-facing half of the decision, and the reason a hold is not just\n * an internal optimisation. `inventory` deliberately does not move while a unit\n * is held, so a merchant looking at \"3 in stock\" while the storefront refuses\n * the third sale has no way to reason about it — the number is right and the\n * behaviour looks wrong. This is what names the gap.\n *\n * Empty string when nothing is held, so the row stays exactly as it reads today\n * on the overwhelming majority of products.\n */\nexport function stockHoldLabel(\n product: HeldStockProduct | undefined,\n nowMs: number = Date.now(),\n): string {\n const held = heldProductUnits(product, nowMs)\n if (held <= 0) return ''\n return `${held} reserved in checkout`\n}\n"],"names":["canPurchase","CHECKOUT_SESSION_TTL_MS","STOCK_HOLD_GRACE_MS","STOCK_HOLD_TTL_MS","STOCK_HELD_MESSAGE","pruneStockHolds","holds","nowMs","live","key","hold","Object","entries","expiresAtMs","Number","isFinite","units","variantId","raw","count","Math","round","heldVariantUnits","product","exceptHoldKey","stockHolds","held","values","heldProductUnits","resolveVariant","variants","find","item","id","availableVariantUnits","variant","inventory","oversellPolicy","onShelf","max","stockIsReservable","canReserveStock","quantity","Date","now","available","stockHoldLabel"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,WAAW,QAAQ,gBAAY;AA4ExC;;;;;;;;;;;;;;;;;;;;CAoBC,GACD;;;;;;;CAOC,GACD,OAAO,MAAMC,0BAA0B,KAAK,KAAK,KAAI;AAErD,+DAA+D,GAC/D,OAAO,MAAMC,sBAAsB,KAAK,KAAK,KAAI;AAEjD,OAAO,MAAMC,oBAAoBF,0BAA0BC,oBAAmB;AAE9E;;;;;;;CAOC,GACD,OAAO,MAAME,qBACX,sDACA,8BAA6B;AAE/B;;;;;;;;CAQC,GACD,OAAO,SAASC,gBACdC,KAA4C,EAC5CC,KAAa;IAEb,MAAMC,OAAkC,CAAC;IACzC,KAAK,MAAM,CAACC,KAAKC,KAAK,IAAIC,OAAOC,OAAO,CAACN,gBAAAA,QAAS,CAAC,GAAI;;QACrD,MAAMO,cAAcC,OAAOJ,wBAAAA,KAAMG,WAAW;QAC5C,IAAI,CAACC,OAAOC,QAAQ,CAACF,gBAAgBA,eAAeN,OAAO;QAC3D,MAAMS,QAAgC,CAAC;QACvC,KAAK,MAAM,CAACC,WAAWC,IAAI,IAAIP,OAAOC,OAAO,SAACF,wBAAAA,KAAMM,KAAK,mBAAI,CAAC,GAAI;YAChE,MAAMG,QAAQC,KAAKC,KAAK,CAACP,OAAOI;YAChC,yEAAyE;YACzE,yEAAyE;YACzE,qEAAqE;YACrE,IAAIJ,OAAOC,QAAQ,CAACI,UAAUA,QAAQ,GAAGH,KAAK,CAACC,UAAU,GAAGE;QAC9D;QACAX,IAAI,CAACC,IAAI,GAAG;YAAEI;YAAaG;QAAM;IACnC;IACA,OAAOR;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASc,iBACdC,OAAqC,EACrCN,SAA6B,EAC7BV,KAAa,EACbiB,aAAsB;IAEtB,IAAI,CAACP,WAAW,OAAO;IACvB,MAAMT,OAAOH,gBAAgBkB,2BAAAA,QAASE,UAAU,EAAElB;IAClD,IAAIiB,eAAe,OAAOhB,IAAI,CAACgB,cAAc;IAC7C,IAAIE,OAAO;IACX,KAAK,MAAMhB,QAAQC,OAAOgB,MAAM,CAACnB,MAAO;;YACvBE;QAAfgB,QAAQZ,gBAAOJ,cAAAA,KAAKM,KAAK,qBAAVN,WAAY,CAACO,UAAU,mBAAI;IAC5C;IACA,OAAOS;AACT;AAEA,yEAAyE,GACzE,OAAO,SAASE,iBACdL,OAAqC,EACrChB,KAAa;IAEb,IAAImB,OAAO;IACX,KAAK,MAAMhB,QAAQC,OAAOgB,MAAM,CAC9BtB,gBAAgBkB,2BAAAA,QAASE,UAAU,EAAElB,QACpC;YACiCG;QAAlC,KAAK,MAAMM,SAASL,OAAOgB,MAAM,EAACjB,cAAAA,KAAKM,KAAK,YAAVN,cAAc,CAAC,GAAIgB,QAAQZ,OAAOE;IACtE;IACA,OAAOU;AACT;AAEA;;;;;;CAMC,GACD,SAASG,eACPN,OAAqC,EACrCN,SAA6B;QAGzBM,mBACAA;IAFJ,OAAON,YACHM,4BAAAA,oBAAAA,QAASO,QAAQ,qBAAjBP,kBAAmBQ,IAAI,CAAC,CAACC,OAASA,KAAKC,EAAE,KAAKhB,aAC9CM,4BAAAA,qBAAAA,QAASO,QAAQ,qBAAjBP,kBAAmB,CAAC,EAAE;AAC5B;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASW,sBACdX,OAAqC,EACrCN,SAA6B,EAC7BV,KAAa,EACbiB,aAAsB;IAEtB,MAAMW,UAAUN,eAAeN,SAASN;IACxC,IAAI,CAACkB,SAAS,OAAO;IACrB,IAAIA,QAAQC,SAAS,IAAI,MAAM,OAAO;IACtC,IAAIb,CAAAA,2BAAAA,QAASc,cAAc,MAAK,aAAa,OAAO;IACpD,MAAMC,UAAUlB,KAAKC,KAAK,CAACP,OAAOqB,QAAQC,SAAS;IACnD,IAAI,CAACtB,OAAOC,QAAQ,CAACuB,UAAU,OAAO;IACtC,OAAOlB,KAAKmB,GAAG,CACb,GACAD,UAAUhB,iBAAiBC,SAASY,QAAQF,EAAE,EAAE1B,OAAOiB;AAE3D;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASgB,kBACdjB,OAAqC,EACrCN,SAA6B,EAC7BV,KAAa;IAEb,OAAO2B,sBAAsBX,SAASN,WAAWV,UAAU;AAC7D;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASkC,gBACdlB,OAAqC,EACrCN,SAA6B,EAC7ByB,WAAW,CAAC,EACZnC,QAAgBoC,KAAKC,GAAG,EAAE,EAC1BpB,aAAsB;IAEtB,IAAI,CAACD,SAAS,OAAO;IACrB,IAAI,CAACvB,YAAYuB,SAAwBN,WAAWyB,WAAW,OAAO;IACtE,MAAMG,YAAYX,sBAChBX,SACAN,WACAV,OACAiB;IAEF,2EAA2E;IAC3E,IAAIqB,aAAa,MAAM,OAAO;IAC9B,OAAOA,aAAazB,KAAKmB,GAAG,CAAC,GAAGnB,KAAKC,KAAK,CAACP,OAAO4B,aAAa;AACjE;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASI,eACdvB,OAAqC,EACrChB,QAAgBoC,KAAKC,GAAG,EAAE;IAE1B,MAAMlB,OAAOE,iBAAiBL,SAAShB;IACvC,IAAImB,QAAQ,GAAG,OAAO;IACtB,OAAO,GAAGA,KAAK,qBAAqB,CAAC;AACvC"}
@@ -0,0 +1,67 @@
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
+ /**
18
+ * The TENANT-side live-subscription list — one copy, for the whole plugin
19
+ * (AGL-1849).
20
+ *
21
+ * ## Why this is not `isLiveSubscriptionStatus`
22
+ *
23
+ * AGL-1715-EXEMPT: this is a decision about the TENANT's own site members'
24
+ * subscriptions to the TENANT's products (`hosts/{hostId}/subscriptions`) — a
25
+ * different Stripe account, a different buyer and a different question from
26
+ * `isLiveSubscriptionStatus` in `@aglyn/aglyn`, which asks whether an Aglyn org
27
+ * is paying US. The words coincide only because both are Stripe subscription
28
+ * statuses. Converging them would tie a shopper's access to a tenant's store to
29
+ * Aglyn's own billing rules, so the two stay apart deliberately.
30
+ *
31
+ * ## Why it is its own file
32
+ *
33
+ * It used to be two private `LIVE_STATUSES` sets, in `server/gate.ts` and
34
+ * `server/member-post.ts`, each carrying its own copy of the marker and its own
35
+ * allowlist entry. Two copies of a list that decides who has already paid is
36
+ * the shape AGL-1715 exists to prevent, and the third caller — the
37
+ * duplicate-subscription guard in `server/checkout.ts` — would have made it
38
+ * three. Hoisting it to the model gives the tenant side the same single source
39
+ * the org side has, rather than a fourth transcription of the same three words.
40
+ *
41
+ * ## Why `past_due` counts as live
42
+ *
43
+ * A subscription in dunning is still a subscription: Stripe is retrying the
44
+ * card, the member has not cancelled, and access has not lapsed. Dropping it
45
+ * would revoke a paying member's content the moment a card expired, and — for
46
+ * the checkout guard — would invite them to buy a SECOND subscription to a
47
+ * product they are already being billed for, which is precisely the duplicate
48
+ * this list is used to refuse.
49
+ *
50
+ * A cancelled or `incomplete_expired` subscription is NOT live, and that is
51
+ * equally load-bearing: a member who cancelled must be able to re-subscribe.
52
+ * A guard built on "has a subscription row" instead of "has a LIVE one" would
53
+ * lock them out forever (the AGL-1715 lesson, restated on the tenant side).
54
+ */
55
+ export declare const TENANT_LIVE_SUBSCRIPTION_STATUSES: readonly ["active", "trialing", "past_due"];
56
+ export type TenantLiveSubscriptionStatus = (typeof TENANT_LIVE_SUBSCRIPTION_STATUSES)[number];
57
+ /**
58
+ * Is this tenant-side subscription status one that still grants access / still
59
+ * counts as an existing subscription?
60
+ *
61
+ * Takes `unknown` on purpose: every caller reads it straight off a Firestore
62
+ * snapshot, where the field may be absent, a number, or anything else a bad
63
+ * write left behind. Narrowing here means no caller has to `String(...)` it
64
+ * defensively and get a `'undefined'` that quietly fails the test for the
65
+ * wrong reason.
66
+ */
67
+ export declare function isTenantSubscriptionLive(status: unknown): boolean;
@@ -0,0 +1,76 @@
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
+ * The TENANT-side live-subscription list — one copy, for the whole plugin
18
+ * (AGL-1849).
19
+ *
20
+ * ## Why this is not `isLiveSubscriptionStatus`
21
+ *
22
+ * AGL-1715-EXEMPT: this is a decision about the TENANT's own site members'
23
+ * subscriptions to the TENANT's products (`hosts/{hostId}/subscriptions`) — a
24
+ * different Stripe account, a different buyer and a different question from
25
+ * `isLiveSubscriptionStatus` in `@aglyn/aglyn`, which asks whether an Aglyn org
26
+ * is paying US. The words coincide only because both are Stripe subscription
27
+ * statuses. Converging them would tie a shopper's access to a tenant's store to
28
+ * Aglyn's own billing rules, so the two stay apart deliberately.
29
+ *
30
+ * ## Why it is its own file
31
+ *
32
+ * It used to be two private `LIVE_STATUSES` sets, in `server/gate.ts` and
33
+ * `server/member-post.ts`, each carrying its own copy of the marker and its own
34
+ * allowlist entry. Two copies of a list that decides who has already paid is
35
+ * the shape AGL-1715 exists to prevent, and the third caller — the
36
+ * duplicate-subscription guard in `server/checkout.ts` — would have made it
37
+ * three. Hoisting it to the model gives the tenant side the same single source
38
+ * the org side has, rather than a fourth transcription of the same three words.
39
+ *
40
+ * ## Why `past_due` counts as live
41
+ *
42
+ * A subscription in dunning is still a subscription: Stripe is retrying the
43
+ * card, the member has not cancelled, and access has not lapsed. Dropping it
44
+ * would revoke a paying member's content the moment a card expired, and — for
45
+ * the checkout guard — would invite them to buy a SECOND subscription to a
46
+ * product they are already being billed for, which is precisely the duplicate
47
+ * this list is used to refuse.
48
+ *
49
+ * A cancelled or `incomplete_expired` subscription is NOT live, and that is
50
+ * equally load-bearing: a member who cancelled must be able to re-subscribe.
51
+ * A guard built on "has a subscription row" instead of "has a LIVE one" would
52
+ * lock them out forever (the AGL-1715 lesson, restated on the tenant side).
53
+ */ // AGL-1715-EXEMPT: the tenant side's own list, for the reasons set out above —
54
+ // a tenant's site member subscribing to a tenant's product, on the tenant's
55
+ // Stripe account. Not the Aglyn org's subscription to us, so not
56
+ // `isLiveSubscriptionStatus`. (Restated here because the guard wants the marker
57
+ // within 25 lines of the literal, and the reasoning above is longer than that.)
58
+ export const TENANT_LIVE_SUBSCRIPTION_STATUSES = [
59
+ 'active',
60
+ 'trialing',
61
+ 'past_due'
62
+ ];
63
+ /**
64
+ * Is this tenant-side subscription status one that still grants access / still
65
+ * counts as an existing subscription?
66
+ *
67
+ * Takes `unknown` on purpose: every caller reads it straight off a Firestore
68
+ * snapshot, where the field may be absent, a number, or anything else a bad
69
+ * write left behind. Narrowing here means no caller has to `String(...)` it
70
+ * defensively and get a `'undefined'` that quietly fails the test for the
71
+ * wrong reason.
72
+ */ export function isTenantSubscriptionLive(status) {
73
+ return typeof status === 'string' && TENANT_LIVE_SUBSCRIPTION_STATUSES.includes(status);
74
+ }
75
+
76
+ //# sourceMappingURL=commerce-subscription-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-subscription-status.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\n/**\n * The TENANT-side live-subscription list — one copy, for the whole plugin\n * (AGL-1849).\n *\n * ## Why this is not `isLiveSubscriptionStatus`\n *\n * AGL-1715-EXEMPT: this is a decision about the TENANT's own site members'\n * subscriptions to the TENANT's products (`hosts/{hostId}/subscriptions`) — a\n * different Stripe account, a different buyer and a different question from\n * `isLiveSubscriptionStatus` in `@aglyn/aglyn`, which asks whether an Aglyn org\n * is paying US. The words coincide only because both are Stripe subscription\n * statuses. Converging them would tie a shopper's access to a tenant's store to\n * Aglyn's own billing rules, so the two stay apart deliberately.\n *\n * ## Why it is its own file\n *\n * It used to be two private `LIVE_STATUSES` sets, in `server/gate.ts` and\n * `server/member-post.ts`, each carrying its own copy of the marker and its own\n * allowlist entry. Two copies of a list that decides who has already paid is\n * the shape AGL-1715 exists to prevent, and the third caller — the\n * duplicate-subscription guard in `server/checkout.ts` — would have made it\n * three. Hoisting it to the model gives the tenant side the same single source\n * the org side has, rather than a fourth transcription of the same three words.\n *\n * ## Why `past_due` counts as live\n *\n * A subscription in dunning is still a subscription: Stripe is retrying the\n * card, the member has not cancelled, and access has not lapsed. Dropping it\n * would revoke a paying member's content the moment a card expired, and — for\n * the checkout guard — would invite them to buy a SECOND subscription to a\n * product they are already being billed for, which is precisely the duplicate\n * this list is used to refuse.\n *\n * A cancelled or `incomplete_expired` subscription is NOT live, and that is\n * equally load-bearing: a member who cancelled must be able to re-subscribe.\n * A guard built on \"has a subscription row\" instead of \"has a LIVE one\" would\n * lock them out forever (the AGL-1715 lesson, restated on the tenant side).\n */\n// AGL-1715-EXEMPT: the tenant side's own list, for the reasons set out above —\n// a tenant's site member subscribing to a tenant's product, on the tenant's\n// Stripe account. Not the Aglyn org's subscription to us, so not\n// `isLiveSubscriptionStatus`. (Restated here because the guard wants the marker\n// within 25 lines of the literal, and the reasoning above is longer than that.)\nexport const TENANT_LIVE_SUBSCRIPTION_STATUSES = [\n 'active',\n 'trialing',\n 'past_due',\n] as const\n\nexport type TenantLiveSubscriptionStatus =\n (typeof TENANT_LIVE_SUBSCRIPTION_STATUSES)[number]\n\n/**\n * Is this tenant-side subscription status one that still grants access / still\n * counts as an existing subscription?\n *\n * Takes `unknown` on purpose: every caller reads it straight off a Firestore\n * snapshot, where the field may be absent, a number, or anything else a bad\n * write left behind. Narrowing here means no caller has to `String(...)` it\n * defensively and get a `'undefined'` that quietly fails the test for the\n * wrong reason.\n */\nexport function isTenantSubscriptionLive(status: unknown): boolean {\n return (\n typeof status === 'string' &&\n (TENANT_LIVE_SUBSCRIPTION_STATUSES as readonly string[]).includes(status)\n )\n}\n"],"names":["TENANT_LIVE_SUBSCRIPTION_STATUSES","isTenantSubscriptionLive","status","includes"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GACD,+EAA+E;AAC/E,4EAA4E;AAC5E,iEAAiE;AACjE,gFAAgF;AAChF,gFAAgF;AAChF,OAAO,MAAMA,oCAAoC;IAC/C;IACA;IACA;CACD,CAAS;AAKV;;;;;;;;;CASC,GACD,OAAO,SAASC,yBAAyBC,MAAe;IACtD,OACE,OAAOA,WAAW,YAClB,AAACF,kCAAwDG,QAAQ,CAACD;AAEtE"}