@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,222 @@
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 { createHash, createHmac, randomBytes, scryptSync, timingSafeEqual } from "crypto";
17
+ import { firebaseAdmin } from "@aglyn/tenant-data-admin";
18
+ /**
19
+ * Site membership primitives (AGL-109): visitor accounts per host with
20
+ * scrypt-hashed passwords and an HMAC-signed httpOnly session cookie.
21
+ * Per-site Firebase Auth is a future infra decision (AGL-45 class); this
22
+ * keeps member content out of static HTML the same way AGL-87 does.
23
+ */ const SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000;
24
+ // A per-boot fallback keeps dev working; production must set the env so
25
+ // sessions survive deploys and instances agree.
26
+ //
27
+ // `||`, NOT `??`, and on a self-host install that is the whole difference
28
+ // (AGL-2014). `.env.selfhost.example` ships `MEMBER_SESSION_SECRET=` blank and
29
+ // compose `env_file` sets a blank line as the EMPTY STRING rather than leaving
30
+ // it unset — so `??` did not fire, `SESSION_SECRET` was `''`, and `createHmac`
31
+ // signs happily with an empty key. Every storefront member session cookie on a
32
+ // template-following deployment was therefore signed with a key any reader of
33
+ // this repository already has, i.e. forgeable. `||` restores the per-boot
34
+ // random the comment above describes and the env template already promises
35
+ // ("without it, sessions don't survive a restart and disagree across
36
+ // instances") — unforgeable, and visibly degraded rather than silently open.
37
+ //
38
+ // Nothing caught it because `tools/scripts/selfhost-ci-env.mjs` FILLS this
39
+ // variable before the image build, so the one job that builds these images
40
+ // tests a configuration no operator following the template has.
41
+ const SESSION_SECRET = process.env.MEMBER_SESSION_SECRET || randomBytes(32).toString('hex');
42
+ export function hashMemberPassword(password) {
43
+ const salt = randomBytes(16).toString('hex');
44
+ const hash = scryptSync(password, salt, 64).toString('hex');
45
+ return `${salt}:${hash}`;
46
+ }
47
+ export function verifyMemberPassword(password, stored) {
48
+ const [salt, hash] = String(stored != null ? stored : '').split(':');
49
+ if (!salt || !hash) return false;
50
+ const candidate = scryptSync(password, salt, 64);
51
+ const expected = Buffer.from(hash, 'hex');
52
+ return candidate.length === expected.length && timingSafeEqual(new Uint8Array(candidate), new Uint8Array(expected));
53
+ }
54
+ function sign(payload) {
55
+ return createHmac('sha256', SESSION_SECRET).update(payload).digest('hex');
56
+ }
57
+ export function mintMemberSession(hostId, memberId) {
58
+ const payload = `${hostId}.${memberId}.${Date.now() + SESSION_TTL_MS}`;
59
+ return `${payload}.${sign(payload)}`;
60
+ }
61
+ /** Returns the session contents when the cookie verifies for this host. */ export function parseMemberSession(hostId, token) {
62
+ const parts = String(token != null ? token : '').split('.');
63
+ if (parts.length !== 4) return null;
64
+ const [tokenHost, memberId, expires, signature] = parts;
65
+ if (tokenHost !== hostId) return null;
66
+ if (Number(expires) < Date.now()) return null;
67
+ const payload = `${tokenHost}.${memberId}.${expires}`;
68
+ const expected = sign(payload);
69
+ const a = Buffer.from(signature);
70
+ const b = Buffer.from(expected);
71
+ if (a.length !== b.length) return null;
72
+ if (!timingSafeEqual(new Uint8Array(a), new Uint8Array(b))) return null;
73
+ return {
74
+ memberId,
75
+ issuedAtMs: Number(expires) - SESSION_TTL_MS
76
+ };
77
+ }
78
+ /** Returns the memberId when the cookie verifies for this host. */ export function verifyMemberSession(hostId, token) {
79
+ var _ref;
80
+ var _parseMemberSession;
81
+ return (_ref = (_parseMemberSession = parseMemberSession(hostId, token)) == null ? void 0 : _parseMemberSession.memberId) != null ? _ref : null;
82
+ }
83
+ /** Password-reset links live for one hour (AGL-552). */ const RESET_TOKEN_TTL_MS = 60 * 60 * 1000;
84
+ /**
85
+ * Fingerprint of the member's CURRENT scrypt hash, folded into the reset
86
+ * token signature (AGL-552): completing a reset rewrites `passwordScrypt`,
87
+ * so every token minted before it stops verifying — single-use without any
88
+ * server-side token storage.
89
+ */ function passwordFingerprint(passwordScrypt) {
90
+ return createHash('sha256').update(String(passwordScrypt != null ? passwordScrypt : '')).digest('hex');
91
+ }
92
+ /**
93
+ * Single-use, time-boxed password-reset token (AGL-552):
94
+ * `hostId.memberId.expiry.HMAC(payload + password-fingerprint)`. Same
95
+ * secret as sessions; the fingerprint binding is what makes it one-shot.
96
+ */ export function mintPasswordResetToken(hostId, memberId, passwordScrypt) {
97
+ const payload = `${hostId}.${memberId}.${Date.now() + RESET_TOKEN_TTL_MS}`;
98
+ return `${payload}.${sign(`${payload}.${passwordFingerprint(passwordScrypt)}`)}`;
99
+ }
100
+ /**
101
+ * First step of reset verification: extract the memberId from a
102
+ * well-formed, unexpired token for THIS host. The signature is NOT checked
103
+ * here (it binds to the member's current password hash, which the caller
104
+ * has to load first) — never trust the result without a follow-up
105
+ * {@link verifyPasswordResetToken}.
106
+ */ export function passwordResetTokenMemberId(hostId, token) {
107
+ const parts = String(token != null ? token : '').split('.');
108
+ if (parts.length !== 4) return null;
109
+ const [tokenHost, memberId, expires] = parts;
110
+ if (tokenHost !== hostId) return null;
111
+ if (!memberId) return null;
112
+ if (Number(expires) < Date.now()) return null;
113
+ return memberId;
114
+ }
115
+ /** Full reset-token check: host, expiry, and password-hash binding. */ export function verifyPasswordResetToken(hostId, token, currentPasswordScrypt) {
116
+ const parts = String(token != null ? token : '').split('.');
117
+ if (parts.length !== 4) return false;
118
+ const [tokenHost, memberId, expires, signature] = parts;
119
+ if (tokenHost !== hostId || !memberId) return false;
120
+ if (Number(expires) < Date.now()) return false;
121
+ const payload = `${tokenHost}.${memberId}.${expires}`;
122
+ const expected = sign(`${payload}.${passwordFingerprint(currentPasswordScrypt)}`);
123
+ const a = Buffer.from(signature);
124
+ const b = Buffer.from(expected);
125
+ if (a.length !== b.length) return false;
126
+ return timingSafeEqual(new Uint8Array(a), new Uint8Array(b));
127
+ }
128
+ export function memberCookieName(hostId) {
129
+ return `aglyn_member_${hostId.replace(/[^A-Za-z0-9_-]/g, '')}`;
130
+ }
131
+ export function setMemberCookie(res, hostId, token) {
132
+ const name = memberCookieName(hostId);
133
+ // Secure in production (AGL-500): the member session is an HMAC-signed
134
+ // credential, so it must never travel over plaintext HTTP on a tenant
135
+ // custom domain. Omit only in local dev (http://localhost), matching the
136
+ // console __session cookie.
137
+ const secure = process.env.NODE_ENV === 'production' ? '; Secure' : '';
138
+ const base = `${name}=${token != null ? token : ''}; Path=/; HttpOnly; SameSite=Lax${secure}`;
139
+ res.setHeader('Set-Cookie', token ? `${base}; Max-Age=${30 * 24 * 60 * 60}` : `${base}; Max-Age=0`);
140
+ }
141
+ export function readMemberSession(req, hostId) {
142
+ var _req_cookies;
143
+ const raw = (_req_cookies = req.cookies) == null ? void 0 : _req_cookies[memberCookieName(hostId)];
144
+ return verifyMemberSession(hostId, raw);
145
+ }
146
+ /**
147
+ * Field name for the bulk session cut-off (AGL-914). Set to `Date.now()`
148
+ * whenever every existing session for a member must stop working — today
149
+ * only when an admin sets their password from the console, since a rewritten
150
+ * `passwordScrypt` does NOT invalidate sessions on its own (unlike a reset
151
+ * TOKEN, which binds to the hash by design).
152
+ */ export const MEMBER_SESSIONS_VALID_FROM_FIELD = 'sessionsValidFromMs';
153
+ /** Suspension rejection body — matches the login flow's AGL-546 message. */ export const MEMBER_SUSPENDED_ERROR = 'This account has been suspended. Contact the site owner.';
154
+ /**
155
+ * Session + member-doc resolution for cookie-authenticated endpoints
156
+ * (AGL-550). A verified cookie alone stopped being enough once console
157
+ * suspension (AGL-546) landed: the HMAC session outlives the member's
158
+ * standing, so every endpoint must also load the doc and check
159
+ * `suspended`. That is one doc read per request — acceptable, and most
160
+ * endpoints needed the member doc anyway. A deleted member doc reads as
161
+ * `anonymous` (same as no cookie).
162
+ */ export async function readActiveMemberSession(req, hostId) {
163
+ var _member_get;
164
+ var _req_cookies;
165
+ const session = parseMemberSession(hostId, (_req_cookies = req.cookies) == null ? void 0 : _req_cookies[memberCookieName(hostId)]);
166
+ if (!session) return {
167
+ status: 'anonymous'
168
+ };
169
+ const { memberId, issuedAtMs } = session;
170
+ const member = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).collection('siteMembers').doc(memberId).get();
171
+ if (!member.exists) return {
172
+ status: 'anonymous'
173
+ };
174
+ // Revoked in bulk (AGL-914): a session minted before the cut-off is dead
175
+ // even though its signature is still good. Reads as `anonymous` rather
176
+ // than `suspended` — the account is fine, this browser just holds a stale
177
+ // cookie, and signing in again is the correct next step.
178
+ const validFrom = Number((_member_get = member.get(MEMBER_SESSIONS_VALID_FROM_FIELD)) != null ? _member_get : 0);
179
+ if (validFrom && issuedAtMs < validFrom) return {
180
+ status: 'anonymous'
181
+ };
182
+ if (member.get('suspended') === true) {
183
+ return {
184
+ status: 'suspended',
185
+ memberId
186
+ };
187
+ }
188
+ return {
189
+ status: 'active',
190
+ memberId,
191
+ member
192
+ };
193
+ }
194
+ /**
195
+ * Guard for cookie-authenticated member endpoints (AGL-550): resolves
196
+ * the session AND the member doc, writing the rejection itself — 401
197
+ * (`signInError`) when there is no usable session, 403 with the session
198
+ * cookie cleared (mirroring membership-account's AGL-546 handling, so
199
+ * the site stops presenting a signed-in shell) when the member is
200
+ * suspended. Returns null after responding; handlers `if (!auth) return`.
201
+ */ export async function requireActiveMember(req, res, hostId, signInError = 'Not signed in') {
202
+ const session = await readActiveMemberSession(req, hostId);
203
+ if (session.status === 'suspended') {
204
+ setMemberCookie(res, hostId, null);
205
+ res.status(403).json({
206
+ error: MEMBER_SUSPENDED_ERROR
207
+ });
208
+ return null;
209
+ }
210
+ if (session.status !== 'active') {
211
+ res.status(401).json({
212
+ error: signInError
213
+ });
214
+ return null;
215
+ }
216
+ return {
217
+ memberId: session.memberId,
218
+ member: session.member
219
+ };
220
+ }
221
+
222
+ //# sourceMappingURL=membership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership.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 createHash,\n createHmac,\n randomBytes,\n scryptSync,\n timingSafeEqual,\n} from 'crypto'\nimport type { PluginApiRequest, PluginApiResponse } from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\n\n/**\n * Site membership primitives (AGL-109): visitor accounts per host with\n * scrypt-hashed passwords and an HMAC-signed httpOnly session cookie.\n * Per-site Firebase Auth is a future infra decision (AGL-45 class); this\n * keeps member content out of static HTML the same way AGL-87 does.\n */\n\nconst SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1000\n\n// A per-boot fallback keeps dev working; production must set the env so\n// sessions survive deploys and instances agree.\n//\n// `||`, NOT `??`, and on a self-host install that is the whole difference\n// (AGL-2014). `.env.selfhost.example` ships `MEMBER_SESSION_SECRET=` blank and\n// compose `env_file` sets a blank line as the EMPTY STRING rather than leaving\n// it unset — so `??` did not fire, `SESSION_SECRET` was `''`, and `createHmac`\n// signs happily with an empty key. Every storefront member session cookie on a\n// template-following deployment was therefore signed with a key any reader of\n// this repository already has, i.e. forgeable. `||` restores the per-boot\n// random the comment above describes and the env template already promises\n// (\"without it, sessions don't survive a restart and disagree across\n// instances\") — unforgeable, and visibly degraded rather than silently open.\n//\n// Nothing caught it because `tools/scripts/selfhost-ci-env.mjs` FILLS this\n// variable before the image build, so the one job that builds these images\n// tests a configuration no operator following the template has.\nconst SESSION_SECRET =\n process.env.MEMBER_SESSION_SECRET || randomBytes(32).toString('hex')\n\nexport function hashMemberPassword(password: string): string {\n const salt = randomBytes(16).toString('hex')\n const hash = scryptSync(password, salt, 64).toString('hex')\n return `${salt}:${hash}`\n}\n\nexport function verifyMemberPassword(\n password: string,\n stored: string | undefined,\n): boolean {\n const [salt, hash] = String(stored ?? '').split(':')\n if (!salt || !hash) return false\n const candidate = scryptSync(password, salt, 64)\n const expected = Buffer.from(hash, 'hex')\n return (\n candidate.length === expected.length &&\n timingSafeEqual(new Uint8Array(candidate), new Uint8Array(expected))\n )\n}\n\nfunction sign(payload: string): string {\n return createHmac('sha256', SESSION_SECRET).update(payload).digest('hex')\n}\n\nexport function mintMemberSession(hostId: string, memberId: string): string {\n const payload = `${hostId}.${memberId}.${Date.now() + SESSION_TTL_MS}`\n return `${payload}.${sign(payload)}`\n}\n\n/**\n * Verified session contents. `issuedAtMs` is derived, not stored: the\n * payload carries `now + TTL`, so subtracting the TTL recovers the mint\n * time. That is what lets sessions be revoked in bulk (AGL-914) without\n * changing the token format or invalidating cookies already in the wild.\n */\nexport interface MemberSessionToken {\n memberId: string\n issuedAtMs: number\n}\n\n/** Returns the session contents when the cookie verifies for this host. */\nexport function parseMemberSession(\n hostId: string,\n token: string | undefined,\n): MemberSessionToken | null {\n const parts = String(token ?? '').split('.')\n if (parts.length !== 4) return null\n const [tokenHost, memberId, expires, signature] = parts\n if (tokenHost !== hostId) return null\n if (Number(expires) < Date.now()) return null\n const payload = `${tokenHost}.${memberId}.${expires}`\n const expected = sign(payload)\n const a = Buffer.from(signature)\n const b = Buffer.from(expected)\n if (a.length !== b.length) return null\n if (!timingSafeEqual(new Uint8Array(a), new Uint8Array(b))) return null\n return { memberId, issuedAtMs: Number(expires) - SESSION_TTL_MS }\n}\n\n/** Returns the memberId when the cookie verifies for this host. */\nexport function verifyMemberSession(\n hostId: string,\n token: string | undefined,\n): string | null {\n return parseMemberSession(hostId, token)?.memberId ?? null\n}\n\n/** Password-reset links live for one hour (AGL-552). */\nconst RESET_TOKEN_TTL_MS = 60 * 60 * 1000\n\n/**\n * Fingerprint of the member's CURRENT scrypt hash, folded into the reset\n * token signature (AGL-552): completing a reset rewrites `passwordScrypt`,\n * so every token minted before it stops verifying — single-use without any\n * server-side token storage.\n */\nfunction passwordFingerprint(passwordScrypt: string | undefined): string {\n return createHash('sha256')\n .update(String(passwordScrypt ?? ''))\n .digest('hex')\n}\n\n/**\n * Single-use, time-boxed password-reset token (AGL-552):\n * `hostId.memberId.expiry.HMAC(payload + password-fingerprint)`. Same\n * secret as sessions; the fingerprint binding is what makes it one-shot.\n */\nexport function mintPasswordResetToken(\n hostId: string,\n memberId: string,\n passwordScrypt: string | undefined,\n): string {\n const payload = `${hostId}.${memberId}.${Date.now() + RESET_TOKEN_TTL_MS}`\n return `${payload}.${sign(\n `${payload}.${passwordFingerprint(passwordScrypt)}`,\n )}`\n}\n\n/**\n * First step of reset verification: extract the memberId from a\n * well-formed, unexpired token for THIS host. The signature is NOT checked\n * here (it binds to the member's current password hash, which the caller\n * has to load first) — never trust the result without a follow-up\n * {@link verifyPasswordResetToken}.\n */\nexport function passwordResetTokenMemberId(\n hostId: string,\n token: string | undefined,\n): string | null {\n const parts = String(token ?? '').split('.')\n if (parts.length !== 4) return null\n const [tokenHost, memberId, expires] = parts\n if (tokenHost !== hostId) return null\n if (!memberId) return null\n if (Number(expires) < Date.now()) return null\n return memberId\n}\n\n/** Full reset-token check: host, expiry, and password-hash binding. */\nexport function verifyPasswordResetToken(\n hostId: string,\n token: string | undefined,\n currentPasswordScrypt: string | undefined,\n): boolean {\n const parts = String(token ?? '').split('.')\n if (parts.length !== 4) return false\n const [tokenHost, memberId, expires, signature] = parts\n if (tokenHost !== hostId || !memberId) return false\n if (Number(expires) < Date.now()) return false\n const payload = `${tokenHost}.${memberId}.${expires}`\n const expected = sign(\n `${payload}.${passwordFingerprint(currentPasswordScrypt)}`,\n )\n const a = Buffer.from(signature)\n const b = Buffer.from(expected)\n if (a.length !== b.length) return false\n return timingSafeEqual(new Uint8Array(a), new Uint8Array(b))\n}\n\nexport function memberCookieName(hostId: string): string {\n return `aglyn_member_${hostId.replace(/[^A-Za-z0-9_-]/g, '')}`\n}\n\nexport function setMemberCookie(\n res: PluginApiResponse,\n hostId: string,\n token: string | null,\n): void {\n const name = memberCookieName(hostId)\n // Secure in production (AGL-500): the member session is an HMAC-signed\n // credential, so it must never travel over plaintext HTTP on a tenant\n // custom domain. Omit only in local dev (http://localhost), matching the\n // console __session cookie.\n const secure = process.env.NODE_ENV === 'production' ? '; Secure' : ''\n const base = `${name}=${token ?? ''}; Path=/; HttpOnly; SameSite=Lax${secure}`\n res.setHeader(\n 'Set-Cookie',\n token ? `${base}; Max-Age=${30 * 24 * 60 * 60}` : `${base}; Max-Age=0`,\n )\n}\n\nexport function readMemberSession(\n req: PluginApiRequest,\n hostId: string,\n): string | null {\n const raw = req.cookies?.[memberCookieName(hostId)]\n return verifyMemberSession(hostId, raw)\n}\n\n/**\n * Field name for the bulk session cut-off (AGL-914). Set to `Date.now()`\n * whenever every existing session for a member must stop working — today\n * only when an admin sets their password from the console, since a rewritten\n * `passwordScrypt` does NOT invalidate sessions on its own (unlike a reset\n * TOKEN, which binds to the hash by design).\n */\nexport const MEMBER_SESSIONS_VALID_FROM_FIELD = 'sessionsValidFromMs'\n\n/** Suspension rejection body — matches the login flow's AGL-546 message. */\nexport const MEMBER_SUSPENDED_ERROR =\n 'This account has been suspended. Contact the site owner.'\n\n/** Resolved member-session state, see {@link readActiveMemberSession}. */\nexport type MemberSessionCheck =\n | { status: 'anonymous' }\n | { status: 'suspended'; memberId: string }\n | {\n status: 'active'\n memberId: string\n member: FirebaseFirestore.DocumentSnapshot\n }\n\n/**\n * Session + member-doc resolution for cookie-authenticated endpoints\n * (AGL-550). A verified cookie alone stopped being enough once console\n * suspension (AGL-546) landed: the HMAC session outlives the member's\n * standing, so every endpoint must also load the doc and check\n * `suspended`. That is one doc read per request — acceptable, and most\n * endpoints needed the member doc anyway. A deleted member doc reads as\n * `anonymous` (same as no cookie).\n */\nexport async function readActiveMemberSession(\n req: PluginApiRequest,\n hostId: string,\n): Promise<MemberSessionCheck> {\n const session = parseMemberSession(\n hostId,\n req.cookies?.[memberCookieName(hostId)],\n )\n if (!session) return { status: 'anonymous' }\n const { memberId, issuedAtMs } = session\n const member = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('siteMembers')\n .doc(memberId)\n .get()\n if (!member.exists) return { status: 'anonymous' }\n // Revoked in bulk (AGL-914): a session minted before the cut-off is dead\n // even though its signature is still good. Reads as `anonymous` rather\n // than `suspended` — the account is fine, this browser just holds a stale\n // cookie, and signing in again is the correct next step.\n const validFrom = Number(member.get(MEMBER_SESSIONS_VALID_FROM_FIELD) ?? 0)\n if (validFrom && issuedAtMs < validFrom) return { status: 'anonymous' }\n if (member.get('suspended') === true) {\n return { status: 'suspended', memberId }\n }\n return { status: 'active', memberId, member }\n}\n\n/**\n * Guard for cookie-authenticated member endpoints (AGL-550): resolves\n * the session AND the member doc, writing the rejection itself — 401\n * (`signInError`) when there is no usable session, 403 with the session\n * cookie cleared (mirroring membership-account's AGL-546 handling, so\n * the site stops presenting a signed-in shell) when the member is\n * suspended. Returns null after responding; handlers `if (!auth) return`.\n */\nexport async function requireActiveMember(\n req: PluginApiRequest,\n res: PluginApiResponse,\n hostId: string,\n signInError = 'Not signed in',\n): Promise<{\n memberId: string\n member: FirebaseFirestore.DocumentSnapshot\n} | null> {\n const session = await readActiveMemberSession(req, hostId)\n if (session.status === 'suspended') {\n setMemberCookie(res, hostId, null)\n res.status(403).json({ error: MEMBER_SUSPENDED_ERROR })\n return null\n }\n if (session.status !== 'active') {\n res.status(401).json({ error: signInError })\n return null\n }\n return { memberId: session.memberId, member: session.member }\n}\n"],"names":["createHash","createHmac","randomBytes","scryptSync","timingSafeEqual","firebaseAdmin","SESSION_TTL_MS","SESSION_SECRET","process","env","MEMBER_SESSION_SECRET","toString","hashMemberPassword","password","salt","hash","verifyMemberPassword","stored","String","split","candidate","expected","Buffer","from","length","Uint8Array","sign","payload","update","digest","mintMemberSession","hostId","memberId","Date","now","parseMemberSession","token","parts","tokenHost","expires","signature","Number","a","b","issuedAtMs","verifyMemberSession","RESET_TOKEN_TTL_MS","passwordFingerprint","passwordScrypt","mintPasswordResetToken","passwordResetTokenMemberId","verifyPasswordResetToken","currentPasswordScrypt","memberCookieName","replace","setMemberCookie","res","name","secure","NODE_ENV","base","setHeader","readMemberSession","req","raw","cookies","MEMBER_SESSIONS_VALID_FROM_FIELD","MEMBER_SUSPENDED_ERROR","readActiveMemberSession","member","session","status","app","firestore","collection","doc","get","exists","validFrom","requireActiveMember","signInError","json","error"],"mappings":"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"}
@@ -0,0 +1,170 @@
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 storefront Payment Element (AGL-1944), the half of native payments
19
+ * AGL-1132 said mattered more: a shopper on a merchant's own domain pays
20
+ * without being bounced to `checkout.stripe.com` and back.
21
+ *
22
+ * ## Why a Checkout Session and not a bare PaymentIntent
23
+ *
24
+ * AGL-1132 recommended "PaymentIntent + `<PaymentElement/>`" for this surface,
25
+ * and AGL-1944 asked for that to be a conscious re-decision rather than an
26
+ * inherited one. It is: the Payment Element ships, the bare PaymentIntent does
27
+ * not, and the difference is worth stating because it is the whole safety
28
+ * argument.
29
+ *
30
+ * A PaymentIntent-backed flow would have to own totals, tax, shipping and the
31
+ * platform fee itself — which means a SECOND place that computes what a shopper
32
+ * owes. Two paths that price a cart independently is how a storefront starts
33
+ * under-collecting tax without anyone noticing: nothing fails, the number is
34
+ * just quietly wrong on one of them. It also emits `payment_intent.succeeded`
35
+ * rather than `checkout.session.completed`, so every fulfilment branch in
36
+ * `billing-webhook.ts` — orders, inventory, coupons, gift cards, licence keys,
37
+ * subscriptions — would need a parallel implementation reading a different
38
+ * object shape.
39
+ *
40
+ * `ui_mode: 'custom'` is the Payment Element backed by a Checkout Session. The
41
+ * shopper sees `<PaymentElement/>` inside the merchant's own layout — no Stripe
42
+ * chrome, which is the thing AGL-1944 objected to about embedded Checkout — and
43
+ * the server keeps the session builder it already has. So:
44
+ *
45
+ * - tax, shipping, inventory, coupons, the Connect destination and the fee
46
+ * ladder are the SAME code, not a parallel implementation. Parity is by
47
+ * construction rather than by test;
48
+ * - `checkout.session.completed` still fires, so `billing-webhook.ts` needs
49
+ * no change at all and stays the only thing that fulfils an order.
50
+ *
51
+ * ## The API version, and why it is pinned per-request
52
+ *
53
+ * Measured against live Stripe in test mode: `ui_mode: 'custom'` is refused at
54
+ * the platform account's default API version with
55
+ *
56
+ * Invalid Stripe API version: 2020-08-27. In order to use `ui_mode:
57
+ * custom`, you must upgrade to Stripe API version 2025-03-31.basil.
58
+ *
59
+ * Upgrading the ACCOUNT default would re-version every Stripe call in this
60
+ * repo at once — a change whose blast radius nobody could review in one pass.
61
+ * A `Stripe-Version` header pins one request instead, and the pin is contained
62
+ * in a way worth being explicit about: webhook deliveries are versioned per
63
+ * ENDPOINT, not per request, so the events `billing-webhook.ts` parses keep
64
+ * arriving in exactly the shape it already reads. The only thing read off the
65
+ * pinned response is `client_secret` and `id`, neither of which the version
66
+ * affects.
67
+ *
68
+ * Same probe, same params (line items, a manual tax line,
69
+ * `shipping_address_collection`, a `fixed_amount` shipping option, Connect
70
+ * `transfer_data[destination]` and `application_fee_amount`), hosted vs custom:
71
+ * `amount_total` **6212 in both**. That is the tax-and-shipping parity claim,
72
+ * measured rather than assumed.
73
+ *
74
+ * ## What this module deliberately does NOT do
75
+ *
76
+ * It does not fulfil anything, and it hands the browser nothing it could use to
77
+ * claim an order was paid. The client gets a `clientSecret` — which is an
78
+ * instruction to Stripe, not an assertion about money — and the same
79
+ * `session_id` the redirect flow already put in the return URL. Fulfilment
80
+ * remains `checkout.session.completed` in `billing-webhook.ts`, exactly as with
81
+ * the redirect. An in-page flow makes trusting the browser tempting; the
82
+ * browser is the one participant that can lie about having paid.
83
+ */
84
+ /**
85
+ * The API version `ui_mode: 'custom'` needs, pinned on the session-create
86
+ * request only. Stated once so the header and the doc comment cannot drift.
87
+ */
88
+ export declare const NATIVE_CHECKOUT_STRIPE_VERSION = "2025-03-31.basil";
89
+ /** The flag both halves of native checkout share (AGL-1132, AGL-1944). */
90
+ export declare const NATIVE_CHECKOUT_FLAG: "release_native_checkout";
91
+ export interface NativeCheckoutMode {
92
+ /** True only when the flag is on for this org AND a publishable key exists. */
93
+ native: boolean;
94
+ /** Empty unless `native` — the client needs it to boot Stripe.js. */
95
+ publishableKey: string;
96
+ }
97
+ /**
98
+ * Two conditions, not one — the same pairing the console's route settled on.
99
+ *
100
+ * A `ui_mode` session returns a `client_secret` and NO `url`, so a browser that
101
+ * cannot mount Stripe.js has nothing to navigate to. If the flag alone decided
102
+ * this, flipping it in an environment without
103
+ * `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` would replace a working redirect with a
104
+ * dead Buy button on every storefront at once. Requiring the key here means the
105
+ * worst case of a premature flag flip is the redirect we already ship.
106
+ *
107
+ * That is not hypothetical for the storefront. Measured on 2026-08-18 through
108
+ * the Vercel REST API (`vercel env ls` cannot see team-shared vars):
109
+ * `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` exists as a project-level var on
110
+ * `aglyn-console` and is set NOWHERE on `aglyn-tenant`, which is the project
111
+ * that serves storefronts. So today this resolves to `native: false` in
112
+ * production however the flag is set, and the shopper keeps the redirect.
113
+ *
114
+ * The org — never the host — is the rollout bucket, per AGL-1656: a hostId and
115
+ * an orgId hash to different buckets, so bucketing on the host could land a
116
+ * merchant's storefront and their console on opposite sides of the same
117
+ * percentage.
118
+ */
119
+ export declare function resolveNativeCheckoutMode(orgId: string | null | undefined): Promise<NativeCheckoutMode>;
120
+ /**
121
+ * Rewrite a built session param set for the Payment Element, in place.
122
+ *
123
+ * DELETES `success_url` and `cancel_url` rather than leaving them alongside
124
+ * `return_url`: Stripe rejects the pair outright on a `ui_mode` session, and a
125
+ * 400 here would surface to the shopper as "Checkout failed" on a store that
126
+ * was working an hour ago. Everything else in the param set — line items, the
127
+ * tax line, `automatic_tax`, shipping options, the Connect destination, the
128
+ * application fee, every `metadata[…]` key the webhook reads — is left exactly
129
+ * as the caller built it. That is the point: this function must be incapable of
130
+ * changing what the shopper is charged.
131
+ *
132
+ * `returnUrl` is the caller's OWN `success_url` string, `{CHECKOUT_SESSION_ID}`
133
+ * placeholder and all. Stripe substitutes it on a `ui_mode: 'custom'` return
134
+ * exactly as it does on a hosted redirect (measured), so the storefront's
135
+ * post-purchase reporting — which reads `session_id` off the query to name the
136
+ * order it just completed (AGL-1641) — keeps working unchanged.
137
+ */
138
+ export declare function applyNativeCheckoutParams(params: URLSearchParams, returnUrl: string): void;
139
+ /**
140
+ * The `Stripe-Version` pin, as headers to spread into the session-create fetch.
141
+ * Empty for the hosted path so its request is byte-identical to today's.
142
+ */
143
+ export declare function nativeCheckoutStripeHeaders(mode: NativeCheckoutMode): Record<string, string>;
144
+ export interface NativeCheckoutPayload {
145
+ /** Stripe's own instruction to the browser. Not a claim that money moved. */
146
+ clientSecret: string;
147
+ publishableKey: string;
148
+ /** The order document id, and the `session_id` the return URL carries. */
149
+ sessionId: string;
150
+ }
151
+ export interface HostedCheckoutPayload {
152
+ url: string;
153
+ }
154
+ /**
155
+ * Read the created session into the payload the storefront gets back.
156
+ *
157
+ * Returns `null` when the session is unusable, which BOTH callers already treat
158
+ * as "nothing was sold" — release the idempotency claim and answer 502. The
159
+ * null case is the whole reason this is a function rather than a ternary: the
160
+ * hosted path's liveness check was `!session.url`, and a `ui_mode` session
161
+ * never has one. Left unchanged, switching a store to native mode would have
162
+ * made every successful session look like a Stripe failure — a 502 to the
163
+ * shopper, a released claim, and a real Checkout Session left open on the
164
+ * account. So the check has to move with the mode, and it does.
165
+ */
166
+ export declare function readCheckoutSessionPayload(session: {
167
+ url?: string;
168
+ id?: string;
169
+ client_secret?: string;
170
+ }, mode: NativeCheckoutMode): NativeCheckoutPayload | HostedCheckoutPayload | null;
@@ -0,0 +1,193 @@
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 { isServerReleaseFlagOnForOrg } from "@aglyn/tenant-data-admin";
17
+ /**
18
+ * The storefront Payment Element (AGL-1944), the half of native payments
19
+ * AGL-1132 said mattered more: a shopper on a merchant's own domain pays
20
+ * without being bounced to `checkout.stripe.com` and back.
21
+ *
22
+ * ## Why a Checkout Session and not a bare PaymentIntent
23
+ *
24
+ * AGL-1132 recommended "PaymentIntent + `<PaymentElement/>`" for this surface,
25
+ * and AGL-1944 asked for that to be a conscious re-decision rather than an
26
+ * inherited one. It is: the Payment Element ships, the bare PaymentIntent does
27
+ * not, and the difference is worth stating because it is the whole safety
28
+ * argument.
29
+ *
30
+ * A PaymentIntent-backed flow would have to own totals, tax, shipping and the
31
+ * platform fee itself — which means a SECOND place that computes what a shopper
32
+ * owes. Two paths that price a cart independently is how a storefront starts
33
+ * under-collecting tax without anyone noticing: nothing fails, the number is
34
+ * just quietly wrong on one of them. It also emits `payment_intent.succeeded`
35
+ * rather than `checkout.session.completed`, so every fulfilment branch in
36
+ * `billing-webhook.ts` — orders, inventory, coupons, gift cards, licence keys,
37
+ * subscriptions — would need a parallel implementation reading a different
38
+ * object shape.
39
+ *
40
+ * `ui_mode: 'custom'` is the Payment Element backed by a Checkout Session. The
41
+ * shopper sees `<PaymentElement/>` inside the merchant's own layout — no Stripe
42
+ * chrome, which is the thing AGL-1944 objected to about embedded Checkout — and
43
+ * the server keeps the session builder it already has. So:
44
+ *
45
+ * - tax, shipping, inventory, coupons, the Connect destination and the fee
46
+ * ladder are the SAME code, not a parallel implementation. Parity is by
47
+ * construction rather than by test;
48
+ * - `checkout.session.completed` still fires, so `billing-webhook.ts` needs
49
+ * no change at all and stays the only thing that fulfils an order.
50
+ *
51
+ * ## The API version, and why it is pinned per-request
52
+ *
53
+ * Measured against live Stripe in test mode: `ui_mode: 'custom'` is refused at
54
+ * the platform account's default API version with
55
+ *
56
+ * Invalid Stripe API version: 2020-08-27. In order to use `ui_mode:
57
+ * custom`, you must upgrade to Stripe API version 2025-03-31.basil.
58
+ *
59
+ * Upgrading the ACCOUNT default would re-version every Stripe call in this
60
+ * repo at once — a change whose blast radius nobody could review in one pass.
61
+ * A `Stripe-Version` header pins one request instead, and the pin is contained
62
+ * in a way worth being explicit about: webhook deliveries are versioned per
63
+ * ENDPOINT, not per request, so the events `billing-webhook.ts` parses keep
64
+ * arriving in exactly the shape it already reads. The only thing read off the
65
+ * pinned response is `client_secret` and `id`, neither of which the version
66
+ * affects.
67
+ *
68
+ * Same probe, same params (line items, a manual tax line,
69
+ * `shipping_address_collection`, a `fixed_amount` shipping option, Connect
70
+ * `transfer_data[destination]` and `application_fee_amount`), hosted vs custom:
71
+ * `amount_total` **6212 in both**. That is the tax-and-shipping parity claim,
72
+ * measured rather than assumed.
73
+ *
74
+ * ## What this module deliberately does NOT do
75
+ *
76
+ * It does not fulfil anything, and it hands the browser nothing it could use to
77
+ * claim an order was paid. The client gets a `clientSecret` — which is an
78
+ * instruction to Stripe, not an assertion about money — and the same
79
+ * `session_id` the redirect flow already put in the return URL. Fulfilment
80
+ * remains `checkout.session.completed` in `billing-webhook.ts`, exactly as with
81
+ * the redirect. An in-page flow makes trusting the browser tempting; the
82
+ * browser is the one participant that can lie about having paid.
83
+ */ /**
84
+ * The API version `ui_mode: 'custom'` needs, pinned on the session-create
85
+ * request only. Stated once so the header and the doc comment cannot drift.
86
+ */ export const NATIVE_CHECKOUT_STRIPE_VERSION = '2025-03-31.basil';
87
+ /** The flag both halves of native checkout share (AGL-1132, AGL-1944). */ export const NATIVE_CHECKOUT_FLAG = 'release_native_checkout';
88
+ /**
89
+ * Two conditions, not one — the same pairing the console's route settled on.
90
+ *
91
+ * A `ui_mode` session returns a `client_secret` and NO `url`, so a browser that
92
+ * cannot mount Stripe.js has nothing to navigate to. If the flag alone decided
93
+ * this, flipping it in an environment without
94
+ * `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` would replace a working redirect with a
95
+ * dead Buy button on every storefront at once. Requiring the key here means the
96
+ * worst case of a premature flag flip is the redirect we already ship.
97
+ *
98
+ * That is not hypothetical for the storefront. Measured on 2026-08-18 through
99
+ * the Vercel REST API (`vercel env ls` cannot see team-shared vars):
100
+ * `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` exists as a project-level var on
101
+ * `aglyn-console` and is set NOWHERE on `aglyn-tenant`, which is the project
102
+ * that serves storefronts. So today this resolves to `native: false` in
103
+ * production however the flag is set, and the shopper keeps the redirect.
104
+ *
105
+ * The org — never the host — is the rollout bucket, per AGL-1656: a hostId and
106
+ * an orgId hash to different buckets, so bucketing on the host could land a
107
+ * merchant's storefront and their console on opposite sides of the same
108
+ * percentage.
109
+ */ export async function resolveNativeCheckoutMode(orgId) {
110
+ var _process_env_NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
111
+ const publishableKey = String((_process_env_NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY) != null ? _process_env_NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY : '').trim();
112
+ if (!publishableKey) return {
113
+ native: false,
114
+ publishableKey: ''
115
+ };
116
+ // A flag lookup that throws must not take a sale down with it, and the catch
117
+ // is a `try` rather than `.catch()` deliberately: Remote Config being
118
+ // unreachable rejects, but the resolver being absent entirely — an
119
+ // environment where the module never loaded — throws SYNCHRONOUSLY, before
120
+ // there is a promise to attach a handler to. Only the block form covers both.
121
+ // Either way the answer is the same and it is the safe one: the redirect.
122
+ let on;
123
+ try {
124
+ on = await isServerReleaseFlagOnForOrg(NATIVE_CHECKOUT_FLAG, orgId != null ? orgId : null);
125
+ } catch (unused) {
126
+ return {
127
+ native: false,
128
+ publishableKey: ''
129
+ };
130
+ }
131
+ return on ? {
132
+ native: true,
133
+ publishableKey
134
+ } : {
135
+ native: false,
136
+ publishableKey: ''
137
+ };
138
+ }
139
+ /**
140
+ * Rewrite a built session param set for the Payment Element, in place.
141
+ *
142
+ * DELETES `success_url` and `cancel_url` rather than leaving them alongside
143
+ * `return_url`: Stripe rejects the pair outright on a `ui_mode` session, and a
144
+ * 400 here would surface to the shopper as "Checkout failed" on a store that
145
+ * was working an hour ago. Everything else in the param set — line items, the
146
+ * tax line, `automatic_tax`, shipping options, the Connect destination, the
147
+ * application fee, every `metadata[…]` key the webhook reads — is left exactly
148
+ * as the caller built it. That is the point: this function must be incapable of
149
+ * changing what the shopper is charged.
150
+ *
151
+ * `returnUrl` is the caller's OWN `success_url` string, `{CHECKOUT_SESSION_ID}`
152
+ * placeholder and all. Stripe substitutes it on a `ui_mode: 'custom'` return
153
+ * exactly as it does on a hosted redirect (measured), so the storefront's
154
+ * post-purchase reporting — which reads `session_id` off the query to name the
155
+ * order it just completed (AGL-1641) — keeps working unchanged.
156
+ */ export function applyNativeCheckoutParams(params, returnUrl) {
157
+ params.delete('success_url');
158
+ params.delete('cancel_url');
159
+ params.set('ui_mode', 'custom');
160
+ params.set('return_url', returnUrl);
161
+ }
162
+ /**
163
+ * The `Stripe-Version` pin, as headers to spread into the session-create fetch.
164
+ * Empty for the hosted path so its request is byte-identical to today's.
165
+ */ export function nativeCheckoutStripeHeaders(mode) {
166
+ return mode.native ? {
167
+ 'Stripe-Version': NATIVE_CHECKOUT_STRIPE_VERSION
168
+ } : {};
169
+ }
170
+ /**
171
+ * Read the created session into the payload the storefront gets back.
172
+ *
173
+ * Returns `null` when the session is unusable, which BOTH callers already treat
174
+ * as "nothing was sold" — release the idempotency claim and answer 502. The
175
+ * null case is the whole reason this is a function rather than a ternary: the
176
+ * hosted path's liveness check was `!session.url`, and a `ui_mode` session
177
+ * never has one. Left unchanged, switching a store to native mode would have
178
+ * made every successful session look like a Stripe failure — a 502 to the
179
+ * shopper, a released claim, and a real Checkout Session left open on the
180
+ * account. So the check has to move with the mode, and it does.
181
+ */ export function readCheckoutSessionPayload(session, mode) {
182
+ if (!mode.native) return (session == null ? void 0 : session.url) ? {
183
+ url: session.url
184
+ } : null;
185
+ if (!(session == null ? void 0 : session.client_secret) || !(session == null ? void 0 : session.id)) return null;
186
+ return {
187
+ clientSecret: String(session.client_secret),
188
+ publishableKey: mode.publishableKey,
189
+ sessionId: String(session.id)
190
+ };
191
+ }
192
+
193
+ //# sourceMappingURL=native-checkout.js.map