@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.
- package/LICENSE +201 -0
- package/README.md +10 -0
- package/package.json +57 -0
- package/src/index.d.ts +38 -0
- package/src/index.js +38 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/account.d.ts +31 -0
- package/src/lib/components/account.js +645 -0
- package/src/lib/components/account.js.map +1 -0
- package/src/lib/components/cart.d.ts +38 -0
- package/src/lib/components/cart.js +851 -0
- package/src/lib/components/cart.js.map +1 -0
- package/src/lib/components/commerce-console-page.d.ts +32 -0
- package/src/lib/components/commerce-console-page.js +296 -0
- package/src/lib/components/commerce-console-page.js.map +1 -0
- package/src/lib/components/commerce-console-sections.d.ts +34 -0
- package/src/lib/components/commerce-console-sections.js +62 -0
- package/src/lib/components/commerce-console-sections.js.map +1 -0
- package/src/lib/components/console/catalog-organization-card.component.d.ts +14 -0
- package/src/lib/components/console/catalog-organization-card.component.js +1087 -0
- package/src/lib/components/console/catalog-organization-card.component.js.map +1 -0
- package/src/lib/components/console/commerce-analytics-card.component.d.ts +27 -0
- package/src/lib/components/console/commerce-analytics-card.component.js +310 -0
- package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -0
- package/src/lib/components/console/commerce-glance-card.component.d.ts +20 -0
- package/src/lib/components/console/commerce-glance-card.component.js +340 -0
- package/src/lib/components/console/commerce-glance-card.component.js.map +1 -0
- package/src/lib/components/console/commerce-stat-tile.component.d.ts +37 -0
- package/src/lib/components/console/commerce-stat-tile.component.js +72 -0
- package/src/lib/components/console/commerce-stat-tile.component.js.map +1 -0
- package/src/lib/components/console/discounts-card.component.d.ts +14 -0
- package/src/lib/components/console/discounts-card.component.js +443 -0
- package/src/lib/components/console/discounts-card.component.js.map +1 -0
- package/src/lib/components/console/entitlement-gate.component.d.ts +76 -0
- package/src/lib/components/console/entitlement-gate.component.js +115 -0
- package/src/lib/components/console/entitlement-gate.component.js.map +1 -0
- package/src/lib/components/console/gift-cards-card.component.d.ts +29 -0
- package/src/lib/components/console/gift-cards-card.component.js +379 -0
- package/src/lib/components/console/gift-cards-card.component.js.map +1 -0
- package/src/lib/components/console/host-coupons-card.component.d.ts +14 -0
- package/src/lib/components/console/host-coupons-card.component.js +252 -0
- package/src/lib/components/console/host-coupons-card.component.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.d.ts +13 -0
- package/src/lib/components/console/host-orders-card.component.js +861 -0
- package/src/lib/components/console/host-orders-card.component.js.map +1 -0
- package/src/lib/components/console/locations-card.component.d.ts +14 -0
- package/src/lib/components/console/locations-card.component.js +256 -0
- package/src/lib/components/console/locations-card.component.js.map +1 -0
- package/src/lib/components/console/member-posts-card.component.d.ts +8 -0
- package/src/lib/components/console/member-posts-card.component.js +283 -0
- package/src/lib/components/console/member-posts-card.component.js.map +1 -0
- package/src/lib/components/console/newest-site-users-card.component.d.ts +36 -0
- package/src/lib/components/console/newest-site-users-card.component.js +126 -0
- package/src/lib/components/console/newest-site-users-card.component.js.map +1 -0
- package/src/lib/components/console/order-detail-dialog.component.d.ts +28 -0
- package/src/lib/components/console/order-detail-dialog.component.js +976 -0
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -0
- package/src/lib/components/console/paid-media.d.ts +127 -0
- package/src/lib/components/console/paid-media.js +397 -0
- package/src/lib/components/console/paid-media.js.map +1 -0
- package/src/lib/components/console/payments-settings-card.component.d.ts +13 -0
- package/src/lib/components/console/payments-settings-card.component.js +295 -0
- package/src/lib/components/console/payments-settings-card.component.js.map +1 -0
- package/src/lib/components/console/pos-page.component.d.ts +13 -0
- package/src/lib/components/console/pos-page.component.js +916 -0
- package/src/lib/components/console/pos-page.component.js.map +1 -0
- package/src/lib/components/console/product-editor-dialog.component.d.ts +32 -0
- package/src/lib/components/console/product-editor-dialog.component.js +1374 -0
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -0
- package/src/lib/components/console/products-hub-card.component.d.ts +16 -0
- package/src/lib/components/console/products-hub-card.component.js +1280 -0
- package/src/lib/components/console/products-hub-card.component.js.map +1 -0
- package/src/lib/components/console/products-hub-zone.component.d.ts +50 -0
- package/src/lib/components/console/products-hub-zone.component.js +200 -0
- package/src/lib/components/console/products-hub-zone.component.js.map +1 -0
- package/src/lib/components/console/recovery-queue-card.component.d.ts +39 -0
- package/src/lib/components/console/recovery-queue-card.component.js +262 -0
- package/src/lib/components/console/recovery-queue-card.component.js.map +1 -0
- package/src/lib/components/console/registers-card.component.d.ts +21 -0
- package/src/lib/components/console/registers-card.component.js +296 -0
- package/src/lib/components/console/registers-card.component.js.map +1 -0
- package/src/lib/components/console/reservations-card.component.d.ts +14 -0
- package/src/lib/components/console/reservations-card.component.js +650 -0
- package/src/lib/components/console/reservations-card.component.js.map +1 -0
- package/src/lib/components/console/reviews-moderation-card.component.d.ts +8 -0
- package/src/lib/components/console/reviews-moderation-card.component.js +235 -0
- package/src/lib/components/console/reviews-moderation-card.component.js.map +1 -0
- package/src/lib/components/console/shipping-settings-card.component.d.ts +38 -0
- package/src/lib/components/console/shipping-settings-card.component.js +560 -0
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -0
- package/src/lib/components/console/stock-movements-card.component.d.ts +31 -0
- package/src/lib/components/console/stock-movements-card.component.js +333 -0
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -0
- package/src/lib/components/console/store-settings-card.component.d.ts +14 -0
- package/src/lib/components/console/store-settings-card.component.js +300 -0
- package/src/lib/components/console/store-settings-card.component.js.map +1 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.d.ts +73 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.js +408 -0
- package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -0
- package/src/lib/components/console/suppliers-card.component.d.ts +8 -0
- package/src/lib/components/console/suppliers-card.component.js +328 -0
- package/src/lib/components/console/suppliers-card.component.js.map +1 -0
- package/src/lib/components/console/tax-settings-card.component.d.ts +14 -0
- package/src/lib/components/console/tax-settings-card.component.js +473 -0
- package/src/lib/components/console/tax-settings-card.component.js.map +1 -0
- package/src/lib/components/gate.d.ts +39 -0
- package/src/lib/components/gate.js +202 -0
- package/src/lib/components/gate.js.map +1 -0
- package/src/lib/components/gated-video.d.ts +40 -0
- package/src/lib/components/gated-video.js +366 -0
- package/src/lib/components/gated-video.js.map +1 -0
- package/src/lib/components/member-feed.d.ts +32 -0
- package/src/lib/components/member-feed.js +198 -0
- package/src/lib/components/member-feed.js.map +1 -0
- package/src/lib/components/member-recovery.d.ts +35 -0
- package/src/lib/components/member-recovery.js +313 -0
- package/src/lib/components/member-recovery.js.map +1 -0
- package/src/lib/components/member-signin.d.ts +35 -0
- package/src/lib/components/member-signin.js +257 -0
- package/src/lib/components/member-signin.js.map +1 -0
- package/src/lib/components/member-signup.d.ts +34 -0
- package/src/lib/components/member-signup.js +287 -0
- package/src/lib/components/member-signup.js.map +1 -0
- package/src/lib/components/newsletter-signup.d.ts +39 -0
- package/src/lib/components/newsletter-signup.js +229 -0
- package/src/lib/components/newsletter-signup.js.map +1 -0
- package/src/lib/components/product-detail.d.ts +38 -0
- package/src/lib/components/product-detail.js +1022 -0
- package/src/lib/components/product-detail.js.map +1 -0
- package/src/lib/components/product-grid.d.ts +67 -0
- package/src/lib/components/product-grid.js +909 -0
- package/src/lib/components/product-grid.js.map +1 -0
- package/src/lib/components/product-reviews.d.ts +32 -0
- package/src/lib/components/product-reviews.js +349 -0
- package/src/lib/components/product-reviews.js.map +1 -0
- package/src/lib/components/related-products.d.ts +33 -0
- package/src/lib/components/related-products.js +232 -0
- package/src/lib/components/related-products.js.map +1 -0
- package/src/lib/components/reservation-widget.d.ts +33 -0
- package/src/lib/components/reservation-widget.js +418 -0
- package/src/lib/components/reservation-widget.js.map +1 -0
- package/src/lib/components/storefront-payment-element-fallback.d.ts +18 -0
- package/src/lib/components/storefront-payment-element-fallback.js +51 -0
- package/src/lib/components/storefront-payment-element-fallback.js.map +1 -0
- package/src/lib/components/storefront-payment-element.d.ts +78 -0
- package/src/lib/components/storefront-payment-element.js +156 -0
- package/src/lib/components/storefront-payment-element.js.map +1 -0
- package/src/lib/components/wishlist.d.ts +46 -0
- package/src/lib/components/wishlist.js +304 -0
- package/src/lib/components/wishlist.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +18 -0
- package/src/lib/constants/bundle-common.js +18 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/constants/product-filters.d.ts +35 -0
- package/src/lib/constants/product-filters.js +109 -0
- package/src/lib/constants/product-filters.js.map +1 -0
- package/src/lib/model/commerce-cart.d.ts +52 -0
- package/src/lib/model/commerce-cart.js +102 -0
- package/src/lib/model/commerce-cart.js.map +1 -0
- package/src/lib/model/commerce-connect-transfer.d.ts +243 -0
- package/src/lib/model/commerce-connect-transfer.js +231 -0
- package/src/lib/model/commerce-connect-transfer.js.map +1 -0
- package/src/lib/model/commerce-discounts.d.ts +164 -0
- package/src/lib/model/commerce-discounts.js +231 -0
- package/src/lib/model/commerce-discounts.js.map +1 -0
- package/src/lib/model/commerce-dispute.d.ts +124 -0
- package/src/lib/model/commerce-dispute.js +225 -0
- package/src/lib/model/commerce-dispute.js.map +1 -0
- package/src/lib/model/commerce-gift-cards.d.ts +107 -0
- package/src/lib/model/commerce-gift-cards.js +121 -0
- package/src/lib/model/commerce-gift-cards.js.map +1 -0
- package/src/lib/model/commerce-io.d.ts +36 -0
- package/src/lib/model/commerce-io.js +245 -0
- package/src/lib/model/commerce-io.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +1040 -0
- package/src/lib/model/commerce-orders.js +1022 -0
- package/src/lib/model/commerce-orders.js.map +1 -0
- package/src/lib/model/commerce-promotions.d.ts +188 -0
- package/src/lib/model/commerce-promotions.js +199 -0
- package/src/lib/model/commerce-promotions.js.map +1 -0
- package/src/lib/model/commerce-reservations.d.ts +175 -0
- package/src/lib/model/commerce-reservations.js +157 -0
- package/src/lib/model/commerce-reservations.js.map +1 -0
- package/src/lib/model/commerce-shipping.d.ts +316 -0
- package/src/lib/model/commerce-shipping.js +484 -0
- package/src/lib/model/commerce-shipping.js.map +1 -0
- package/src/lib/model/commerce-stock-holds.d.ts +199 -0
- package/src/lib/model/commerce-stock-holds.js +190 -0
- package/src/lib/model/commerce-stock-holds.js.map +1 -0
- package/src/lib/model/commerce-subscription-status.d.ts +67 -0
- package/src/lib/model/commerce-subscription-status.js +76 -0
- package/src/lib/model/commerce-subscription-status.js.map +1 -0
- package/src/lib/model/commerce-tax-decision.d.ts +222 -0
- package/src/lib/model/commerce-tax-decision.js +165 -0
- package/src/lib/model/commerce-tax-decision.js.map +1 -0
- package/src/lib/model/commerce-tax.d.ts +176 -0
- package/src/lib/model/commerce-tax.js +92 -0
- package/src/lib/model/commerce-tax.js.map +1 -0
- package/src/lib/model/commerce.d.ts +551 -0
- package/src/lib/model/commerce.js +739 -0
- package/src/lib/model/commerce.js.map +1 -0
- package/src/lib/model/index.d.ts +40 -0
- package/src/lib/model/index.js +40 -0
- package/src/lib/model/index.js.map +1 -0
- package/src/lib/model/order-figures.d.ts +48 -0
- package/src/lib/model/order-figures.js +74 -0
- package/src/lib/model/order-figures.js.map +1 -0
- package/src/lib/model/plugin-permissions.d.ts +23 -0
- package/src/lib/model/plugin-permissions.js +34 -0
- package/src/lib/model/plugin-permissions.js.map +1 -0
- package/src/lib/model/purchase-analytics.d.ts +186 -0
- package/src/lib/model/purchase-analytics.js +216 -0
- package/src/lib/model/purchase-analytics.js.map +1 -0
- package/src/lib/plugin-config.d.ts +62 -0
- package/src/lib/plugin-config.js +78 -0
- package/src/lib/plugin-config.js.map +1 -0
- package/src/lib/plugin.d.ts +28 -0
- package/src/lib/plugin.js +147 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server/billing-webhook.d.ts +26 -0
- package/src/lib/server/billing-webhook.js +4011 -0
- package/src/lib/server/billing-webhook.js.map +1 -0
- package/src/lib/server/cancel-order.d.ts +87 -0
- package/src/lib/server/cancel-order.js +368 -0
- package/src/lib/server/cancel-order.js.map +1 -0
- package/src/lib/server/cart-checkout.d.ts +25 -0
- package/src/lib/server/cart-checkout.js +976 -0
- package/src/lib/server/cart-checkout.js.map +1 -0
- package/src/lib/server/cart-cookie.d.ts +32 -0
- package/src/lib/server/cart-cookie.js +77 -0
- package/src/lib/server/cart-cookie.js.map +1 -0
- package/src/lib/server/cart.d.ts +34 -0
- package/src/lib/server/cart.js +181 -0
- package/src/lib/server/cart.js.map +1 -0
- package/src/lib/server/catalog.d.ts +127 -0
- package/src/lib/server/catalog.js +277 -0
- package/src/lib/server/catalog.js.map +1 -0
- package/src/lib/server/checkout.d.ts +36 -0
- package/src/lib/server/checkout.js +915 -0
- package/src/lib/server/checkout.js.map +1 -0
- package/src/lib/server/connect.d.ts +26 -0
- package/src/lib/server/connect.js +238 -0
- package/src/lib/server/connect.js.map +1 -0
- package/src/lib/server/contact-refund.d.ts +117 -0
- package/src/lib/server/contact-refund.js +196 -0
- package/src/lib/server/contact-refund.js.map +1 -0
- package/src/lib/server/download.d.ts +39 -0
- package/src/lib/server/download.js +206 -0
- package/src/lib/server/download.js.map +1 -0
- package/src/lib/server/draft-order.d.ts +26 -0
- package/src/lib/server/draft-order.js +706 -0
- package/src/lib/server/draft-order.js.map +1 -0
- package/src/lib/server/feed.d.ts +24 -0
- package/src/lib/server/feed.js +59 -0
- package/src/lib/server/feed.js.map +1 -0
- package/src/lib/server/fulfill-order.d.ts +97 -0
- package/src/lib/server/fulfill-order.js +235 -0
- package/src/lib/server/fulfill-order.js.map +1 -0
- package/src/lib/server/gate.d.ts +36 -0
- package/src/lib/server/gate.js +112 -0
- package/src/lib/server/gate.js.map +1 -0
- package/src/lib/server/gift-cards.d.ts +42 -0
- package/src/lib/server/gift-cards.js +219 -0
- package/src/lib/server/gift-cards.js.map +1 -0
- package/src/lib/server/low-stock.d.ts +49 -0
- package/src/lib/server/low-stock.js +60 -0
- package/src/lib/server/low-stock.js.map +1 -0
- package/src/lib/server/manual-tax-rate.d.ts +82 -0
- package/src/lib/server/manual-tax-rate.js +113 -0
- package/src/lib/server/manual-tax-rate.js.map +1 -0
- package/src/lib/server/member-feed.d.ts +24 -0
- package/src/lib/server/member-feed.js +72 -0
- package/src/lib/server/member-feed.js.map +1 -0
- package/src/lib/server/member-name-search.d.ts +37 -0
- package/src/lib/server/member-name-search.js +42 -0
- package/src/lib/server/member-name-search.js.map +1 -0
- package/src/lib/server/member-post.d.ts +23 -0
- package/src/lib/server/member-post.js +166 -0
- package/src/lib/server/member-post.js.map +1 -0
- package/src/lib/server/membership-account.d.ts +37 -0
- package/src/lib/server/membership-account.js +148 -0
- package/src/lib/server/membership-account.js.map +1 -0
- package/src/lib/server/membership-admin-password.d.ts +33 -0
- package/src/lib/server/membership-admin-password.js +202 -0
- package/src/lib/server/membership-admin-password.js.map +1 -0
- package/src/lib/server/membership-content.d.ts +23 -0
- package/src/lib/server/membership-content.js +105 -0
- package/src/lib/server/membership-content.js.map +1 -0
- package/src/lib/server/membership-login.d.ts +19 -0
- package/src/lib/server/membership-login.js +137 -0
- package/src/lib/server/membership-login.js.map +1 -0
- package/src/lib/server/membership-logout.d.ts +19 -0
- package/src/lib/server/membership-logout.js +35 -0
- package/src/lib/server/membership-logout.js.map +1 -0
- package/src/lib/server/membership-recover.d.ts +60 -0
- package/src/lib/server/membership-recover.js +244 -0
- package/src/lib/server/membership-recover.js.map +1 -0
- package/src/lib/server/membership-register.d.ts +22 -0
- package/src/lib/server/membership-register.js +241 -0
- package/src/lib/server/membership-register.js.map +1 -0
- package/src/lib/server/membership-reset.d.ts +26 -0
- package/src/lib/server/membership-reset.js +88 -0
- package/src/lib/server/membership-reset.js.map +1 -0
- package/src/lib/server/membership-wishlist.d.ts +23 -0
- package/src/lib/server/membership-wishlist.js +89 -0
- package/src/lib/server/membership-wishlist.js.map +1 -0
- package/src/lib/server/membership.d.ts +96 -0
- package/src/lib/server/membership.js +222 -0
- package/src/lib/server/membership.js.map +1 -0
- package/src/lib/server/native-checkout.d.ts +170 -0
- package/src/lib/server/native-checkout.js +193 -0
- package/src/lib/server/native-checkout.js.map +1 -0
- package/src/lib/server/newsletter.d.ts +23 -0
- package/src/lib/server/newsletter.js +323 -0
- package/src/lib/server/newsletter.js.map +1 -0
- package/src/lib/server/notify-restock.d.ts +22 -0
- package/src/lib/server/notify-restock.js +84 -0
- package/src/lib/server/notify-restock.js.map +1 -0
- package/src/lib/server/order-analytics.d.ts +134 -0
- package/src/lib/server/order-analytics.js +263 -0
- package/src/lib/server/order-analytics.js.map +1 -0
- package/src/lib/server/order-figures.d.ts +33 -0
- package/src/lib/server/order-figures.js +220 -0
- package/src/lib/server/order-figures.js.map +1 -0
- package/src/lib/server/paid-media-delivery.d.ts +173 -0
- package/src/lib/server/paid-media-delivery.js +212 -0
- package/src/lib/server/paid-media-delivery.js.map +1 -0
- package/src/lib/server/pos-order.d.ts +54 -0
- package/src/lib/server/pos-order.js +1137 -0
- package/src/lib/server/pos-order.js.map +1 -0
- package/src/lib/server/process-abandoned.d.ts +54 -0
- package/src/lib/server/process-abandoned.js +222 -0
- package/src/lib/server/process-abandoned.js.map +1 -0
- package/src/lib/server/process-restock.d.ts +42 -0
- package/src/lib/server/process-restock.js +219 -0
- package/src/lib/server/process-restock.js.map +1 -0
- package/src/lib/server/product-card.d.ts +29 -0
- package/src/lib/server/product-card.js +54 -0
- package/src/lib/server/product-card.js.map +1 -0
- package/src/lib/server/product.d.ts +70 -0
- package/src/lib/server/product.js +105 -0
- package/src/lib/server/product.js.map +1 -0
- package/src/lib/server/promotion-hold.d.ts +144 -0
- package/src/lib/server/promotion-hold.js +174 -0
- package/src/lib/server/promotion-hold.js.map +1 -0
- package/src/lib/server/reconcile-stock.d.ts +106 -0
- package/src/lib/server/reconcile-stock.js +349 -0
- package/src/lib/server/reconcile-stock.js.map +1 -0
- package/src/lib/server/refund.d.ts +45 -0
- package/src/lib/server/refund.js +607 -0
- package/src/lib/server/refund.js.map +1 -0
- package/src/lib/server/related.d.ts +24 -0
- package/src/lib/server/related.js +104 -0
- package/src/lib/server/related.js.map +1 -0
- package/src/lib/server/reservation-availability.d.ts +23 -0
- package/src/lib/server/reservation-availability.js +111 -0
- package/src/lib/server/reservation-availability.js.map +1 -0
- package/src/lib/server/reserve-stock.d.ts +156 -0
- package/src/lib/server/reserve-stock.js +146 -0
- package/src/lib/server/reserve-stock.js.map +1 -0
- package/src/lib/server/reserve.d.ts +25 -0
- package/src/lib/server/reserve.js +426 -0
- package/src/lib/server/reserve.js.map +1 -0
- package/src/lib/server/restock-flag.d.ts +202 -0
- package/src/lib/server/restock-flag.js +449 -0
- package/src/lib/server/restock-flag.js.map +1 -0
- package/src/lib/server/reviews.d.ts +51 -0
- package/src/lib/server/reviews.js +132 -0
- package/src/lib/server/reviews.js.map +1 -0
- package/src/lib/server/site-page-enricher.d.ts +42 -0
- package/src/lib/server/site-page-enricher.js +149 -0
- package/src/lib/server/site-page-enricher.js.map +1 -0
- package/src/lib/server/site-page-resolver.d.ts +26 -0
- package/src/lib/server/site-page-resolver.js +223 -0
- package/src/lib/server/site-page-resolver.js.map +1 -0
- package/src/lib/server/stock-hold.d.ts +127 -0
- package/src/lib/server/stock-hold.js +258 -0
- package/src/lib/server/stock-hold.js.map +1 -0
- package/src/lib/server/storefront-tax-record.d.ts +22 -0
- package/src/lib/server/storefront-tax-record.js +218 -0
- package/src/lib/server/storefront-tax-record.js.map +1 -0
- package/src/lib/server/storefront-tax.d.ts +197 -0
- package/src/lib/server/storefront-tax.js +245 -0
- package/src/lib/server/storefront-tax.js.map +1 -0
- package/src/lib/server/stream.d.ts +25 -0
- package/src/lib/server/stream.js +191 -0
- package/src/lib/server/stream.js.map +1 -0
- package/src/lib/server/subscription-portal.d.ts +24 -0
- package/src/lib/server/subscription-portal.js +91 -0
- package/src/lib/server/subscription-portal.js.map +1 -0
- package/src/lib/server/supplier-outbox.d.ts +190 -0
- package/src/lib/server/supplier-outbox.js +342 -0
- package/src/lib/server/supplier-outbox.js.map +1 -0
- package/src/lib/server/supplier-update.d.ts +23 -0
- package/src/lib/server/supplier-update.js +289 -0
- package/src/lib/server/supplier-update.js.map +1 -0
- package/src/lib/server/tax-profile.d.ts +29 -0
- package/src/lib/server/tax-profile.js +40 -0
- package/src/lib/server/tax-profile.js.map +1 -0
- package/src/lib/server.d.ts +32 -0
- package/src/lib/server.js +311 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/site.d.ts +27 -0
- package/src/lib/site.js +134 -0
- package/src/lib/site.js.map +1 -0
- package/src/lib/utils/escape-html.d.ts +41 -0
- package/src/lib/utils/escape-html.js +41 -0
- package/src/lib/utils/escape-html.js.map +1 -0
- package/src/lib/utils/generate-preset-id.d.ts +19 -0
- package/src/lib/utils/generate-preset-id.js +25 -0
- package/src/lib/utils/generate-preset-id.js.map +1 -0
- package/src/lib/utils/member-continue.d.ts +45 -0
- package/src/lib/utils/member-continue.js +49 -0
- package/src/lib/utils/member-continue.js.map +1 -0
- package/src/lib/utils/use-storefront-purchase-event.d.ts +60 -0
- package/src/lib/utils/use-storefront-purchase-event.js +142 -0
- package/src/lib/utils/use-storefront-purchase-event.js.map +1 -0
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
* A merchant's `manual`-mode tax as a real Stripe Tax Rate (AGL-1953).
|
|
19
|
+
*
|
|
20
|
+
* ## Why this exists as its own module
|
|
21
|
+
*
|
|
22
|
+
* AGL-1751 built exactly this — mint a Tax Rate on the platform account, cache
|
|
23
|
+
* it under the host — inline in `checkout.ts`, for subscriptions. AGL-1953
|
|
24
|
+
* needed the same thing for the cart and draft-order paths, and a second copy
|
|
25
|
+
* of a money-shaped cache is how two paths quietly diverge. One implementation,
|
|
26
|
+
* three callers.
|
|
27
|
+
*
|
|
28
|
+
* ## Why a TAX RATE and not another line item
|
|
29
|
+
*
|
|
30
|
+
* Buy-now's one-time construction (AGL-1711) appends the tax as an ordinary
|
|
31
|
+
* `line_items[1]` product line Stripe is never told is tax. That works there
|
|
32
|
+
* because buy-now prices its discount INTO the unit amount, so nothing at
|
|
33
|
+
* session level can touch the tax line afterwards.
|
|
34
|
+
*
|
|
35
|
+
* **The cart cannot use it.** The cart applies its discounts, coupons and gift
|
|
36
|
+
* cards as real Stripe coupons at `discounts[0][coupon]`, and a session-level
|
|
37
|
+
* coupon is spread across every line — including a fake tax line. The tax would
|
|
38
|
+
* be discounted along with the goods, and the `metadata[taxCents]` snapshot
|
|
39
|
+
* would then over-state what was actually collected.
|
|
40
|
+
*
|
|
41
|
+
* A real tax rate is applied by Stripe AFTER the discount, which is the
|
|
42
|
+
* behaviour we want and which was MEASURED rather than assumed. Test mode, a
|
|
43
|
+
* $100.00 line carrying an 8.25% rate plus a $30.00 `amount_off` coupon:
|
|
44
|
+
*
|
|
45
|
+
* amount_subtotal 10000, amount_discount 3000, **amount_tax 578**,
|
|
46
|
+
* breakdown.taxes[0].taxable_amount **7000**
|
|
47
|
+
*
|
|
48
|
+
* 578 is 8.25% of the discounted 7000, not of the 10000 subtotal. Stripe
|
|
49
|
+
* discounts first and taxes the remainder, so the arithmetic stays right with
|
|
50
|
+
* no snapshot to keep in step — and `total_details.amount_tax` becomes a real
|
|
51
|
+
* field, so the order's own decomposition needs no metadata witness at all.
|
|
52
|
+
*
|
|
53
|
+
* ## This does NOT make the sale Stripe Tax
|
|
54
|
+
*
|
|
55
|
+
* `automatic_tax.enabled` stays `false` (confirmed on the same measured
|
|
56
|
+
* session), which is the discriminator `storefront-tax.ts` reads. A rate minted
|
|
57
|
+
* here is the merchant's own configured percentage against their own declared
|
|
58
|
+
* origin; Aglyn's registrations never enter into it and the money is not
|
|
59
|
+
* Aglyn's to remit. Reading tax-LINE presence instead of the flag would book
|
|
60
|
+
* this as Aglyn-collected, which is the AGL-1904 trap stated in that module.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* The rate id for (percentage, label) on this host, minting and caching one on
|
|
64
|
+
* first use. `null` means Stripe refused — the caller decides what that costs,
|
|
65
|
+
* and for every current caller it is a visible 502 rather than a silent
|
|
66
|
+
* untaxed sale.
|
|
67
|
+
*
|
|
68
|
+
* The rate is created on the PLATFORM account (no `Stripe-Account` header),
|
|
69
|
+
* matching the session calls, because that is where the session lives — a
|
|
70
|
+
* connected-account rate id would not resolve. Tax rates are immutable, so one
|
|
71
|
+
* object exists per (percentage, label): a merchant who edits their zone rate
|
|
72
|
+
* misses the cache and mints a fresh one, while the old object stays behind on
|
|
73
|
+
* everything already carrying it, exactly as it should.
|
|
74
|
+
*/
|
|
75
|
+
export declare function resolveManualTaxRateId(options: {
|
|
76
|
+
/** `hosts/{hostId}` — the cache lives under it. */
|
|
77
|
+
hostRef: any;
|
|
78
|
+
taxPct: number;
|
|
79
|
+
taxLabel: string;
|
|
80
|
+
/** Per-object idempotency header, derived by the caller (AGL-1714). */
|
|
81
|
+
headers?: Record<string, string>;
|
|
82
|
+
}): Promise<string | null>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ /**
|
|
18
|
+
* A merchant's `manual`-mode tax as a real Stripe Tax Rate (AGL-1953).
|
|
19
|
+
*
|
|
20
|
+
* ## Why this exists as its own module
|
|
21
|
+
*
|
|
22
|
+
* AGL-1751 built exactly this — mint a Tax Rate on the platform account, cache
|
|
23
|
+
* it under the host — inline in `checkout.ts`, for subscriptions. AGL-1953
|
|
24
|
+
* needed the same thing for the cart and draft-order paths, and a second copy
|
|
25
|
+
* of a money-shaped cache is how two paths quietly diverge. One implementation,
|
|
26
|
+
* three callers.
|
|
27
|
+
*
|
|
28
|
+
* ## Why a TAX RATE and not another line item
|
|
29
|
+
*
|
|
30
|
+
* Buy-now's one-time construction (AGL-1711) appends the tax as an ordinary
|
|
31
|
+
* `line_items[1]` product line Stripe is never told is tax. That works there
|
|
32
|
+
* because buy-now prices its discount INTO the unit amount, so nothing at
|
|
33
|
+
* session level can touch the tax line afterwards.
|
|
34
|
+
*
|
|
35
|
+
* **The cart cannot use it.** The cart applies its discounts, coupons and gift
|
|
36
|
+
* cards as real Stripe coupons at `discounts[0][coupon]`, and a session-level
|
|
37
|
+
* coupon is spread across every line — including a fake tax line. The tax would
|
|
38
|
+
* be discounted along with the goods, and the `metadata[taxCents]` snapshot
|
|
39
|
+
* would then over-state what was actually collected.
|
|
40
|
+
*
|
|
41
|
+
* A real tax rate is applied by Stripe AFTER the discount, which is the
|
|
42
|
+
* behaviour we want and which was MEASURED rather than assumed. Test mode, a
|
|
43
|
+
* $100.00 line carrying an 8.25% rate plus a $30.00 `amount_off` coupon:
|
|
44
|
+
*
|
|
45
|
+
* amount_subtotal 10000, amount_discount 3000, **amount_tax 578**,
|
|
46
|
+
* breakdown.taxes[0].taxable_amount **7000**
|
|
47
|
+
*
|
|
48
|
+
* 578 is 8.25% of the discounted 7000, not of the 10000 subtotal. Stripe
|
|
49
|
+
* discounts first and taxes the remainder, so the arithmetic stays right with
|
|
50
|
+
* no snapshot to keep in step — and `total_details.amount_tax` becomes a real
|
|
51
|
+
* field, so the order's own decomposition needs no metadata witness at all.
|
|
52
|
+
*
|
|
53
|
+
* ## This does NOT make the sale Stripe Tax
|
|
54
|
+
*
|
|
55
|
+
* `automatic_tax.enabled` stays `false` (confirmed on the same measured
|
|
56
|
+
* session), which is the discriminator `storefront-tax.ts` reads. A rate minted
|
|
57
|
+
* here is the merchant's own configured percentage against their own declared
|
|
58
|
+
* origin; Aglyn's registrations never enter into it and the money is not
|
|
59
|
+
* Aglyn's to remit. Reading tax-LINE presence instead of the flag would book
|
|
60
|
+
* this as Aglyn-collected, which is the AGL-1904 trap stated in that module.
|
|
61
|
+
*/ /**
|
|
62
|
+
* The rate id for (percentage, label) on this host, minting and caching one on
|
|
63
|
+
* first use. `null` means Stripe refused — the caller decides what that costs,
|
|
64
|
+
* and for every current caller it is a visible 502 rather than a silent
|
|
65
|
+
* untaxed sale.
|
|
66
|
+
*
|
|
67
|
+
* The rate is created on the PLATFORM account (no `Stripe-Account` header),
|
|
68
|
+
* matching the session calls, because that is where the session lives — a
|
|
69
|
+
* connected-account rate id would not resolve. Tax rates are immutable, so one
|
|
70
|
+
* object exists per (percentage, label): a merchant who edits their zone rate
|
|
71
|
+
* misses the cache and mints a fresh one, while the old object stays behind on
|
|
72
|
+
* everything already carrying it, exactly as it should.
|
|
73
|
+
*/ export async function resolveManualTaxRateId(options) {
|
|
74
|
+
var _cached_get;
|
|
75
|
+
const { hostRef, taxPct, taxLabel, headers = {} } = options;
|
|
76
|
+
if (!(taxPct > 0)) return null;
|
|
77
|
+
// ≤4 decimal places, which is all Stripe accepts on a tax rate.
|
|
78
|
+
const percentage = Math.round(taxPct * 10000) / 10000;
|
|
79
|
+
const displayLabel = (taxLabel || `Tax (${percentage}%)`).slice(0, 50);
|
|
80
|
+
const cacheKey = `p${Math.round(percentage * 10000)}-${encodeURIComponent(displayLabel)}`;
|
|
81
|
+
const cacheRef = hostRef.collection('stripeTaxRates').doc(cacheKey);
|
|
82
|
+
const cached = await cacheRef.get();
|
|
83
|
+
const cachedId = String((_cached_get = cached.get('taxRateId')) != null ? _cached_get : '');
|
|
84
|
+
if (cachedId) return cachedId;
|
|
85
|
+
const response = await fetch('https://api.stripe.com/v1/tax_rates', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: _extends({
|
|
88
|
+
Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
|
|
89
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
90
|
+
}, headers),
|
|
91
|
+
body: new URLSearchParams({
|
|
92
|
+
display_name: displayLabel,
|
|
93
|
+
percentage: String(percentage),
|
|
94
|
+
inclusive: 'false'
|
|
95
|
+
}).toString()
|
|
96
|
+
});
|
|
97
|
+
const taxRate = await response.json();
|
|
98
|
+
if (!response.ok || !taxRate.id) {
|
|
99
|
+
console.error('Stripe tax rate error', taxRate.error);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// Best-effort: the rate exists either way, and two concurrent misses racing
|
|
103
|
+
// this write just means one orphan rate on the dashboard.
|
|
104
|
+
await cacheRef.set({
|
|
105
|
+
taxRateId: String(taxRate.id),
|
|
106
|
+
pct: percentage,
|
|
107
|
+
label: displayLabel,
|
|
108
|
+
createdAtMs: Date.now()
|
|
109
|
+
}).catch(()=>undefined);
|
|
110
|
+
return String(taxRate.id);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=manual-tax-rate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/manual-tax-rate.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 * A merchant's `manual`-mode tax as a real Stripe Tax Rate (AGL-1953).\n *\n * ## Why this exists as its own module\n *\n * AGL-1751 built exactly this — mint a Tax Rate on the platform account, cache\n * it under the host — inline in `checkout.ts`, for subscriptions. AGL-1953\n * needed the same thing for the cart and draft-order paths, and a second copy\n * of a money-shaped cache is how two paths quietly diverge. One implementation,\n * three callers.\n *\n * ## Why a TAX RATE and not another line item\n *\n * Buy-now's one-time construction (AGL-1711) appends the tax as an ordinary\n * `line_items[1]` product line Stripe is never told is tax. That works there\n * because buy-now prices its discount INTO the unit amount, so nothing at\n * session level can touch the tax line afterwards.\n *\n * **The cart cannot use it.** The cart applies its discounts, coupons and gift\n * cards as real Stripe coupons at `discounts[0][coupon]`, and a session-level\n * coupon is spread across every line — including a fake tax line. The tax would\n * be discounted along with the goods, and the `metadata[taxCents]` snapshot\n * would then over-state what was actually collected.\n *\n * A real tax rate is applied by Stripe AFTER the discount, which is the\n * behaviour we want and which was MEASURED rather than assumed. Test mode, a\n * $100.00 line carrying an 8.25% rate plus a $30.00 `amount_off` coupon:\n *\n * amount_subtotal 10000, amount_discount 3000, **amount_tax 578**,\n * breakdown.taxes[0].taxable_amount **7000**\n *\n * 578 is 8.25% of the discounted 7000, not of the 10000 subtotal. Stripe\n * discounts first and taxes the remainder, so the arithmetic stays right with\n * no snapshot to keep in step — and `total_details.amount_tax` becomes a real\n * field, so the order's own decomposition needs no metadata witness at all.\n *\n * ## This does NOT make the sale Stripe Tax\n *\n * `automatic_tax.enabled` stays `false` (confirmed on the same measured\n * session), which is the discriminator `storefront-tax.ts` reads. A rate minted\n * here is the merchant's own configured percentage against their own declared\n * origin; Aglyn's registrations never enter into it and the money is not\n * Aglyn's to remit. Reading tax-LINE presence instead of the flag would book\n * this as Aglyn-collected, which is the AGL-1904 trap stated in that module.\n */\n\n/**\n * The rate id for (percentage, label) on this host, minting and caching one on\n * first use. `null` means Stripe refused — the caller decides what that costs,\n * and for every current caller it is a visible 502 rather than a silent\n * untaxed sale.\n *\n * The rate is created on the PLATFORM account (no `Stripe-Account` header),\n * matching the session calls, because that is where the session lives — a\n * connected-account rate id would not resolve. Tax rates are immutable, so one\n * object exists per (percentage, label): a merchant who edits their zone rate\n * misses the cache and mints a fresh one, while the old object stays behind on\n * everything already carrying it, exactly as it should.\n */\nexport async function resolveManualTaxRateId(options: {\n /** `hosts/{hostId}` — the cache lives under it. */\n hostRef: any\n taxPct: number\n taxLabel: string\n /** Per-object idempotency header, derived by the caller (AGL-1714). */\n headers?: Record<string, string>\n}): Promise<string | null> {\n const { hostRef, taxPct, taxLabel, headers = {} } = options\n if (!(taxPct > 0)) return null\n // ≤4 decimal places, which is all Stripe accepts on a tax rate.\n const percentage = Math.round(taxPct * 10000) / 10000\n const displayLabel = (taxLabel || `Tax (${percentage}%)`).slice(0, 50)\n const cacheKey = `p${Math.round(percentage * 10000)}-${encodeURIComponent(\n displayLabel,\n )}`\n const cacheRef = hostRef.collection('stripeTaxRates').doc(cacheKey)\n const cached = await cacheRef.get()\n const cachedId = String(cached.get('taxRateId') ?? '')\n if (cachedId) return cachedId\n\n const response = await fetch('https://api.stripe.com/v1/tax_rates', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,\n 'Content-Type': 'application/x-www-form-urlencoded',\n ...headers,\n },\n body: new URLSearchParams({\n display_name: displayLabel,\n percentage: String(percentage),\n inclusive: 'false',\n }).toString(),\n })\n const taxRate = (await response.json()) as { id?: string; error?: any }\n if (!response.ok || !taxRate.id) {\n console.error('Stripe tax rate error', taxRate.error)\n return null\n }\n // Best-effort: the rate exists either way, and two concurrent misses racing\n // this write just means one orphan rate on the dashboard.\n await cacheRef\n .set({\n taxRateId: String(taxRate.id),\n pct: percentage,\n label: displayLabel,\n createdAtMs: Date.now(),\n })\n .catch(() => undefined)\n return String(taxRate.id)\n}\n"],"names":["resolveManualTaxRateId","options","cached","hostRef","taxPct","taxLabel","headers","percentage","Math","round","displayLabel","slice","cacheKey","encodeURIComponent","cacheRef","collection","doc","get","cachedId","String","response","fetch","method","Authorization","process","env","STRIPE_SECRET_KEY","body","URLSearchParams","display_name","inclusive","toString","taxRate","json","ok","id","console","error","set","taxRateId","pct","label","createdAtMs","Date","now","catch","undefined"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CC,GAED;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeA,uBAAuBC,OAO5C;QAWyBC;IAVxB,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,EAAEC,UAAU,CAAC,CAAC,EAAE,GAAGL;IACpD,IAAI,CAAEG,CAAAA,SAAS,CAAA,GAAI,OAAO;IAC1B,gEAAgE;IAChE,MAAMG,aAAaC,KAAKC,KAAK,CAACL,SAAS,SAAS;IAChD,MAAMM,eAAe,AAACL,CAAAA,YAAY,CAAC,KAAK,EAAEE,WAAW,EAAE,CAAC,AAAD,EAAGI,KAAK,CAAC,GAAG;IACnE,MAAMC,WAAW,CAAC,CAAC,EAAEJ,KAAKC,KAAK,CAACF,aAAa,OAAO,CAAC,EAAEM,mBACrDH,eACC;IACH,MAAMI,WAAWX,QAAQY,UAAU,CAAC,kBAAkBC,GAAG,CAACJ;IAC1D,MAAMV,SAAS,MAAMY,SAASG,GAAG;IACjC,MAAMC,WAAWC,QAAOjB,cAAAA,OAAOe,GAAG,CAAC,wBAAXf,cAA2B;IACnD,IAAIgB,UAAU,OAAOA;IAErB,MAAME,WAAW,MAAMC,MAAM,uCAAuC;QAClEC,QAAQ;QACRhB,SAAS;YACPiB,eAAe,CAAC,OAAO,EAAEC,QAAQC,GAAG,CAACC,iBAAiB,EAAE;YACxD,gBAAgB;WACbpB;QAELqB,MAAM,IAAIC,gBAAgB;YACxBC,cAAcnB;YACdH,YAAYY,OAAOZ;YACnBuB,WAAW;QACb,GAAGC,QAAQ;IACb;IACA,MAAMC,UAAW,MAAMZ,SAASa,IAAI;IACpC,IAAI,CAACb,SAASc,EAAE,IAAI,CAACF,QAAQG,EAAE,EAAE;QAC/BC,QAAQC,KAAK,CAAC,yBAAyBL,QAAQK,KAAK;QACpD,OAAO;IACT;IACA,4EAA4E;IAC5E,0DAA0D;IAC1D,MAAMvB,SACHwB,GAAG,CAAC;QACHC,WAAWpB,OAAOa,QAAQG,EAAE;QAC5BK,KAAKjC;QACLkC,OAAO/B;QACPgC,aAAaC,KAAKC,GAAG;IACvB,GACCC,KAAK,CAAC,IAAMC;IACf,OAAO3B,OAAOa,QAAQG,EAAE;AAC1B"}
|
|
@@ -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
|
+
* Member feed (AGL-316): posts the signed-in member is entitled to —
|
|
20
|
+
* unscoped posts need any live subscription; product-scoped posts need
|
|
21
|
+
* that product's entitlement. Server-enforced; bodies never reach
|
|
22
|
+
* non-members.
|
|
23
|
+
*/
|
|
24
|
+
export declare const memberFeedHandler: PluginApiHandler;
|
|
@@ -0,0 +1,72 @@
|
|
|
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 { firebaseAdmin } from "@aglyn/tenant-data-admin";
|
|
17
|
+
import { requireActiveMember } from "./membership.js";
|
|
18
|
+
import { checkMemberEntitlement } from "./gate.js";
|
|
19
|
+
/**
|
|
20
|
+
* Member feed (AGL-316): posts the signed-in member is entitled to —
|
|
21
|
+
* unscoped posts need any live subscription; product-scoped posts need
|
|
22
|
+
* that product's entitlement. Server-enforced; bodies never reach
|
|
23
|
+
* non-members.
|
|
24
|
+
*/ export const memberFeedHandler = async (req, res)=>{
|
|
25
|
+
var _req_query_hostId;
|
|
26
|
+
const hostId = String((_req_query_hostId = req.query.hostId) != null ? _req_query_hostId : '');
|
|
27
|
+
if (!hostId) return res.status(400).json({
|
|
28
|
+
error: 'Missing hostId'
|
|
29
|
+
});
|
|
30
|
+
try {
|
|
31
|
+
var _auth_member_get;
|
|
32
|
+
// Suspension gate (AGL-550): a suspended member's cookie (AGL-546)
|
|
33
|
+
// no longer reads the feed — 403'd with the cookie cleared.
|
|
34
|
+
const auth = await requireActiveMember(req, res, hostId, 'Sign in first');
|
|
35
|
+
if (!auth) return;
|
|
36
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
37
|
+
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
38
|
+
const email = String((_auth_member_get = auth.member.get('email')) != null ? _auth_member_get : '');
|
|
39
|
+
if (!email) return res.status(401).json({
|
|
40
|
+
error: 'Sign in first'
|
|
41
|
+
});
|
|
42
|
+
const postsSnapshot = await hostRef.collection('memberPosts').limit(50).get();
|
|
43
|
+
const entitlementCache = new Map();
|
|
44
|
+
const posts = [];
|
|
45
|
+
for (const docSnapshot of postsSnapshot.docs){
|
|
46
|
+
var _docSnapshot_get, _docSnapshot_get1, _docSnapshot_get2, _docSnapshot_get3;
|
|
47
|
+
const scope = String((_docSnapshot_get = docSnapshot.get('productId')) != null ? _docSnapshot_get : '') || 'any';
|
|
48
|
+
if (!entitlementCache.has(scope)) {
|
|
49
|
+
entitlementCache.set(scope, await checkMemberEntitlement(hostId, email, scope));
|
|
50
|
+
}
|
|
51
|
+
if (!entitlementCache.get(scope)) continue;
|
|
52
|
+
posts.push({
|
|
53
|
+
id: docSnapshot.id,
|
|
54
|
+
title: String((_docSnapshot_get1 = docSnapshot.get('title')) != null ? _docSnapshot_get1 : ''),
|
|
55
|
+
body: String((_docSnapshot_get2 = docSnapshot.get('body')) != null ? _docSnapshot_get2 : ''),
|
|
56
|
+
createdAtMs: Number((_docSnapshot_get3 = docSnapshot.get('createdAtMs')) != null ? _docSnapshot_get3 : 0)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
posts.sort((a, b)=>b.createdAtMs - a.createdAtMs);
|
|
60
|
+
res.setHeader('Cache-Control', 'private, no-store');
|
|
61
|
+
return res.status(200).json({
|
|
62
|
+
posts
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error(error);
|
|
66
|
+
return res.status(500).json({
|
|
67
|
+
error: 'Feed unavailable'
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=member-feed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/member-feed.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PluginApiHandler } from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { requireActiveMember } from './membership'\nimport { checkMemberEntitlement } from './gate'\n\n/**\n * Member feed (AGL-316): posts the signed-in member is entitled to —\n * unscoped posts need any live subscription; product-scoped posts need\n * that product's entitlement. Server-enforced; bodies never reach\n * non-members.\n */\nexport const memberFeedHandler: PluginApiHandler = async (req, res) => {\n const hostId = String(req.query.hostId ?? '')\n if (!hostId) return res.status(400).json({ error: 'Missing hostId' })\n try {\n // Suspension gate (AGL-550): a suspended member's cookie (AGL-546)\n // no longer reads the feed — 403'd with the cookie cleared.\n const auth = await requireActiveMember(req, res, hostId, 'Sign in first')\n if (!auth) return\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const email = String(auth.member.get('email') ?? '')\n if (!email) return res.status(401).json({ error: 'Sign in first' })\n\n const postsSnapshot = await hostRef\n .collection('memberPosts')\n .limit(50)\n .get()\n const entitlementCache = new Map<string, boolean>()\n const posts: Array<{\n id: string\n title: string\n body: string\n createdAtMs: number\n }> = []\n for (const docSnapshot of postsSnapshot.docs) {\n const scope = String(docSnapshot.get('productId') ?? '') || 'any'\n if (!entitlementCache.has(scope)) {\n entitlementCache.set(\n scope,\n await checkMemberEntitlement(hostId, email, scope),\n )\n }\n if (!entitlementCache.get(scope)) continue\n posts.push({\n id: docSnapshot.id,\n title: String(docSnapshot.get('title') ?? ''),\n body: String(docSnapshot.get('body') ?? ''),\n createdAtMs: Number(docSnapshot.get('createdAtMs') ?? 0),\n })\n }\n posts.sort((a, b) => b.createdAtMs - a.createdAtMs)\n res.setHeader('Cache-Control', 'private, no-store')\n return res.status(200).json({ posts })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Feed unavailable' })\n }\n}\n"],"names":["firebaseAdmin","requireActiveMember","checkMemberEntitlement","memberFeedHandler","req","res","hostId","String","query","status","json","error","auth","firestore","app","hostRef","collection","doc","email","member","get","postsSnapshot","limit","entitlementCache","Map","posts","docSnapshot","docs","scope","has","set","push","id","title","body","createdAtMs","Number","sort","a","b","setHeader","console"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,aAAa,QAAQ,2BAA0B;AACxD,SAASC,mBAAmB,QAAQ,kBAAc;AAClD,SAASC,sBAAsB,QAAQ,YAAQ;AAE/C;;;;;CAKC,GACD,OAAO,MAAMC,oBAAsC,OAAOC,KAAKC;QACvCD;IAAtB,MAAME,SAASC,QAAOH,oBAAAA,IAAII,KAAK,CAACF,MAAM,YAAhBF,oBAAoB;IAC1C,IAAI,CAACE,QAAQ,OAAOD,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;QAAEC,OAAO;IAAiB;IACnE,IAAI;YAOmBC;QANrB,mEAAmE;QACnE,4DAA4D;QAC5D,MAAMA,OAAO,MAAMX,oBAAoBG,KAAKC,KAAKC,QAAQ;QACzD,IAAI,CAACM,MAAM;QACX,MAAMC,YAAYb,cAAcc,GAAG,GAAGD,SAAS;QAC/C,MAAME,UAAUF,UAAUG,UAAU,CAAC,SAASC,GAAG,CAACX;QAClD,MAAMY,QAAQX,QAAOK,mBAAAA,KAAKO,MAAM,CAACC,GAAG,CAAC,oBAAhBR,mBAA4B;QACjD,IAAI,CAACM,OAAO,OAAOb,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAgB;QAEjE,MAAMU,gBAAgB,MAAMN,QACzBC,UAAU,CAAC,eACXM,KAAK,CAAC,IACNF,GAAG;QACN,MAAMG,mBAAmB,IAAIC;QAC7B,MAAMC,QAKD,EAAE;QACP,KAAK,MAAMC,eAAeL,cAAcM,IAAI,CAAE;gBACvBD,kBAULA,mBACDA,mBACOA;YAZtB,MAAME,QAAQrB,QAAOmB,mBAAAA,YAAYN,GAAG,CAAC,wBAAhBM,mBAAgC,OAAO;YAC5D,IAAI,CAACH,iBAAiBM,GAAG,CAACD,QAAQ;gBAChCL,iBAAiBO,GAAG,CAClBF,OACA,MAAM1B,uBAAuBI,QAAQY,OAAOU;YAEhD;YACA,IAAI,CAACL,iBAAiBH,GAAG,CAACQ,QAAQ;YAClCH,MAAMM,IAAI,CAAC;gBACTC,IAAIN,YAAYM,EAAE;gBAClBC,OAAO1B,QAAOmB,oBAAAA,YAAYN,GAAG,CAAC,oBAAhBM,oBAA4B;gBAC1CQ,MAAM3B,QAAOmB,oBAAAA,YAAYN,GAAG,CAAC,mBAAhBM,oBAA2B;gBACxCS,aAAaC,QAAOV,oBAAAA,YAAYN,GAAG,CAAC,0BAAhBM,oBAAkC;YACxD;QACF;QACAD,MAAMY,IAAI,CAAC,CAACC,GAAGC,IAAMA,EAAEJ,WAAW,GAAGG,EAAEH,WAAW;QAClD9B,IAAImC,SAAS,CAAC,iBAAiB;QAC/B,OAAOnC,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEe;QAAM;IACtC,EAAE,OAAOd,OAAO;QACd8B,QAAQ9B,KAAK,CAACA;QACd,OAAON,IAAII,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAmB;IAC1D;AACF,EAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
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 search fields that travel with a site member's display name (AGL-2501).
|
|
19
|
+
*
|
|
20
|
+
* The console's Site users card is paged, so a name search that compares the
|
|
21
|
+
* rows already fetched answers "no such member" for everyone past the first
|
|
22
|
+
* page. Searching the whole collection needs a query, and a query over a raw
|
|
23
|
+
* `displayName` is case-sensitive — it would miss rather than fail, which is
|
|
24
|
+
* the worse of the two.
|
|
25
|
+
*
|
|
26
|
+
* `email` needs no twin: the register path already lower-cases it before
|
|
27
|
+
* storing, so the stored value IS its own normalized key.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ EVERY writer of `displayName` must call this. A member updated through a
|
|
30
|
+
* path that skips it keeps a stale key and becomes unfindable by their new
|
|
31
|
+
* name while still listing normally — the quiet half of a search bug.
|
|
32
|
+
*/
|
|
33
|
+
export declare function memberNameSearchFields(displayName: string): {
|
|
34
|
+
displayName: string;
|
|
35
|
+
displayNameLower: string;
|
|
36
|
+
displayNameTokens: string[];
|
|
37
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { nameSearchKey, nameSearchTokens } from "@aglyn/aglyn/app-utils/name-search";
|
|
17
|
+
/**
|
|
18
|
+
* The search fields that travel with a site member's display name (AGL-2501).
|
|
19
|
+
*
|
|
20
|
+
* The console's Site users card is paged, so a name search that compares the
|
|
21
|
+
* rows already fetched answers "no such member" for everyone past the first
|
|
22
|
+
* page. Searching the whole collection needs a query, and a query over a raw
|
|
23
|
+
* `displayName` is case-sensitive — it would miss rather than fail, which is
|
|
24
|
+
* the worse of the two.
|
|
25
|
+
*
|
|
26
|
+
* `email` needs no twin: the register path already lower-cases it before
|
|
27
|
+
* storing, so the stored value IS its own normalized key.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ EVERY writer of `displayName` must call this. A member updated through a
|
|
30
|
+
* path that skips it keeps a stale key and becomes unfindable by their new
|
|
31
|
+
* name while still listing normally — the quiet half of a search bug.
|
|
32
|
+
*/ export function memberNameSearchFields(displayName) {
|
|
33
|
+
return {
|
|
34
|
+
displayName,
|
|
35
|
+
displayNameLower: nameSearchKey(displayName),
|
|
36
|
+
// Every prefix of every WORD, so a reader finds "Ada Lovelace" by typing
|
|
37
|
+
// "lovelace" and not only by typing "ada".
|
|
38
|
+
displayNameTokens: nameSearchTokens(displayName)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=member-name-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/member-name-search.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 nameSearchKey,\n nameSearchTokens,\n} from '@aglyn/aglyn/app-utils/name-search'\n\n/**\n * The search fields that travel with a site member's display name (AGL-2501).\n *\n * The console's Site users card is paged, so a name search that compares the\n * rows already fetched answers \"no such member\" for everyone past the first\n * page. Searching the whole collection needs a query, and a query over a raw\n * `displayName` is case-sensitive — it would miss rather than fail, which is\n * the worse of the two.\n *\n * `email` needs no twin: the register path already lower-cases it before\n * storing, so the stored value IS its own normalized key.\n *\n * ⚠️ EVERY writer of `displayName` must call this. A member updated through a\n * path that skips it keeps a stale key and becomes unfindable by their new\n * name while still listing normally — the quiet half of a search bug.\n */\nexport function memberNameSearchFields(displayName: string): {\n displayName: string\n displayNameLower: string\n displayNameTokens: string[]\n} {\n return {\n displayName,\n displayNameLower: nameSearchKey(displayName),\n // Every prefix of every WORD, so a reader finds \"Ada Lovelace\" by typing\n // \"lovelace\" and not only by typing \"ada\".\n displayNameTokens: nameSearchTokens(displayName),\n }\n}\n"],"names":["nameSearchKey","nameSearchTokens","memberNameSearchFields","displayName","displayNameLower","displayNameTokens"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,aAAa,EACbC,gBAAgB,QACX,qCAAoC;AAE3C;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,uBAAuBC,WAAmB;IAKxD,OAAO;QACLA;QACAC,kBAAkBJ,cAAcG;QAChC,yEAAyE;QACzE,2CAA2C;QAC3CE,mBAAmBJ,iBAAiBE;IACtC;AACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
* Member post publish (AGL-316): manager-gated; writes the post and
|
|
20
|
+
* optionally emails the entitled subscribers (product-scoped or all
|
|
21
|
+
* live subscribers) through the env-gated Resend pipeline.
|
|
22
|
+
*/
|
|
23
|
+
export declare const memberPostHandler: PluginApiHandler;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { firebaseAdmin, getOrgForHost, hostSendingIdentity, meterHostEmail } from "@aglyn/tenant-data-admin";
|
|
18
|
+
import * as CommerceModel from "../model/index.js";
|
|
19
|
+
import { isEmailConfigured, sendEmail } from "@aglyn/shared-util-email";
|
|
20
|
+
import { EMAIL_TOPIC_NEWSLETTER, hostPublicOrigin, resolveBrandingProfile } from "@aglyn/aglyn/server";
|
|
21
|
+
// Which member subscribers are live enough to email (AGL-316). The list itself
|
|
22
|
+
// now lives in the model as `isTenantSubscriptionLive` (AGL-1849) — it was a
|
|
23
|
+
// private copy here and a second private copy in `gate.ts`, which is the drift
|
|
24
|
+
// AGL-1715 guards against on the org side.
|
|
25
|
+
/**
|
|
26
|
+
* Member post publish (AGL-316): manager-gated; writes the post and
|
|
27
|
+
* optionally emails the entitled subscribers (product-scoped or all
|
|
28
|
+
* live subscribers) through the env-gated Resend pipeline.
|
|
29
|
+
*/ export const memberPostHandler = async (req, res)=>{
|
|
30
|
+
var _req_headers_authorization, _req_body, _body_hostId, _body_title, _body_body, _body_productId;
|
|
31
|
+
if (req.method !== 'POST') {
|
|
32
|
+
return res.status(405).json({
|
|
33
|
+
error: 'Method not allowed'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
|
|
37
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
38
|
+
if (!idToken) return res.status(401).json({
|
|
39
|
+
error: 'Unauthenticated'
|
|
40
|
+
});
|
|
41
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : (_req_body = req.body) != null ? _req_body : {};
|
|
42
|
+
const hostId = String((_body_hostId = body.hostId) != null ? _body_hostId : '');
|
|
43
|
+
const title = String((_body_title = body.title) != null ? _body_title : '').trim().slice(0, 160);
|
|
44
|
+
const postBody = String((_body_body = body.body) != null ? _body_body : '').trim().slice(0, 5000);
|
|
45
|
+
const productId = String((_body_productId = body.productId) != null ? _body_productId : '').trim();
|
|
46
|
+
const emailSubscribers = Boolean(body.emailSubscribers);
|
|
47
|
+
if (!hostId || !title) {
|
|
48
|
+
return res.status(400).json({
|
|
49
|
+
error: 'Missing hostId or title'
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
var _hostSnapshot_get;
|
|
54
|
+
const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
55
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
56
|
+
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
57
|
+
const hostSnapshot = await hostRef.get();
|
|
58
|
+
// AN ALLOWLIST (AGL-2372), matching `gift-cards.ts` and the AGL-2262
|
|
59
|
+
// correction to the register. The old `!role || role === 'viewer'`
|
|
60
|
+
// denylist admitted every string that was not literally `viewer` — and
|
|
61
|
+
// since AGL-2334 that includes `author`, a real grantable role, on a route
|
|
62
|
+
// that publishes to paying subscribers and sends them mail on the
|
|
63
|
+
// merchant's brand. A denylist that widens whenever the role union grows
|
|
64
|
+
// is the defect; the set is the fix. Absent is refused, not permitted.
|
|
65
|
+
const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
|
|
66
|
+
if (memberRole !== 'admin' && memberRole !== 'editor') {
|
|
67
|
+
return res.status(403).json({
|
|
68
|
+
error: 'Not permitted'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const postRef = await hostRef.collection('memberPosts').add(_extends({
|
|
72
|
+
title,
|
|
73
|
+
body: postBody
|
|
74
|
+
}, productId ? {
|
|
75
|
+
productId
|
|
76
|
+
} : {}, {
|
|
77
|
+
createdAtMs: Date.now(),
|
|
78
|
+
createdBy: decoded.uid
|
|
79
|
+
}));
|
|
80
|
+
let emailed = 0;
|
|
81
|
+
if (emailSubscribers && isEmailConfigured()) {
|
|
82
|
+
var _hostPublicOrigin;
|
|
83
|
+
var _this;
|
|
84
|
+
/*
|
|
85
|
+
* ORDERED (D1). This was `limit(500)` with no `orderBy`, and Firestore
|
|
86
|
+
* answers an unordered limit in DOCUMENT-ID order — so a site past the
|
|
87
|
+
* ceiling mailed whichever five hundred subscriptions happened to hash
|
|
88
|
+
* low, and the same five hundred every time. `__name__` is the order it
|
|
89
|
+
* was already getting; stating it makes the window a decision rather
|
|
90
|
+
* than an accident, and makes it the same window on every publish.
|
|
91
|
+
*/ const subscriptions = await hostRef.collection('subscriptions').orderBy(firebaseAdmin.firestore.FieldPath.documentId()).limit(500).get();
|
|
92
|
+
const recipients = [
|
|
93
|
+
...new Set(subscriptions.docs.filter((docSnapshot)=>CommerceModel.isTenantSubscriptionLive(docSnapshot.get('status')) && (!productId || docSnapshot.get('productId') === productId) && docSnapshot.get('customerEmail')).map((docSnapshot)=>String(docSnapshot.get('customerEmail'))))
|
|
94
|
+
].slice(0, 200);
|
|
95
|
+
// White-label sender identity (White-Label Phase 3): the store's brand
|
|
96
|
+
// via the one shared resolver, from the owning org doc.
|
|
97
|
+
const branding = resolveBrandingProfile((_this = await getOrgForHost(hostId).catch(()=>null)) == null ? void 0 : _this.org);
|
|
98
|
+
// The site's own origin, for the unsubscribe link the gate mints. Read
|
|
99
|
+
// from the host document already in hand rather than assembled from an
|
|
100
|
+
// apex: a post is mailed and read later, and a wrong origin sends a
|
|
101
|
+
// subscriber's opt-out to a domain the merchant does not control.
|
|
102
|
+
const siteBase = (_hostPublicOrigin = hostPublicOrigin({
|
|
103
|
+
cname: hostSnapshot.get('cname'),
|
|
104
|
+
subdomain: hostSnapshot.get('subdomain')
|
|
105
|
+
})) != null ? _hostPublicOrigin : '';
|
|
106
|
+
/*
|
|
107
|
+
* ONE site, up to two hundred recipients — so the identity is resolved
|
|
108
|
+
* once and reused, not read per message. Hoisted rather than cached
|
|
109
|
+
* inside the helper for the reason its docblock gives: verification can
|
|
110
|
+
* change under a long-lived process, so how long a resolution may be
|
|
111
|
+
* trusted is the caller's declaration.
|
|
112
|
+
*/ const identity = await hostSendingIdentity(hostId);
|
|
113
|
+
for (const to of recipients){
|
|
114
|
+
/*
|
|
115
|
+
* MARKETING. A member post is a merchant mailing their audience, so
|
|
116
|
+
* it carries the unsubscribe header pair and a visible link, it is
|
|
117
|
+
* checked against both suppression lists, and it counts against how
|
|
118
|
+
* much mail one person receives from this site.
|
|
119
|
+
*
|
|
120
|
+
* Priority stays transactional — this handler answers a click and has
|
|
121
|
+
* nowhere to put a message the hourly governor deferred, and the rule
|
|
122
|
+
* on `'bulk'` is that only a resumable sweep may use it.
|
|
123
|
+
*/ const result = await sendEmail({
|
|
124
|
+
to,
|
|
125
|
+
subject: title,
|
|
126
|
+
text: postBody || title,
|
|
127
|
+
fromName: branding.fromName,
|
|
128
|
+
sendingIdentity: identity,
|
|
129
|
+
audience: 'tenant',
|
|
130
|
+
context: 'member post',
|
|
131
|
+
/*
|
|
132
|
+
* A post to a site's members is the "Newsletter" stream — regular
|
|
133
|
+
* news and stories from the site. Declared so a member who left
|
|
134
|
+
* that stream, while staying subscribed to everything else, is not
|
|
135
|
+
* mailed it; the gate asks the topic only for callers that name
|
|
136
|
+
* one.
|
|
137
|
+
*/ marketing: {
|
|
138
|
+
hostId,
|
|
139
|
+
siteBase,
|
|
140
|
+
topicId: EMAIL_TOPIC_NEWSLETTER
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
// The cost meter counts messages that LEFT. A suppressed or capped
|
|
144
|
+
// recipient produced no message and therefore no cost, and counting
|
|
145
|
+
// one would bill a merchant for the control working.
|
|
146
|
+
if (result.sent) emailed += 1;
|
|
147
|
+
}
|
|
148
|
+
// Cost meter (AGL-1438), once for the batch rather than per recipient —
|
|
149
|
+
// same number, one write. Transactional: a member post is content the
|
|
150
|
+
// subscriber is paying for, not a discretionary campaign, so it counts
|
|
151
|
+
// toward cost and is never refused by the campaign cap.
|
|
152
|
+
await meterHostEmail(hostId, emailed);
|
|
153
|
+
}
|
|
154
|
+
return res.status(200).json({
|
|
155
|
+
postId: postRef.id,
|
|
156
|
+
emailed
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(error);
|
|
160
|
+
return res.status(500).json({
|
|
161
|
+
error: 'Publish failed'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
//# sourceMappingURL=member-post.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/member-post.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 firebaseAdmin,\n getOrgForHost,\n hostSendingIdentity,\n meterHostEmail,\n} from '@aglyn/tenant-data-admin'\nimport * as CommerceModel from '../model'\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport {\n EMAIL_TOPIC_NEWSLETTER,\n hostPublicOrigin,\n type PluginApiHandler,\n resolveBrandingProfile,\n} from '@aglyn/aglyn/server'\n\n// Which member subscribers are live enough to email (AGL-316). The list itself\n// now lives in the model as `isTenantSubscriptionLive` (AGL-1849) — it was a\n// private copy here and a second private copy in `gate.ts`, which is the drift\n// AGL-1715 guards against on the org side.\n\n/**\n * Member post publish (AGL-316): manager-gated; writes the post and\n * optionally emails the entitled subscribers (product-scoped or all\n * live subscribers) through the env-gated Resend pipeline.\n */\nexport const memberPostHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n const body =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const hostId = String(body.hostId ?? '')\n const title = String(body.title ?? '').trim().slice(0, 160)\n const postBody = String(body.body ?? '').trim().slice(0, 5000)\n const productId = String(body.productId ?? '').trim()\n const emailSubscribers = Boolean(body.emailSubscribers)\n if (!hostId || !title) {\n return res.status(400).json({ error: 'Missing hostId or title' })\n }\n\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n // AN ALLOWLIST (AGL-2372), matching `gift-cards.ts` and the AGL-2262\n // correction to the register. The old `!role || role === 'viewer'`\n // denylist admitted every string that was not literally `viewer` — and\n // since AGL-2334 that includes `author`, a real grantable role, on a route\n // that publishes to paying subscribers and sends them mail on the\n // merchant's brand. A denylist that widens whenever the role union grows\n // is the defect; the set is the fix. Absent is refused, not permitted.\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[decoded.uid]\n if (memberRole !== 'admin' && memberRole !== 'editor') {\n return res.status(403).json({ error: 'Not permitted' })\n }\n const postRef = await hostRef.collection('memberPosts').add({\n title,\n body: postBody,\n ...(productId ? { productId } : {}),\n createdAtMs: Date.now(),\n createdBy: decoded.uid,\n })\n\n let emailed = 0\n if (emailSubscribers && isEmailConfigured()) {\n /*\n * ORDERED (D1). This was `limit(500)` with no `orderBy`, and Firestore\n * answers an unordered limit in DOCUMENT-ID order — so a site past the\n * ceiling mailed whichever five hundred subscriptions happened to hash\n * low, and the same five hundred every time. `__name__` is the order it\n * was already getting; stating it makes the window a decision rather\n * than an accident, and makes it the same window on every publish.\n */\n const subscriptions = await hostRef\n .collection('subscriptions')\n .orderBy(firebaseAdmin.firestore.FieldPath.documentId())\n .limit(500)\n .get()\n const recipients = [\n ...new Set(\n subscriptions.docs\n .filter(\n (docSnapshot) =>\n CommerceModel.isTenantSubscriptionLive(\n docSnapshot.get('status'),\n ) &&\n (!productId ||\n docSnapshot.get('productId') === productId) &&\n docSnapshot.get('customerEmail'),\n )\n .map((docSnapshot) => String(docSnapshot.get('customerEmail'))),\n ),\n ].slice(0, 200)\n // White-label sender identity (White-Label Phase 3): the store's brand\n // via the one shared resolver, from the owning org doc.\n const branding = resolveBrandingProfile(\n (await getOrgForHost(hostId).catch(() => null))?.org as never,\n )\n // The site's own origin, for the unsubscribe link the gate mints. Read\n // from the host document already in hand rather than assembled from an\n // apex: a post is mailed and read later, and a wrong origin sends a\n // subscriber's opt-out to a domain the merchant does not control.\n const siteBase =\n hostPublicOrigin({\n cname: hostSnapshot.get('cname'),\n subdomain: hostSnapshot.get('subdomain'),\n }) ?? ''\n /*\n * ONE site, up to two hundred recipients — so the identity is resolved\n * once and reused, not read per message. Hoisted rather than cached\n * inside the helper for the reason its docblock gives: verification can\n * change under a long-lived process, so how long a resolution may be\n * trusted is the caller's declaration.\n */\n const identity = await hostSendingIdentity(hostId)\n for (const to of recipients) {\n /*\n * MARKETING. A member post is a merchant mailing their audience, so\n * it carries the unsubscribe header pair and a visible link, it is\n * checked against both suppression lists, and it counts against how\n * much mail one person receives from this site.\n *\n * Priority stays transactional — this handler answers a click and has\n * nowhere to put a message the hourly governor deferred, and the rule\n * on `'bulk'` is that only a resumable sweep may use it.\n */\n const result = await sendEmail({\n to,\n subject: title,\n text: postBody || title,\n fromName: branding.fromName,\n sendingIdentity: identity,\n audience: 'tenant',\n context: 'member post',\n /*\n * A post to a site's members is the \"Newsletter\" stream — regular\n * news and stories from the site. Declared so a member who left\n * that stream, while staying subscribed to everything else, is not\n * mailed it; the gate asks the topic only for callers that name\n * one.\n */\n marketing: {\n hostId,\n siteBase,\n topicId: EMAIL_TOPIC_NEWSLETTER,\n },\n })\n // The cost meter counts messages that LEFT. A suppressed or capped\n // recipient produced no message and therefore no cost, and counting\n // one would bill a merchant for the control working.\n if (result.sent) emailed += 1\n }\n // Cost meter (AGL-1438), once for the batch rather than per recipient —\n // same number, one write. Transactional: a member post is content the\n // subscriber is paying for, not a discretionary campaign, so it counts\n // toward cost and is never refused by the campaign cap.\n await meterHostEmail(hostId, emailed)\n }\n return res.status(200).json({ postId: postRef.id, emailed })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Publish failed' })\n }\n}\n"],"names":["firebaseAdmin","getOrgForHost","hostSendingIdentity","meterHostEmail","CommerceModel","isEmailConfigured","sendEmail","EMAIL_TOPIC_NEWSLETTER","hostPublicOrigin","resolveBrandingProfile","memberPostHandler","req","res","body","method","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","undefined","JSON","parse","hostId","title","trim","postBody","productId","emailSubscribers","Boolean","hostSnapshot","decoded","app","auth","verifyIdToken","firestore","hostRef","collection","doc","get","memberRole","uid","postRef","add","createdAtMs","Date","now","createdBy","emailed","subscriptions","orderBy","FieldPath","documentId","limit","recipients","Set","docs","filter","docSnapshot","isTenantSubscriptionLive","map","branding","catch","org","siteBase","cname","subdomain","identity","to","result","subject","text","fromName","sendingIdentity","audience","context","marketing","topicId","sent","postId","id","console"],"mappings":"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"}
|