@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,245 @@
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
+ * What a storefront sale's TAX decomposes to (AGL-1904) — the record that
18
+ * makes tax collected on a merchant's sale visible to Aglyn's own return.
19
+ *
20
+ * ## The observation this module exists to record
21
+ *
22
+ * Every storefront checkout — cart, buy-now, subscription — creates its
23
+ * Checkout Session on the PLATFORM account: platform secret key, no
24
+ * `Stripe-Account` header, no `on_behalf_of`, with
25
+ * `payment_intent_data[transfer_data][destination]` pointing at the merchant's
26
+ * connected account. `cart-checkout.ts` and `checkout.ts` then set
27
+ * `automatic_tax[enabled]` when the store's tax mode is `stripe`.
28
+ *
29
+ * That was long suspected to mean Stripe Tax computes the shopper's tax
30
+ * against AGLYN's registrations rather than the merchant's. It was MEASURED on
31
+ * 2026-08-18, in test mode, as a two-arm experiment on exactly that session
32
+ * shape (a $100 line, a Texas shopper address, `transfer_data[destination]`
33
+ * set):
34
+ *
35
+ * - **Platform unregistered, destination connected account registered in
36
+ * TX** → `amount_tax: 0`, one breakdown line with
37
+ * `taxability_reason: "not_collecting"`, `taxable_amount: 0`.
38
+ * - **Platform registered in TX, everything else byte-identical** →
39
+ * `amount_tax: 825`, `taxability_reason: "standard_rated"`,
40
+ * `taxable_amount: 10000`, rate 8.25% "Texas".
41
+ *
42
+ * Both sessions reported `automatic_tax.liability: { type: "self" }` — Stripe's
43
+ * own word for "the account this session was created on is the one liable".
44
+ * `transfer_data[destination]` had no effect on tax in either arm.
45
+ *
46
+ * So on a `mode: 'stripe'` store the tax charged to the shopper is computed on
47
+ * Aglyn's registration footprint, lands in Aglyn's platform balance, and is
48
+ * Aglyn's to remit. The live platform account has held an ACTIVE Texas state
49
+ * sales tax registration since 2026-08-17, so this is not hypothetical.
50
+ *
51
+ * **This module describes that mechanism and records the money.** Aglyn IS a
52
+ * marketplace facilitator for storefront sales, and the published Terms say so
53
+ * at §10.7 (AGL-1956). Facilitator status still attaches by operation of
54
+ * law rather than by our say-so, and §10.7 is deliberately narrow — Aglyn
55
+ * collects and remits **where applicable law gives it an obligation**, not
56
+ * universally — so this module still records the mechanism per sale and does
57
+ * not stamp a per-merchant or per-jurisdiction verdict on any row.
58
+ *
59
+ * ## The two store modes are DIFFERENT FACTS and must never be summed
60
+ *
61
+ * `taxMode` is the whole point of this record, and it is derived from
62
+ * `automatic_tax.enabled` — **never** from whether tax lines are present:
63
+ *
64
+ * - **`stripe-automatic`** — Stripe Tax computed it, against whichever
65
+ * account `automatic_tax.liability` names. When that is `platform`, the
66
+ * money is held under Aglyn's registration.
67
+ * - **`manual`** — the merchant's own configured rate
68
+ * (`checkout.ts` / `pos-order.ts`), applied to the merchant's own declared
69
+ * origin. Aglyn's registrations never enter into it and the amount is not
70
+ * Aglyn's to remit.
71
+ *
72
+ * The trap that makes the `automatic_tax.enabled` discriminator load-bearing:
73
+ * a MANUAL-mode subscription renewal carries a real Stripe Tax Rate (AGL-1751
74
+ * attaches one so the tax recurs), so its invoice arrives with a populated
75
+ * `total_taxes[]` — indistinguishable from a Stripe Tax invoice if you look at
76
+ * the tax lines. Summing tax lines would silently book merchant-configured tax
77
+ * as Aglyn-collected. **Read the flag, not the lines.**
78
+ *
79
+ * ## The taxable base is kept, never inferred
80
+ *
81
+ * `taxable_amount` is what a return reports as "taxable sales". Deriving it as
82
+ * `amount ÷ rate` needs a live read to resolve the rate id and rounds wrong at
83
+ * the edges, so it is stored when Stripe states it and left `null` when it
84
+ * does not — and a row whose base cannot be read is COUNTED OUT LOUD by the
85
+ * return rather than treated as a zero-base sale.
86
+ *
87
+ * A Checkout Session's `total_details.breakdown` is not in the webhook payload
88
+ * and must be re-read with `expand[]=total_details.breakdown`; an invoice
89
+ * carries `total_taxes[]` inline. Both land in the same `taxLines` shape.
90
+ *
91
+ * Pure and total: unreadable input answers `null`, never throws — a recording
92
+ * failure must never 500 a webhook into redelivery.
93
+ */ import { storefrontTaxMode } from "../model/commerce-tax-decision.js";
94
+ function asCents(value) {
95
+ const cents = Number(value != null ? value : 0);
96
+ return Number.isFinite(cents) ? Math.round(cents) : 0;
97
+ }
98
+ function asStringOrNull(value) {
99
+ return typeof value === 'string' && value ? value : null;
100
+ }
101
+ function asNumberOrNull(value) {
102
+ const parsed = Number(value != null ? value : NaN);
103
+ return Number.isFinite(parsed) ? parsed : null;
104
+ }
105
+ /** Seconds-since-epoch to Date, or null for anything unreadable. */ function asDate(value) {
106
+ const seconds = Number(value != null ? value : NaN);
107
+ return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * 1000) : null;
108
+ }
109
+ function address(source) {
110
+ if (!source || typeof source !== 'object') return null;
111
+ return {
112
+ country: asStringOrNull(source.country),
113
+ state: asStringOrNull(source.state),
114
+ city: asStringOrNull(source.city),
115
+ postalCode: asStringOrNull(source.postal_code)
116
+ };
117
+ }
118
+ /**
119
+ * The breakdown entries, from either shape.
120
+ *
121
+ * A Checkout Session expands them under `total_details.breakdown.taxes[]`
122
+ * with the rate object inline; an invoice carries `total_taxes[]` (rate under
123
+ * `tax_rate_details.tax_rate`) or the older `total_tax_amounts[]` (`tax_rate`,
124
+ * an id or an expanded object). All three map onto one stored line.
125
+ */ function taxLines(source) {
126
+ var _source_total_details_breakdown, _source_total_details;
127
+ const entries = Array.isArray(source == null ? void 0 : (_source_total_details = source.total_details) == null ? void 0 : (_source_total_details_breakdown = _source_total_details.breakdown) == null ? void 0 : _source_total_details_breakdown.taxes) ? source.total_details.breakdown.taxes : Array.isArray(source == null ? void 0 : source.total_taxes) ? source.total_taxes : Array.isArray(source == null ? void 0 : source.total_tax_amounts) ? source.total_tax_amounts : [];
128
+ return entries.map((entry)=>{
129
+ var _ref, _ref1, _ref2, _asStringOrNull, _asNumberOrNull;
130
+ var _entry_tax_rate_details;
131
+ const rate = (_ref = (_ref1 = entry == null ? void 0 : entry.rate) != null ? _ref1 : entry == null ? void 0 : (_entry_tax_rate_details = entry.tax_rate_details) == null ? void 0 : _entry_tax_rate_details.tax_rate) != null ? _ref : entry == null ? void 0 : entry.tax_rate;
132
+ const rateObject = rate && typeof rate === 'object' ? rate : null;
133
+ // Distinguish an absent `taxable_amount` from a genuine zero: a zero base
134
+ // is a real fact (`not_collecting`), an absent one means the base is
135
+ // UNKNOWN, and the return has to be able to tell those apart.
136
+ const taxableAmount = Number((_ref2 = entry == null ? void 0 : entry.taxable_amount) != null ? _ref2 : NaN);
137
+ return {
138
+ amountCents: asCents(entry == null ? void 0 : entry.amount),
139
+ taxabilityReason: asStringOrNull(entry == null ? void 0 : entry.taxability_reason),
140
+ taxRateId: (_asStringOrNull = asStringOrNull(rate)) != null ? _asStringOrNull : asStringOrNull(rateObject == null ? void 0 : rateObject.id),
141
+ taxableAmountCents: Number.isFinite(taxableAmount) ? Math.round(taxableAmount) : null,
142
+ jurisdiction: asStringOrNull(rateObject == null ? void 0 : rateObject.jurisdiction),
143
+ rateState: asStringOrNull(rateObject == null ? void 0 : rateObject.state),
144
+ percentage: (_asNumberOrNull = asNumberOrNull(rateObject == null ? void 0 : rateObject.effective_percentage)) != null ? _asNumberOrNull : asNumberOrNull(rateObject == null ? void 0 : rateObject.percentage)
145
+ };
146
+ });
147
+ }
148
+ /** Sum of the stored lines' amounts. */ function sumLines(lines) {
149
+ return lines.reduce((sum, line)=>sum + line.amountCents, 0);
150
+ }
151
+ /**
152
+ * Reads the tax off one paid session or invoice — the SINGLE derivation, and
153
+ * the reason it is its own exported function (AGL-2451).
154
+ *
155
+ * `storefrontTaxRow` builds the `storefrontTaxCollected` record from it, and
156
+ * the four order-writing doors in `billing-webhook.ts` stamp `HostOrder.taxMode`
157
+ * from it, so an order and the row filed for the same Stripe id cannot state
158
+ * different regimes. Re-deriving the mode beside a second reading of the same
159
+ * object is exactly how the two would drift.
160
+ *
161
+ * Pure and total: unreadable input answers `taxMode: 'none'`, never throws.
162
+ */ export function resolveStorefrontTax(object, manualTaxCents = 0) {
163
+ var _ref, _ref1;
164
+ var _source_automatic_tax, _source_total_details, _source_total_details1;
165
+ const source = object;
166
+ const automatic = (source == null ? void 0 : (_source_automatic_tax = source.automatic_tax) == null ? void 0 : _source_automatic_tax.enabled) === true;
167
+ const lines = automatic ? taxLines(source) : [];
168
+ const manualCents = Math.max(0, asCents(manualTaxCents));
169
+ // The flag, never the lines — see the module note. A manual-mode
170
+ // subscription renewal carries real Stripe tax rates in `total_taxes[]`,
171
+ // and booking those as Aglyn-collected would be the whole defect restated.
172
+ //
173
+ // Read on SESSIONS as well as invoices since AGL-1953: the cart and
174
+ // draft-order paths now carry their manual tax as a real Stripe Tax Rate on
175
+ // the line (the construction that survives a session-level coupon), so a
176
+ // manual session reports a genuine `total_details.amount_tax`. The scalar is
177
+ // the fallback because `total_details.breakdown` is expandable and absent
178
+ // unless the recorder re-read it. Buy-now's `line_items[1]` construction
179
+ // reports 0 in both shapes and still answers from `manualTaxCents` below.
180
+ const manualLineCents = !automatic ? sumLines(taxLines(source)) || asCents((_ref = source == null ? void 0 : (_source_total_details = source.total_details) == null ? void 0 : _source_total_details.amount_tax) != null ? _ref : source == null ? void 0 : source.tax) : 0;
181
+ const taxCents = automatic ? // invoice. Prefer the summed lines when they exist (a computed invoice
182
+ // can send a 0/null scalar beside a populated array — AGL-1811's
183
+ // measurement), and fall back to the scalar shapes otherwise.
184
+ lines.length > 0 ? sumLines(lines) : asCents((_ref1 = source == null ? void 0 : (_source_total_details1 = source.total_details) == null ? void 0 : _source_total_details1.amount_tax) != null ? _ref1 : source == null ? void 0 : source.tax) : Math.max(manualCents, manualLineCents);
185
+ return {
186
+ automatic,
187
+ lines,
188
+ taxCents,
189
+ taxMode: storefrontTaxMode({
190
+ automatic,
191
+ taxCents
192
+ })
193
+ };
194
+ }
195
+ /**
196
+ * Just the regime, for the order-writing doors (AGL-2451). Delegates, so this
197
+ * is the same answer the filed tax record gives for the same object.
198
+ */ export function storefrontTaxModeOf(object, manualTaxCents = 0) {
199
+ return resolveStorefrontTax(object, manualTaxCents).taxMode;
200
+ }
201
+ /**
202
+ * Decomposes a paid storefront session or invoice into the stored row, or
203
+ * `null` when there is no host to attribute it to.
204
+ *
205
+ * The caller decides WHICH objects reach this — the commerce webhook
206
+ * self-selects on `metadata.type`, exactly as every other section does.
207
+ */ export function storefrontTaxRow(object, context) {
208
+ var _ref, _ref1, _ref2, _asStringOrNull, _asStringOrNull1, _asStringOrNull2, _address, _asDate;
209
+ var _source_automatic_tax_liability, _source_automatic_tax, _source_payment_intent_data_transfer_data, _source_payment_intent_data, _source_transfer_data, _source_metadata, _source_subscription_details_metadata, _source_subscription_details, _source_parent_subscription_details_metadata, _source_parent_subscription_details, _source_parent, _source_customer_details, _source_status_transitions;
210
+ const source = object;
211
+ const hostId = String((_ref = context == null ? void 0 : context.hostId) != null ? _ref : '');
212
+ if (!hostId) return null;
213
+ const { automatic, lines, taxCents, taxMode } = resolveStorefrontTax(source, context == null ? void 0 : context.manualTaxCents);
214
+ const liabilityType = asStringOrNull(source == null ? void 0 : (_source_automatic_tax = source.automatic_tax) == null ? void 0 : (_source_automatic_tax_liability = _source_automatic_tax.liability) == null ? void 0 : _source_automatic_tax_liability.type);
215
+ const taxLiability = !automatic ? null : liabilityType === 'account' ? 'connected-account' : // older API versions, where the only account a session could be
216
+ // liable under is the one it was created on — the platform.
217
+ 'platform';
218
+ const grossCents = asCents(context.kind === 'invoice' ? source == null ? void 0 : source.amount_paid : source == null ? void 0 : source.amount_total);
219
+ const transferDestination = (_ref1 = source == null ? void 0 : (_source_payment_intent_data = source.payment_intent_data) == null ? void 0 : (_source_payment_intent_data_transfer_data = _source_payment_intent_data.transfer_data) == null ? void 0 : _source_payment_intent_data_transfer_data.destination) != null ? _ref1 : source == null ? void 0 : (_source_transfer_data = source.transfer_data) == null ? void 0 : _source_transfer_data.destination;
220
+ return {
221
+ kind: context.kind,
222
+ hostId,
223
+ metadataType: (_ref2 = (_asStringOrNull = asStringOrNull(source == null ? void 0 : (_source_metadata = source.metadata) == null ? void 0 : _source_metadata.type)) != null ? _asStringOrNull : asStringOrNull(source == null ? void 0 : (_source_subscription_details = source.subscription_details) == null ? void 0 : (_source_subscription_details_metadata = _source_subscription_details.metadata) == null ? void 0 : _source_subscription_details_metadata.type)) != null ? _ref2 : asStringOrNull(source == null ? void 0 : (_source_parent = source.parent) == null ? void 0 : (_source_parent_subscription_details = _source_parent.subscription_details) == null ? void 0 : (_source_parent_subscription_details_metadata = _source_parent_subscription_details.metadata) == null ? void 0 : _source_parent_subscription_details_metadata.type),
224
+ taxMode,
225
+ taxLiability,
226
+ connectedAccountId: (_asStringOrNull1 = asStringOrNull(transferDestination)) != null ? _asStringOrNull1 : asStringOrNull(transferDestination == null ? void 0 : transferDestination.id),
227
+ grossCents,
228
+ taxCents,
229
+ netCents: grossCents - taxCents,
230
+ currency: (_asStringOrNull2 = asStringOrNull(source == null ? void 0 : source.currency)) != null ? _asStringOrNull2 : 'usd',
231
+ customerAddress: (_address = address(source == null ? void 0 : (_source_customer_details = source.customer_details) == null ? void 0 : _source_customer_details.address)) != null ? _address : address(source == null ? void 0 : source.customer_address),
232
+ // Stripe's OWN breakdown, whenever Stripe stated one — never an invented
233
+ // base. Buy-now's manual construction states none (Stripe was never told
234
+ // the line was tax) and answers `[]`, which is the honest answer. The
235
+ // cart, draft-order and subscription-renewal manual paths carry a real
236
+ // Tax Rate, so Stripe DOES state a `taxable_amount` for them, and keeping
237
+ // it gives `merchantManual` a real taxable-sales figure instead of a
238
+ // permanent zero (AGL-1953). It never reaches the Aglyn-liable bucket:
239
+ // the classification above is the flag, not these lines.
240
+ taxLines: automatic ? lines : taxLines(source),
241
+ paidAt: (_asDate = asDate(source == null ? void 0 : (_source_status_transitions = source.status_transitions) == null ? void 0 : _source_status_transitions.paid_at)) != null ? _asDate : asDate(source == null ? void 0 : source.created)
242
+ };
243
+ }
244
+
245
+ //# sourceMappingURL=storefront-tax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/storefront-tax.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 * What a storefront sale's TAX decomposes to (AGL-1904) — the record that\n * makes tax collected on a merchant's sale visible to Aglyn's own return.\n *\n * ## The observation this module exists to record\n *\n * Every storefront checkout — cart, buy-now, subscription — creates its\n * Checkout Session on the PLATFORM account: platform secret key, no\n * `Stripe-Account` header, no `on_behalf_of`, with\n * `payment_intent_data[transfer_data][destination]` pointing at the merchant's\n * connected account. `cart-checkout.ts` and `checkout.ts` then set\n * `automatic_tax[enabled]` when the store's tax mode is `stripe`.\n *\n * That was long suspected to mean Stripe Tax computes the shopper's tax\n * against AGLYN's registrations rather than the merchant's. It was MEASURED on\n * 2026-08-18, in test mode, as a two-arm experiment on exactly that session\n * shape (a $100 line, a Texas shopper address, `transfer_data[destination]`\n * set):\n *\n * - **Platform unregistered, destination connected account registered in\n * TX** → `amount_tax: 0`, one breakdown line with\n * `taxability_reason: \"not_collecting\"`, `taxable_amount: 0`.\n * - **Platform registered in TX, everything else byte-identical** →\n * `amount_tax: 825`, `taxability_reason: \"standard_rated\"`,\n * `taxable_amount: 10000`, rate 8.25% \"Texas\".\n *\n * Both sessions reported `automatic_tax.liability: { type: \"self\" }` — Stripe's\n * own word for \"the account this session was created on is the one liable\".\n * `transfer_data[destination]` had no effect on tax in either arm.\n *\n * So on a `mode: 'stripe'` store the tax charged to the shopper is computed on\n * Aglyn's registration footprint, lands in Aglyn's platform balance, and is\n * Aglyn's to remit. The live platform account has held an ACTIVE Texas state\n * sales tax registration since 2026-08-17, so this is not hypothetical.\n *\n * **This module describes that mechanism and records the money.** Aglyn IS a\n * marketplace facilitator for storefront sales, and the published Terms say so\n * at §10.7 (AGL-1956). Facilitator status still attaches by operation of\n * law rather than by our say-so, and §10.7 is deliberately narrow — Aglyn\n * collects and remits **where applicable law gives it an obligation**, not\n * universally — so this module still records the mechanism per sale and does\n * not stamp a per-merchant or per-jurisdiction verdict on any row.\n *\n * ## The two store modes are DIFFERENT FACTS and must never be summed\n *\n * `taxMode` is the whole point of this record, and it is derived from\n * `automatic_tax.enabled` — **never** from whether tax lines are present:\n *\n * - **`stripe-automatic`** — Stripe Tax computed it, against whichever\n * account `automatic_tax.liability` names. When that is `platform`, the\n * money is held under Aglyn's registration.\n * - **`manual`** — the merchant's own configured rate\n * (`checkout.ts` / `pos-order.ts`), applied to the merchant's own declared\n * origin. Aglyn's registrations never enter into it and the amount is not\n * Aglyn's to remit.\n *\n * The trap that makes the `automatic_tax.enabled` discriminator load-bearing:\n * a MANUAL-mode subscription renewal carries a real Stripe Tax Rate (AGL-1751\n * attaches one so the tax recurs), so its invoice arrives with a populated\n * `total_taxes[]` — indistinguishable from a Stripe Tax invoice if you look at\n * the tax lines. Summing tax lines would silently book merchant-configured tax\n * as Aglyn-collected. **Read the flag, not the lines.**\n *\n * ## The taxable base is kept, never inferred\n *\n * `taxable_amount` is what a return reports as \"taxable sales\". Deriving it as\n * `amount ÷ rate` needs a live read to resolve the rate id and rounds wrong at\n * the edges, so it is stored when Stripe states it and left `null` when it\n * does not — and a row whose base cannot be read is COUNTED OUT LOUD by the\n * return rather than treated as a zero-base sale.\n *\n * A Checkout Session's `total_details.breakdown` is not in the webhook payload\n * and must be re-read with `expand[]=total_details.breakdown`; an invoice\n * carries `total_taxes[]` inline. Both land in the same `taxLines` shape.\n *\n * Pure and total: unreadable input answers `null`, never throws — a recording\n * failure must never 500 a webhook into redelivery.\n */\n\nimport {\n storefrontTaxMode,\n type StorefrontTaxMode,\n} from '../model/commerce-tax-decision'\n\n/**\n * Which machinery produced the tax on this sale. See the module note.\n *\n * The union and its derivation live in the MODEL since AGL-2451, because the\n * merchant's order document now carries the same fact (`HostOrder.taxMode`)\n * and two copies of a rule that must agree are one copy too many. Re-exported\n * here so this module stays the place the mechanism is documented.\n */\nexport type { StorefrontTaxMode }\n\n/**\n * Whose registrations Stripe Tax computed against, from\n * `automatic_tax.liability.type`: `self` (the account the object lives on,\n * i.e. Aglyn's platform) or `account` (a named connected account).\n * Null whenever `taxMode` is not `stripe-automatic`.\n */\nexport type StorefrontTaxLiability = 'platform' | 'connected-account'\n\n/** One entry of the stored tax breakdown. */\nexport interface StorefrontTaxLine {\n amountCents: number\n /** Stripe's stated reason, e.g. `standard_rated`, `not_collecting`. */\n taxabilityReason: string | null\n taxRateId: string | null\n /** Stripe's `taxable_amount`; null when the shape did not carry it. */\n taxableAmountCents: number | null\n /** e.g. `Texas` — from the expanded rate, for the working papers. */\n jurisdiction: string | null\n /** The rate's own state code, which is not the shopper's address. */\n rateState: string | null\n percentage: number | null\n}\n\n/** The per-transaction record `storefrontTaxCollected/{stripeId}` stores. */\nexport interface StorefrontTaxRow {\n /** `session` for one-time sales, `invoice` for subscription cycles. */\n kind: 'session' | 'invoice'\n hostId: string\n /** The commerce path — `commerce-cart`, `commerce-order`, … */\n metadataType: string | null\n taxMode: StorefrontTaxMode\n taxLiability: StorefrontTaxLiability | null\n /** The merchant's connected account, when the object names one. */\n connectedAccountId: string | null\n /** What the shopper actually paid, tax included. */\n grossCents: number\n taxCents: number\n /** Gross minus tax. NOT Aglyn's revenue — most of it is the merchant's. */\n netCents: number\n currency: string\n /** The jurisdiction input — the shopper's address on the Stripe object. */\n customerAddress: {\n country: string | null\n state: string | null\n city: string | null\n postalCode: string | null\n } | null\n taxLines: StorefrontTaxLine[]\n paidAt: Date | null\n}\n\nfunction asCents(value: unknown): number {\n const cents = Number(value ?? 0)\n return Number.isFinite(cents) ? Math.round(cents) : 0\n}\n\nfunction asStringOrNull(value: unknown): string | null {\n return typeof value === 'string' && value ? value : null\n}\n\nfunction asNumberOrNull(value: unknown): number | null {\n const parsed = Number(value ?? NaN)\n return Number.isFinite(parsed) ? parsed : null\n}\n\n/** Seconds-since-epoch to Date, or null for anything unreadable. */\nfunction asDate(value: unknown): Date | null {\n const seconds = Number(value ?? NaN)\n return Number.isFinite(seconds) && seconds > 0\n ? new Date(seconds * 1000)\n : null\n}\n\nfunction address(source: any): StorefrontTaxRow['customerAddress'] {\n if (!source || typeof source !== 'object') return null\n return {\n country: asStringOrNull(source.country),\n state: asStringOrNull(source.state),\n city: asStringOrNull(source.city),\n postalCode: asStringOrNull(source.postal_code),\n }\n}\n\n/**\n * The breakdown entries, from either shape.\n *\n * A Checkout Session expands them under `total_details.breakdown.taxes[]`\n * with the rate object inline; an invoice carries `total_taxes[]` (rate under\n * `tax_rate_details.tax_rate`) or the older `total_tax_amounts[]` (`tax_rate`,\n * an id or an expanded object). All three map onto one stored line.\n */\nfunction taxLines(source: any): StorefrontTaxLine[] {\n const entries: any[] = Array.isArray(source?.total_details?.breakdown?.taxes)\n ? source.total_details.breakdown.taxes\n : Array.isArray(source?.total_taxes)\n ? source.total_taxes\n : Array.isArray(source?.total_tax_amounts)\n ? source.total_tax_amounts\n : []\n return entries.map((entry: any) => {\n const rate = entry?.rate ?? entry?.tax_rate_details?.tax_rate ?? entry?.tax_rate\n const rateObject = rate && typeof rate === 'object' ? rate : null\n // Distinguish an absent `taxable_amount` from a genuine zero: a zero base\n // is a real fact (`not_collecting`), an absent one means the base is\n // UNKNOWN, and the return has to be able to tell those apart.\n const taxableAmount = Number(entry?.taxable_amount ?? NaN)\n return {\n amountCents: asCents(entry?.amount),\n taxabilityReason: asStringOrNull(entry?.taxability_reason),\n taxRateId: asStringOrNull(rate) ?? asStringOrNull(rateObject?.id),\n taxableAmountCents: Number.isFinite(taxableAmount)\n ? Math.round(taxableAmount)\n : null,\n jurisdiction: asStringOrNull(rateObject?.jurisdiction),\n rateState: asStringOrNull(rateObject?.state),\n percentage:\n asNumberOrNull(rateObject?.effective_percentage) ??\n asNumberOrNull(rateObject?.percentage),\n }\n })\n}\n\n/** Sum of the stored lines' amounts. */\nfunction sumLines(lines: readonly StorefrontTaxLine[]): number {\n return lines.reduce((sum, line) => sum + line.amountCents, 0)\n}\n\nexport interface StorefrontTaxContext {\n kind: 'session' | 'invoice'\n hostId: string\n /**\n * The manual-mode tax `checkout.ts` puts over as an ordinary line item.\n * Stripe is never told it is tax, so no Stripe field carries it and the\n * session's own `metadata[taxCents]` is the only witness (AGL-1711).\n */\n manualTaxCents?: number\n}\n\n/** What one Stripe object says about the tax it carried. */\nexport interface ResolvedStorefrontTax {\n /** `automatic_tax.enabled` — the discriminator, never the tax lines. */\n automatic: boolean\n /** Stripe's own breakdown, present only on a `stripe-automatic` sale. */\n lines: StorefrontTaxLine[]\n taxCents: number\n taxMode: StorefrontTaxMode\n}\n\n/**\n * Reads the tax off one paid session or invoice — the SINGLE derivation, and\n * the reason it is its own exported function (AGL-2451).\n *\n * `storefrontTaxRow` builds the `storefrontTaxCollected` record from it, and\n * the four order-writing doors in `billing-webhook.ts` stamp `HostOrder.taxMode`\n * from it, so an order and the row filed for the same Stripe id cannot state\n * different regimes. Re-deriving the mode beside a second reading of the same\n * object is exactly how the two would drift.\n *\n * Pure and total: unreadable input answers `taxMode: 'none'`, never throws.\n */\nexport function resolveStorefrontTax(\n object: unknown,\n manualTaxCents = 0,\n): ResolvedStorefrontTax {\n const source = object as any\n const automatic = source?.automatic_tax?.enabled === true\n const lines = automatic ? taxLines(source) : []\n const manualCents = Math.max(0, asCents(manualTaxCents))\n // The flag, never the lines — see the module note. A manual-mode\n // subscription renewal carries real Stripe tax rates in `total_taxes[]`,\n // and booking those as Aglyn-collected would be the whole defect restated.\n //\n // Read on SESSIONS as well as invoices since AGL-1953: the cart and\n // draft-order paths now carry their manual tax as a real Stripe Tax Rate on\n // the line (the construction that survives a session-level coupon), so a\n // manual session reports a genuine `total_details.amount_tax`. The scalar is\n // the fallback because `total_details.breakdown` is expandable and absent\n // unless the recorder re-read it. Buy-now's `line_items[1]` construction\n // reports 0 in both shapes and still answers from `manualTaxCents` below.\n const manualLineCents = !automatic\n ? sumLines(taxLines(source)) ||\n asCents(source?.total_details?.amount_tax ?? source?.tax)\n : 0\n const taxCents = automatic\n ? // `total_details.amount_tax` on a session, `total_taxes[]` on an\n // invoice. Prefer the summed lines when they exist (a computed invoice\n // can send a 0/null scalar beside a populated array — AGL-1811's\n // measurement), and fall back to the scalar shapes otherwise.\n lines.length > 0\n ? sumLines(lines)\n : asCents(source?.total_details?.amount_tax ?? source?.tax)\n : Math.max(manualCents, manualLineCents)\n return {\n automatic,\n lines,\n taxCents,\n taxMode: storefrontTaxMode({ automatic, taxCents }),\n }\n}\n\n/**\n * Just the regime, for the order-writing doors (AGL-2451). Delegates, so this\n * is the same answer the filed tax record gives for the same object.\n */\nexport function storefrontTaxModeOf(\n object: unknown,\n manualTaxCents = 0,\n): StorefrontTaxMode {\n return resolveStorefrontTax(object, manualTaxCents).taxMode\n}\n\n/**\n * Decomposes a paid storefront session or invoice into the stored row, or\n * `null` when there is no host to attribute it to.\n *\n * The caller decides WHICH objects reach this — the commerce webhook\n * self-selects on `metadata.type`, exactly as every other section does.\n */\nexport function storefrontTaxRow(\n object: unknown,\n context: StorefrontTaxContext,\n): StorefrontTaxRow | null {\n const source = object as any\n const hostId = String(context?.hostId ?? '')\n if (!hostId) return null\n\n const { automatic, lines, taxCents, taxMode } = resolveStorefrontTax(\n source,\n context?.manualTaxCents,\n )\n\n const liabilityType = asStringOrNull(source?.automatic_tax?.liability?.type)\n const taxLiability: StorefrontTaxLiability | null = !automatic\n ? null\n : liabilityType === 'account'\n ? 'connected-account'\n : // `self`, and also an absent liability: Stripe omits the field on\n // older API versions, where the only account a session could be\n // liable under is the one it was created on — the platform.\n 'platform'\n\n const grossCents = asCents(\n context.kind === 'invoice' ? source?.amount_paid : source?.amount_total,\n )\n const transferDestination =\n source?.payment_intent_data?.transfer_data?.destination ??\n source?.transfer_data?.destination\n return {\n kind: context.kind,\n hostId,\n metadataType:\n asStringOrNull(source?.metadata?.type) ??\n asStringOrNull(source?.subscription_details?.metadata?.type) ??\n asStringOrNull(source?.parent?.subscription_details?.metadata?.type),\n taxMode,\n taxLiability,\n connectedAccountId:\n asStringOrNull(transferDestination) ??\n asStringOrNull((transferDestination as any)?.id),\n grossCents,\n taxCents,\n netCents: grossCents - taxCents,\n currency: asStringOrNull(source?.currency) ?? 'usd',\n customerAddress:\n address(source?.customer_details?.address) ??\n address(source?.customer_address),\n // Stripe's OWN breakdown, whenever Stripe stated one — never an invented\n // base. Buy-now's manual construction states none (Stripe was never told\n // the line was tax) and answers `[]`, which is the honest answer. The\n // cart, draft-order and subscription-renewal manual paths carry a real\n // Tax Rate, so Stripe DOES state a `taxable_amount` for them, and keeping\n // it gives `merchantManual` a real taxable-sales figure instead of a\n // permanent zero (AGL-1953). It never reaches the Aglyn-liable bucket:\n // the classification above is the flag, not these lines.\n taxLines: automatic ? lines : taxLines(source),\n paidAt:\n asDate(source?.status_transitions?.paid_at) ?? asDate(source?.created),\n }\n}\n"],"names":["storefrontTaxMode","asCents","value","cents","Number","isFinite","Math","round","asStringOrNull","asNumberOrNull","parsed","NaN","asDate","seconds","Date","address","source","country","state","city","postalCode","postal_code","taxLines","entries","Array","isArray","total_details","breakdown","taxes","total_taxes","total_tax_amounts","map","entry","rate","tax_rate_details","tax_rate","rateObject","taxableAmount","taxable_amount","amountCents","amount","taxabilityReason","taxability_reason","taxRateId","id","taxableAmountCents","jurisdiction","rateState","percentage","effective_percentage","sumLines","lines","reduce","sum","line","resolveStorefrontTax","object","manualTaxCents","automatic","automatic_tax","enabled","manualCents","max","manualLineCents","amount_tax","tax","taxCents","length","taxMode","storefrontTaxModeOf","storefrontTaxRow","context","hostId","String","liabilityType","liability","type","taxLiability","grossCents","kind","amount_paid","amount_total","transferDestination","payment_intent_data","transfer_data","destination","metadataType","metadata","subscription_details","parent","connectedAccountId","netCents","currency","customerAddress","customer_details","customer_address","paidAt","status_transitions","paid_at","created"],"mappings":"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"}
@@ -0,0 +1,25 @@
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
+ * Gated video streaming (AGL-315). POST (member session) checks the
20
+ * entitlement and mints a short-TTL signed URL; GET with a valid signature
21
+ * redirects to a media URL that itself expires (AGL-2814). The mint step is
22
+ * the server-enforced gate, a shared stream link dies within 15 minutes, and
23
+ * the media URL behind it dies with the viewing session.
24
+ */
25
+ export declare const streamHandler: PluginApiHandler;
@@ -0,0 +1,191 @@
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 { MEDIA_CDN_RENDITION_AUTO, MEDIA_CDN_RENDITION_PARAM } from "@aglyn/aglyn/server";
17
+ import * as CommerceModel from "../model/index.js";
18
+ import { firebaseAdmin, GATED_VIDEO_SESSION_TTL_MS } from "@aglyn/tenant-data-admin";
19
+ import { createPaidMediaDeliveryIo, resolvePaidMediaDelivery } from "./paid-media-delivery.js";
20
+ import { createHmac, timingSafeEqual } from "crypto";
21
+ import { requireActiveMember } from "./membership.js";
22
+ import { checkMemberEntitlement } from "./gate.js";
23
+ import { tokenSigningSecret } from "./download.js";
24
+ const TTL_MS = 15 * 60 * 1000;
25
+ /**
26
+ * Signed with the dedicated, fail-closed `TOKEN_SIGNING_SECRET` (AGL-509).
27
+ * AGL-509 converted the download and supplier tokens but missed this call
28
+ * site (AGL-689): the old `STRIPE_SECRET_KEY ?? 'aglyn'` key left the whole
29
+ * payload — `stream:${hostId}:${productId}:${video}:${exp}`, all public
30
+ * identifiers — forgeable on any deploy without the Stripe key, and the
31
+ * short TTL bought nothing because the forger chooses `exp`.
32
+ */ function sign(hostId, productId, video, exp) {
33
+ return createHmac('sha256', tokenSigningSecret()).update(`stream:${hostId}:${productId}:${video}:${exp}`).digest('hex').slice(0, 32);
34
+ }
35
+ /**
36
+ * Constant-time signature compare (AGL-512, closed here by AGL-1881).
37
+ *
38
+ * The presented signature was compared with `!==`, which short-circuits on
39
+ * the first differing byte — the exact defect AGL-512 closed in `download.ts`
40
+ * and `supplier-update.ts` and missed here, one directory over. It matters
41
+ * more on this route than it reads: the signature is a truncated HMAC over
42
+ * values the caller already knows (`hostId`, `productId`, `video`, `exp`), so
43
+ * a byte-at-a-time oracle recovers a token for a video the caller was never
44
+ * entitled to, and the 15-minute TTL does not bound the attempt rate.
45
+ *
46
+ * Length is checked first because `timingSafeEqual` THROWS on a length
47
+ * mismatch rather than returning false, and the expected length here is a
48
+ * public constant (32 hex chars) — leaking it costs nothing.
49
+ */ function signatureMatches(presented, expected) {
50
+ const a = Buffer.from(String(presented != null ? presented : ''), 'utf8');
51
+ const b = Buffer.from(String(expected != null ? expected : ''), 'utf8');
52
+ if (a.length !== b.length) return false;
53
+ return timingSafeEqual(new Uint8Array(a), new Uint8Array(b));
54
+ }
55
+ /**
56
+ * Gated video streaming (AGL-315). POST (member session) checks the
57
+ * entitlement and mints a short-TTL signed URL; GET with a valid signature
58
+ * redirects to a media URL that itself expires (AGL-2814). The mint step is
59
+ * the server-enforced gate, a shared stream link dies within 15 minutes, and
60
+ * the media URL behind it dies with the viewing session.
61
+ */ export const streamHandler = async (req, res)=>{
62
+ var _ref, _ref1, _ref2;
63
+ var _req_body, _req_body1, _req_body2;
64
+ const hostId = String((_ref = req.method === 'POST' ? (_req_body = req.body) == null ? void 0 : _req_body.hostId : req.query.hostId) != null ? _ref : '');
65
+ const productId = String((_ref1 = req.method === 'POST' ? (_req_body1 = req.body) == null ? void 0 : _req_body1.productId : req.query.productId) != null ? _ref1 : '');
66
+ const video = Math.max(0, Number((_ref2 = req.method === 'POST' ? (_req_body2 = req.body) == null ? void 0 : _req_body2.video : req.query.video) != null ? _ref2 : 0));
67
+ if (!hostId || !productId) {
68
+ return res.status(400).json({
69
+ error: 'Missing hostId or productId'
70
+ });
71
+ }
72
+ try {
73
+ var _req_query_exp, _req_query_sig, _productSnapshot_data;
74
+ var _product_gatedVideos, _req_headers;
75
+ if (req.method === 'POST') {
76
+ var _auth_member_get;
77
+ // Suspension gate (AGL-550): suspended members (AGL-546) cannot
78
+ // mint stream URLs — 403'd with the session cookie cleared.
79
+ const auth = await requireActiveMember(req, res, hostId, 'Sign in first');
80
+ if (!auth) return;
81
+ const email = String((_auth_member_get = auth.member.get('email')) != null ? _auth_member_get : '');
82
+ const entitled = email && await checkMemberEntitlement(hostId, email, productId);
83
+ if (!entitled) {
84
+ return res.status(403).json({
85
+ error: 'Not entitled'
86
+ });
87
+ }
88
+ const exp = Date.now() + TTL_MS;
89
+ const url = `/api/commerce/stream?hostId=${encodeURIComponent(hostId)}` + `&productId=${encodeURIComponent(productId)}&video=${video}` + `&exp=${exp}&sig=${sign(hostId, productId, video, exp)}`;
90
+ res.setHeader('Cache-Control', 'private, no-store');
91
+ return res.status(200).json({
92
+ url,
93
+ expiresAtMs: exp
94
+ });
95
+ }
96
+ // GET: signature + expiry gate, then redirect to the media.
97
+ const exp = Number((_req_query_exp = req.query.exp) != null ? _req_query_exp : 0);
98
+ const sig = String((_req_query_sig = req.query.sig) != null ? _req_query_sig : '');
99
+ if (!exp || exp < Date.now() || !signatureMatches(sig, sign(hostId, productId, video, exp))) {
100
+ return res.status(403).send('Link expired — reload the page');
101
+ }
102
+ const app = firebaseAdmin.app();
103
+ const productSnapshot = await app.firestore().collection('hosts').doc(hostId).collection('products').doc(productId).get();
104
+ const product = CommerceModel.liftLegacyProduct((_productSnapshot_data = productSnapshot.data()) != null ? _productSnapshot_data : {});
105
+ const file = (_product_gatedVideos = product.gatedVideos) == null ? void 0 : _product_gatedVideos[video];
106
+ if (!(file == null ? void 0 : file.url)) return res.status(404).send('No video');
107
+ /**
108
+ * The redirect lands on a link that expires (AGL-2814).
109
+ *
110
+ * Everything above only guards the 302. What the 302 points at is what
111
+ * the buyer, and anyone who copies the `Location` out of the buyer's
112
+ * network panel, keeps. So `resolvePaidMediaDelivery` hands back one of
113
+ * three things and never a permanent URL:
114
+ *
115
+ * - a CDN URL signed for {@link GATED_VIDEO_SESSION_TTL_MS}, when the video
116
+ * is a PRIVATE asset in this site's library or its org's. The CDN refuses
117
+ * a private asset on every path without an unexpired signature.
118
+ * - a V4 signed Storage read with the same lifetime, for an object in those
119
+ * libraries that no media document owns. A raw download URL's token lives
120
+ * as long as the object, so it is never the redirect.
121
+ * - an author-typed hotlink, unchanged, because nothing of ours runs there.
122
+ *
123
+ * A video whose asset is still PUBLIC is refused rather than delivered:
124
+ * its URL already works for anyone, so redirecting a buyer to it is the
125
+ * leak. The 409 is what the gated player reports to the viewer, and the
126
+ * log line is what tells the operator which product needs its video made
127
+ * private.
128
+ *
129
+ * `?r=auto` rides inside the signed URL (AGL-2766). The signature covers
130
+ * `(scope, mediaId, exp)` and not the parameter, so it asks the CDN for the
131
+ * best delivery copy of the one asset the signature names and can reach no
132
+ * other. Nothing the caller sends reaches this `Location`: the GET half
133
+ * reads `hostId`, `productId`, `video`, `exp` and `sig`, and builds the
134
+ * target from the product document.
135
+ *
136
+ * With a delivery provider configured and its release flag on for the
137
+ * owning org, a video with a current copy there is redirected straight to
138
+ * the provider's short-lived URL instead (AGL-2824), for the same session
139
+ * and the same rendition the CDN would pick. `Accept` is the one request
140
+ * header that reaches that choice, and only to pick among the asset's own
141
+ * encodings.
142
+ */ const delivery = await resolvePaidMediaDelivery({
143
+ stored: file.url,
144
+ hostId,
145
+ ttlMs: GATED_VIDEO_SESSION_TTL_MS,
146
+ cdnParams: [
147
+ [
148
+ MEDIA_CDN_RENDITION_PARAM,
149
+ MEDIA_CDN_RENDITION_AUTO
150
+ ]
151
+ ],
152
+ delivery: {
153
+ accept: (_req_headers = req.headers) == null ? void 0 : _req_headers['accept']
154
+ },
155
+ io: createPaidMediaDeliveryIo({
156
+ firestore: app.firestore(),
157
+ bucket: app.storage().bucket(process.env['NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET'])
158
+ })
159
+ });
160
+ // Per-member and time-boxed, whichever way it goes: no cache may keep the
161
+ // redirect past the session it opens, nor a refusal past the fix.
162
+ res.setHeader('Cache-Control', 'private, no-store');
163
+ if (delivery.ok === false) {
164
+ console.error('[commerce/stream] gated video not delivered', JSON.stringify({
165
+ hostId,
166
+ productId,
167
+ video,
168
+ refusal: delivery.refusal
169
+ }));
170
+ return delivery.refusal === 'not-private' ? res.status(409).send('This video is not ready to play yet') : res.status(404).send('No video');
171
+ }
172
+ /*
173
+ * `Vary: Accept` only when this response chose the rendition (AGL-2824).
174
+ *
175
+ * A signed CDN URL is the same for every client: the `Location` is a
176
+ * function of the product document and the clock, not of the request's
177
+ * `Accept`, and the negotiation happens on the SECOND response, where
178
+ * `serveMediaCdn` declares `Vary` (AGL-2766). A delivery provider's URL is
179
+ * minted after the rendition is picked, so there the `Accept` shaped this
180
+ * `Location`, and saying so is correct even under `no-store`.
181
+ */ if (delivery.via === 'delivery') res.setHeader('Vary', 'Accept');
182
+ return res.redirect(302, delivery.location);
183
+ } catch (error) {
184
+ console.error(error);
185
+ return res.status(500).json({
186
+ error: 'Stream unavailable'
187
+ });
188
+ }
189
+ };
190
+
191
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/stream.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 {\n MEDIA_CDN_RENDITION_AUTO,\n MEDIA_CDN_RENDITION_PARAM,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport * as CommerceModel from '../model'\nimport {\n firebaseAdmin,\n GATED_VIDEO_SESSION_TTL_MS,\n} from '@aglyn/tenant-data-admin'\nimport {\n createPaidMediaDeliveryIo,\n resolvePaidMediaDelivery,\n} from './paid-media-delivery'\nimport { createHmac, timingSafeEqual } from 'crypto'\nimport { requireActiveMember } from './membership'\nimport { checkMemberEntitlement } from './gate'\nimport { tokenSigningSecret } from './download'\n\nconst TTL_MS = 15 * 60 * 1000\n\n/**\n * Signed with the dedicated, fail-closed `TOKEN_SIGNING_SECRET` (AGL-509).\n * AGL-509 converted the download and supplier tokens but missed this call\n * site (AGL-689): the old `STRIPE_SECRET_KEY ?? 'aglyn'` key left the whole\n * payload — `stream:${hostId}:${productId}:${video}:${exp}`, all public\n * identifiers — forgeable on any deploy without the Stripe key, and the\n * short TTL bought nothing because the forger chooses `exp`.\n */\nfunction sign(hostId: string, productId: string, video: number, exp: number) {\n return createHmac('sha256', tokenSigningSecret())\n .update(`stream:${hostId}:${productId}:${video}:${exp}`)\n .digest('hex')\n .slice(0, 32)\n}\n\n/**\n * Constant-time signature compare (AGL-512, closed here by AGL-1881).\n *\n * The presented signature was compared with `!==`, which short-circuits on\n * the first differing byte — the exact defect AGL-512 closed in `download.ts`\n * and `supplier-update.ts` and missed here, one directory over. It matters\n * more on this route than it reads: the signature is a truncated HMAC over\n * values the caller already knows (`hostId`, `productId`, `video`, `exp`), so\n * a byte-at-a-time oracle recovers a token for a video the caller was never\n * entitled to, and the 15-minute TTL does not bound the attempt rate.\n *\n * Length is checked first because `timingSafeEqual` THROWS on a length\n * mismatch rather than returning false, and the expected length here is a\n * public constant (32 hex chars) — leaking it costs nothing.\n */\nfunction signatureMatches(presented: string, expected: string): boolean {\n const a = Buffer.from(String(presented ?? ''), 'utf8')\n const b = Buffer.from(String(expected ?? ''), 'utf8')\n if (a.length !== b.length) return false\n return timingSafeEqual(new Uint8Array(a), new Uint8Array(b))\n}\n\n/**\n * Gated video streaming (AGL-315). POST (member session) checks the\n * entitlement and mints a short-TTL signed URL; GET with a valid signature\n * redirects to a media URL that itself expires (AGL-2814). The mint step is\n * the server-enforced gate, a shared stream link dies within 15 minutes, and\n * the media URL behind it dies with the viewing session.\n */\nexport const streamHandler: PluginApiHandler = async (req, res) => {\n const hostId = String(\n (req.method === 'POST' ? req.body?.hostId : req.query.hostId) ?? '',\n )\n const productId = String(\n (req.method === 'POST' ? req.body?.productId : req.query.productId) ?? '',\n )\n const video = Math.max(\n 0,\n Number(\n (req.method === 'POST' ? req.body?.video : req.query.video) ?? 0,\n ),\n )\n if (!hostId || !productId) {\n return res.status(400).json({ error: 'Missing hostId or productId' })\n }\n\n try {\n if (req.method === 'POST') {\n // Suspension gate (AGL-550): suspended members (AGL-546) cannot\n // mint stream URLs — 403'd with the session cookie cleared.\n const auth = await requireActiveMember(req, res, hostId, 'Sign in first')\n if (!auth) return\n const email = String(auth.member.get('email') ?? '')\n const entitled =\n email && (await checkMemberEntitlement(hostId, email, productId))\n if (!entitled) {\n return res.status(403).json({ error: 'Not entitled' })\n }\n const exp = Date.now() + TTL_MS\n const url =\n `/api/commerce/stream?hostId=${encodeURIComponent(hostId)}` +\n `&productId=${encodeURIComponent(productId)}&video=${video}` +\n `&exp=${exp}&sig=${sign(hostId, productId, video, exp)}`\n res.setHeader('Cache-Control', 'private, no-store')\n return res.status(200).json({ url, expiresAtMs: exp })\n }\n\n // GET: signature + expiry gate, then redirect to the media.\n const exp = Number(req.query.exp ?? 0)\n const sig = String(req.query.sig ?? '')\n if (\n !exp ||\n exp < Date.now() ||\n !signatureMatches(sig, sign(hostId, productId, video, exp))\n ) {\n return res.status(403).send('Link expired — reload the page')\n }\n const app = firebaseAdmin.app()\n const productSnapshot = await app\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('products')\n .doc(productId)\n .get()\n const product = CommerceModel.liftLegacyProduct(\n (productSnapshot.data() as any) ?? {},\n )\n const file = product.gatedVideos?.[video]\n if (!file?.url) return res.status(404).send('No video')\n /**\n * The redirect lands on a link that expires (AGL-2814).\n *\n * Everything above only guards the 302. What the 302 points at is what\n * the buyer, and anyone who copies the `Location` out of the buyer's\n * network panel, keeps. So `resolvePaidMediaDelivery` hands back one of\n * three things and never a permanent URL:\n *\n * - a CDN URL signed for {@link GATED_VIDEO_SESSION_TTL_MS}, when the video\n * is a PRIVATE asset in this site's library or its org's. The CDN refuses\n * a private asset on every path without an unexpired signature.\n * - a V4 signed Storage read with the same lifetime, for an object in those\n * libraries that no media document owns. A raw download URL's token lives\n * as long as the object, so it is never the redirect.\n * - an author-typed hotlink, unchanged, because nothing of ours runs there.\n *\n * A video whose asset is still PUBLIC is refused rather than delivered:\n * its URL already works for anyone, so redirecting a buyer to it is the\n * leak. The 409 is what the gated player reports to the viewer, and the\n * log line is what tells the operator which product needs its video made\n * private.\n *\n * `?r=auto` rides inside the signed URL (AGL-2766). The signature covers\n * `(scope, mediaId, exp)` and not the parameter, so it asks the CDN for the\n * best delivery copy of the one asset the signature names and can reach no\n * other. Nothing the caller sends reaches this `Location`: the GET half\n * reads `hostId`, `productId`, `video`, `exp` and `sig`, and builds the\n * target from the product document.\n *\n * With a delivery provider configured and its release flag on for the\n * owning org, a video with a current copy there is redirected straight to\n * the provider's short-lived URL instead (AGL-2824), for the same session\n * and the same rendition the CDN would pick. `Accept` is the one request\n * header that reaches that choice, and only to pick among the asset's own\n * encodings.\n */\n const delivery = await resolvePaidMediaDelivery({\n stored: file.url,\n hostId,\n ttlMs: GATED_VIDEO_SESSION_TTL_MS,\n cdnParams: [[MEDIA_CDN_RENDITION_PARAM, MEDIA_CDN_RENDITION_AUTO]],\n delivery: { accept: req.headers?.['accept'] },\n io: createPaidMediaDeliveryIo({\n firestore: app.firestore(),\n bucket: app\n .storage()\n .bucket(process.env['NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET']),\n }),\n })\n // Per-member and time-boxed, whichever way it goes: no cache may keep the\n // redirect past the session it opens, nor a refusal past the fix.\n res.setHeader('Cache-Control', 'private, no-store')\n if (delivery.ok === false) {\n console.error(\n '[commerce/stream] gated video not delivered',\n JSON.stringify({ hostId, productId, video, refusal: delivery.refusal }),\n )\n return delivery.refusal === 'not-private'\n ? res.status(409).send('This video is not ready to play yet')\n : res.status(404).send('No video')\n }\n /*\n * `Vary: Accept` only when this response chose the rendition (AGL-2824).\n *\n * A signed CDN URL is the same for every client: the `Location` is a\n * function of the product document and the clock, not of the request's\n * `Accept`, and the negotiation happens on the SECOND response, where\n * `serveMediaCdn` declares `Vary` (AGL-2766). A delivery provider's URL is\n * minted after the rendition is picked, so there the `Accept` shaped this\n * `Location`, and saying so is correct even under `no-store`.\n */\n if (delivery.via === 'delivery') res.setHeader('Vary', 'Accept')\n return res.redirect(302, delivery.location)\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Stream unavailable' })\n }\n}\n"],"names":["MEDIA_CDN_RENDITION_AUTO","MEDIA_CDN_RENDITION_PARAM","CommerceModel","firebaseAdmin","GATED_VIDEO_SESSION_TTL_MS","createPaidMediaDeliveryIo","resolvePaidMediaDelivery","createHmac","timingSafeEqual","requireActiveMember","checkMemberEntitlement","tokenSigningSecret","TTL_MS","sign","hostId","productId","video","exp","update","digest","slice","signatureMatches","presented","expected","a","Buffer","from","String","b","length","Uint8Array","streamHandler","req","res","method","body","query","Math","max","Number","status","json","error","productSnapshot","product","auth","email","member","get","entitled","Date","now","url","encodeURIComponent","setHeader","expiresAtMs","sig","send","app","firestore","collection","doc","liftLegacyProduct","data","file","gatedVideos","delivery","stored","ttlMs","cdnParams","accept","headers","io","bucket","storage","process","env","ok","console","JSON","stringify","refusal","via","redirect","location"],"mappings":"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"}
@@ -0,0 +1,24 @@
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
+ * Subscriber self-service (AGL-303): opens the Stripe Billing Portal for
20
+ * the signed-in member's storefront subscription (cancel, switch
21
+ * payment method). Member-gated; the portal customer is matched by the
22
+ * member's email through the recorded subscription doc.
23
+ */
24
+ export declare const subscriptionPortalHandler: PluginApiHandler;